diff --git a/IMPCaches.md b/IMPCaches.md new file mode 100644 index 0000000..d77afe0 --- /dev/null +++ b/IMPCaches.md @@ -0,0 +1,166 @@ +# IMP caches generation + +## Principle + +When you call an Objective-C method, Objective-C looks for the actual IMP (function pointer) given the class and selector. It then +stores, in malloced memory, this [selector, IMP] pair in a hash table. + +These hash tables are: + +* imperfect: the hash function is just a mask applied to the selector's +address, so if two selectors have the same low bits, you will get a collision. +Collisions are resolved through linear probing +* expensive: the footprint of these hash tables is often around 70MB total on a carry device. + +We want to replace this with static hash tables, inside the shared cache, where you have: + +* a perfect hashing function +* and the tables are in clean memory + +## Hash function + +Because the hash function is executed as part of `objc_msgSend`, it has to be blazingly fast. The algorithm below explains how we can +make a hash function of the form `h(x) = (x >> shift) & mask` work, where `shift` and `mask` are two class-specific parameters. + +Note that we cannot use traditional perfect hash tables techniques as: + +* they require at least [1.44 bits of information per key](https://arxiv.org/pdf/1910.06416.pdf). +* they often require multiple lookups (two-level hashing). + +The idea is that because the input of the hash function is a selector's address, we have some control over the input... because the +dyld shared cache builder is the one placing all the selectors. + +So now the problem becomes : given a set of classes and the selectors they implement, how do you place the selectors, and for each +class, how do you find a shift and mask so that the hash table generated by (selector >> shift) & mask is perfect? + +(Note that the shift + mask idiom lets us use various "bit stripes" of the selector's address for various hash tables). + +## Algorithm + +There are basically two steps to the main algorithm: + +### Finding shifts and masks + +Assign some of the high bits of the selector's address, and find a shift and a mask for each class. This is a backtracking +algorithm which goes through each class, one after another. As it goes through classes, it finds a shift and a mask compatible +with the bit ranges that are already set on each selector's address, and assigns the corresponding bits of the selector's address. +Note that because addresses are partial at this point (some of the bits are unset) it's very difficult to check for collisions, +so selectors will end up at the same address. + +At each step of the algorithm, we go through a set of possible shifts and masks until we find one which works. If none work, we let +the hash table grow to one more bit to make our job slightly easier. In practice few hash tables grow one more bit (82 out of 18k). +If we cannot find a suitable shift and mask after backtracking a few times, we'll also allow ourselves to drop a class from the set +and not generate an IMP cache for that one. This happens for a dozen classes or so with the current data set. + +Next we have to deal with collisions, and constraints on the addresses themselves because each selector is... a `char*`, which has +a length. You cannot have an overlap between two selectors. So the idea here is to try to get rid of this constraint by assigning +in step 1 just the address of a "bucket" which is 128 bytes long (7 bits). So step 1 will assign the high bits of the address +(which will be the address of the bucket) and we can then place the selectors linearly in the buckets. + +### Shuffling selectors around + +Then you have to deal with address collisions. If the lengths of selectors in a given bucket add up to more than 128, you have +to move some selectors out. So step 2 goes through all the selectors, checks if it fits within the bucket it's supposed to be in, +and if it doesn't finds another suitable bucket. + +To do so, it iterates through all the classes the selector is in, and builds a "constraint set" applying to that selector's +bucket's address (by basically looking at which slots in each hash table are free and combining all these constraints, which +impact a different "stripe" of the address due to the shifts and masks). Once we have the constraint set, we can find a different +bucket for the selector without changing the shifts and masks. (If there is no other possible bucket, we allow ourselves to drop the +classes involving this selector, too). + +Once each selector has a valid bucket, you can simply assign the low 7 bits of each address by looking at which selectors are in +each bucket and looking at their lengths. + +The problem is hard but not that hard given the number of classes we are targeting with this optimization: +we are roughly targeting ~ 20k classes out of 120k and ~ 200k selectors out of 900k, so we have lots of "free space". + +Note that any holes we leave in the bucketization of the selectors can be filled later by placing all the selectors not targeted +by the optimization and any selectors from OS executables inside them. + +## Shared cache builder setup + +The optimization is guided by a file dropped by the OrderFiles project (`/AppleInternal/OrderFiles/shared-cache-objc-optimizations.json`). +The performance team is responsible for updating this file by looking at the caches on live or StressCycler devices with `objcdt dump-imp-caches`. + +This file specifies a list of classes and metaclasses the algorithm targets, and a list of flattening roots. + +We haven't explained flattening yet. When a class D(aughter) inherits from a class M(other), we should in theory add all of M's methods +to D's IMP cache. However + +* This constrains the problem too much. The solver's job will be too difficult if the same selectors are in thousands of classes. +* This makes the IMP cache sizes blow up. + +So our scheme is to target only classes which are leaves in the inheritance tree with this optimization. For any selector that comes from +parent classes, the cache lookup will fail and fall back to looking for the method in `super`. Because `super` is not a leaf class, +it will have a dynamically allocated IMP cache and can cache there any selector that comes from the parents. + +However, this means that some very interesting classes from a memory standpoint (because we find their caches in many processes) +get excluded because they have child classes. A solution to this is to turn on selector inheritance (add Mother's selectors +to Daughter's cache) starting at some flattening root. Then the IMP cache will have a "fallback class" that is the superclass +of the flattening root, and `objc_msgSend` will fallback to that class if it cannot find the selector in the child cache, +skipping over all the classes up to the flattening root (because we know all the selectors in that chain will be present +in the child cache). + +Very early into the shared cache builder's life, the algorithm described above runs. To do so, it parses all of the source dylibs +to find out which methods will end up in which class's cache (see section "what ends up in each cache" below). +The output of the algorithm is: + +- for each class: + * a shift and a mask + * a list of methods that will end up in the cache with (source install name, source class name, source category name) +- for each selector: an offset at which it needs to be placed relative to the beginning of the selectors section +- a list of holes in the selector address space (or more accurately offset space) that can be used to add additional selectors + not targeted by the algorithm + +Then, we do all the selector deduping work, and when we get to the ObjC optimizer: + +- we go through all the classes again to build a map from (source install name, source class name, source category name) to the + actual IMP's address (which is not known before the shared cache is laid out) +- we go through all the IMP caches returned by the algorithm, find the corresponding IMP, and emit the actual IMP cache in the + objc optimizer's RO region. + +## Some code pointers + +Most of the logic lives in the single C++ file `IMPCaches.cpp` and the two headers `IMPCaches.hpp` and `IMPCachesBuilder.hpp`. A tour: + +### Types + +`IMPCaches::Selector` : A unique selector. Has a name, a list of classes it's used in, and an address (which is actually an offset from +the beginning of the selectors section). Due to how the placement algorithm work, it also has a current +partial address and the corresponding bitmask of fixed bits. The algorithm adds bits to the partial address +as it makes progress and updates the mask accordingly + +`IMPCaches::AddressSpace` : a representation of the address space available in the selectors section, so that step 2 of the algorithm +can check if selector buckets are overflowing. + +`IMPCaches::HoleMap` : represents the holes left by the selector placement algorithm, to be filled later with other selectors we did not target. + +`IMPCaches::Constraint` : represents a constraint on some of the bits of an address. It stores a set of allowed values for a given range of bits (shift and mask) + +`IMPCachesBuilder` : what actually computes the caches (part of the algorithm ; what actually lays down the caches lives in the +ObjC optimizer) + +### Entry points + +* `IMPCachesBuilder::parseDylibs` : parses the source dylibs to figure out which method ends up in which cache. + +* `IMPCachesBuilder::findShiftsAndMasks` : finds the shift and mask for each class (see "Findings shifts and masks" above). + +* `IMPCachesBuilder::solveGivenShiftsAndMasks` : moves selectors around to make sure each bucket only contains 128 bytes worth of selectors. + +Then in `OptimizerObjC`: + +* `IMPMapBuilder` : Builds a map from (install name, class name, method name) to actual IMPs + +* `IMPCachesEmitter` : emits the actual IMP caches in the shared cache. + +### What ends up in each cache? + +`IMPCachesBuilder::parseDylibs` goes through all the classes and categories to decide, for each (class,selector) pair, which implementation we will actually call at runtime. +The sequence is: + +* Get all the methods from the method lists +* Attach any methods from non-cross-image categories (when we have cross-image categories, we prevent the class from getting an IMP cache). If there is any collision at this point we'll +use the implementation from the main class (or from the first category we found). +* Then we may inline some of the implementations from superclasses, see the explanation on flattening above. diff --git a/bin/expand.rb b/bin/expand.rb deleted file mode 100755 index 01c7542..0000000 --- a/bin/expand.rb +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env ruby - -require 'yaml' - -if ENV["DRIVERKITROOT"] - $availCmd = ENV["SDKROOT"] + ENV["DRIVERKITROOT"] + "/usr/local/libexec/availability.pl"; -else - $availCmd = ENV["SDKROOT"] + "/usr/local/libexec/availability.pl"; -end - -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 =~ /^\/\/\@TVOS_VERSION_DEFS\@$/ - expandVersions("DYLD_TVOS_VERSION_", "--appletvos") - elsif line =~ /^\/\/\@BRIDGEOS_VERSION_DEFS\@$/ - expandVersions("DYLD_BRIDGEOS_VERSION_", "--bridgeos") - 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 diff --git a/build-scripts/ContainerizedTestRunner-build-everything.sh b/build-scripts/ContainerizedTestRunner-build-everything.sh new file mode 100755 index 0000000..d2b7d71 --- /dev/null +++ b/build-scripts/ContainerizedTestRunner-build-everything.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +source $SRCROOT/build-scripts/include.sh + +env -i PATH="${PATH}" xcodebuild install -sdk ${SDKROOT} -configuration ${CONFIGURATION} -target dyld -target libdyld -target dyld_tests -target dyld_usage TOOLCHAINS="${TOOLCHAINS}" DSTROOT=${DERIVED_FILES_DIR}/TestRoot OBJROOT=${DERIVED_FILES_DIR}/objroot DISABLE_SDK_METADATA_PARSING=YES XCTestGenPath=${DERIVED_FILES_DIR}/XCTestGenerated.h GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS BUILD_FOR_TESTING=1' || exit_if_error $? "Build failed" +${BUILT_PRODUCTS_DIR}/chroot_util -chroot ${DERIVED_FILES_DIR}/TestRoot -fallback / -add_file /bin/echo -add_file /bin/sh -add_file /bin/bash -add_file /bin/ls -add_file /usr/sbin/dtrace -add_file /sbin/mount -add_file /sbin/mount_devfs -add_file /usr/lib/libobjc-trampolines.dylib -add_file /usr/bin/leaks -add_file /System/iOSSupport/System/Library/Frameworks/UIKit.framework/UIKit || exit_if_error $? "Chroot build failed" +/bin/mkdir -p ${DERIVED_FILES_DIR}/TestRoot/dev +/bin/mkdir -m 777 -p ${DERIVED_FILES_DIR}/TestRoot/tmp + diff --git a/build-scripts/configure-dyld-archives.sh b/build-scripts/configure-dyld-archives.sh new file mode 100755 index 0000000..60a663e --- /dev/null +++ b/build-scripts/configure-dyld-archives.sh @@ -0,0 +1,15 @@ +# link with all .a files in /usr/local/lib/dyld +ls -1 ${SDKROOT}/usr/local/lib/dyld/*.a | grep -v libcompiler_rt > ${DERIVED_SOURCES_DIR}/archives.txt + +# link with crash report archive if it exists +if [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ] +then + echo \"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\" >> ${DERIVED_SOURCES_DIR}/archives.txt +fi + +# link with crypto archive if it exists +if [ -f ${SDKROOT}/usr/local/lib/libcorecrypto_static.a ] +then + echo \"${SDKROOT}/usr/local/lib/libcorecrypto_static.a\" >> ${DERIVED_SOURCES_DIR}/archives.txt +fi + diff --git a/build-scripts/dyld_tests-build.sh b/build-scripts/dyld_tests-build.sh new file mode 100755 index 0000000..eda854f --- /dev/null +++ b/build-scripts/dyld_tests-build.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +set -e + +source $SRCROOT/build-scripts/include.sh + +# Exit on failure + +OBJROOT_DYLD_APP_CACHE_UTIL="${TARGET_TEMP_DIR}/Objects_Dyld_App_Cache_Util" +OBJROOT_RUN_STATIC="${TARGET_TEMP_DIR}/Objects_Run_Static" + +SYMROOT=${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/dyld_tests +OBJROOT=${PROJECT_TEMP_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME} +SDKROOT=${SDKROOT:-$(xcrun -sdk macosx.internal --show-sdk-path)} +DEPLOYMENT_TARGET_CLANG_FLAG_NAME=${DEPLOYMENT_TARGET_CLANG_FLAG_NAME:-"mmacosx-version-min"} +DERIVED_FILES_DIR=${DERIVED_FILES_DIR} +LDFLAGS="-L$BUILT_PRODUCTS_DIR" +#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null) +NINJA=${LLBUILD:-`xcrun --sdk $SDKROOT --find ninja 2> /dev/null`} +BUILD_TARGET=${ONLY_BUILD_TEST:-all} + +if [ ! -z "$LLBUILD" ]; then + NINJA="$LLBUILD ninja build" +fi + +OSVERSION="10.14" +if [ ! -z "$DEPLOYMENT_TARGET_CLANG_ENV_NAME" ]; then + OSVERSION=${!DEPLOYMENT_TARGET_CLANG_ENV_NAME} +fi + +if [ -z "$SRCROOT" ]; then + echo "Error $$SRCROOT must be set" +fi + +if [ -z "$ARCHS" ]; then + PLATFORM_NAME=${PLATFORM_NAME:macosx} + case "$PLATFORM_NAME" in + "watchos") ARCHS="armv7k arm64_32" + ;; + "appletvos") ARCHS="arm64" + ;; + *) ARCHS=${ARCHS_STANDARD} + ;; + esac +fi + +if [ -z "$ARCHS" ]; then + ARCHS="x86_64" +fi + +/bin/mkdir -p ${DERIVED_FILES_DIR} +TMPFILE=$(mktemp ${DERIVED_FILES_DIR}/config.ninja.XXXXXX) + +echo "OBJROOT = $OBJROOT" >> $TMPFILE +echo "OSFLAG = $DEPLOYMENT_TARGET_CLANG_FLAG_NAME" >> $TMPFILE +echo "OSVERSION = $OSVERSION" >> $TMPFILE +echo "SDKROOT = $SDKROOT" >> $TMPFILE +echo "SRCROOT = $SRCROOT" >> $TMPFILE +echo "SYMROOT = $SYMROOT" >> $TMPFILE +echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR" >> $TMPFILE +echo "INSTALL_GROUP = $INSTALL_GROUP" >> $TMPFILE +echo "INSTALL_MODE_FLAG = $INSTALL_MODE_FLAG" >> $TMPFILE +echo "INSTALL_OWNER = $INSTALL_OWNER" >> $TMPFILE +echo "INSTALL_DIR = $INSTALL_DIR" >> $TMPFILE +echo "USER_HEADER_SEARCH_PATHS = $USER_HEADER_SEARCH_PATHS" >> $TMPFILE +echo "SYSTEM_HEADER_SEARCH_PATHS = $SYSTEM_HEADER_SEARCH_PATHS" >> $TMPFILE +echo "ARCHS = $ARCHS" >> $TMPFILE +echo "DERIVED_FILES_DIR = $DERIVED_FILES_DIR" >> $TMPFILE +echo "LDFLAGS = $LDFLAGS" >> $TMPFILE + +/usr/bin/rsync -vc $TMPFILE ${DERIVED_FILES_DIR}/config.ninja +/bin/rm -f $TMPFILE + +xcodebuild install -target run-static SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_RUN_STATIC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + +xcodebuild install -target dyld_app_cache_util -sdk macosx.internal -configuration ${CONFIGURATION} MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_DYLD_APP_CACHE_UTIL}" SRCROOT="${SRCROOT}" DSTROOT="${BUILT_PRODUCTS_DIR}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" INSTALL_PATH="/host_tools" RC_ARCHS="${NATIVE_ARCH_ACTUAL}" DISABLE_SDK_METADATA_PARSING=YES + +${SRCROOT}/testing/build_ninja.py ${DERIVED_FILES_DIR}/config.ninja || exit_if_error $? "Generating build.ninja failed" +${NINJA} -C ${DERIVED_FILES_DIR} ${BUILD_TARGET} || exit_if_error $? "Ninja build failed" diff --git a/build-scripts/dyld_tests-install.sh b/build-scripts/dyld_tests-install.sh new file mode 100755 index 0000000..4e79b0b --- /dev/null +++ b/build-scripts/dyld_tests-install.sh @@ -0,0 +1,16 @@ +#!/bin/sh + + +#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null) +NINJA=${LLBUILD:-`xcrun --sdk $SDKROOT --find ninja 2> /dev/null`} +INSTALL_TARGET="install" + +if [ ! -z "$LLBUILD" ]; then + NINJA="$LLBUILD ninja build" +fi + +if [ ! -z "$ONLY_BUILD_TEST" ]; then + INSTALL_TARGET="install-$BUILD_ONLY" +fi + +${NINJA} -C ${DERIVED_FILES_DIR} ${INSTALL_TARGET} || exit_if_error $? "Ninja install failed" diff --git a/build-scripts/generate-cache-config-header.sh b/build-scripts/generate-cache-config-header.sh new file mode 100755 index 0000000..dac8049 --- /dev/null +++ b/build-scripts/generate-cache-config-header.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +/bin/echo "" > ${DERIVED_FILE_DIR}/dyld_cache_config.h + +if [ -z "${ARM_SDK}" ]; then + # if iOS SDK not available, use MacOSX SDK + ARM_SDK=`xcrun -sdk macosx.internal --show-sdk-path` +fi + +SHARED_REGION_FILE="${ARM_SDK}/usr/include/mach/shared_region.h" + + +if [ -r "${SHARED_REGION_FILE}" ]; then + /bin/echo -n "#define ARM_SHARED_REGION_START " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_BASE_ARM[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + + /bin/echo -n "#define ARM_SHARED_REGION_SIZE " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_SIZE_ARM[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + + /bin/echo -n "#define ARM64_SHARED_REGION_START " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_BASE_ARM64[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + + /bin/echo -n "#define ARM64_SHARED_REGION_SIZE " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_SIZE_ARM64[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + + grep SHARED_REGION_BASE_ARM64_32 "${SHARED_REGION_FILE}" > /dev/null 2>&1 + if [ "$?" -eq "0" ]; then + /bin/echo -n "#define ARM64_32_SHARED_REGION_START " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + + /bin/echo -n "#define ARM64_32_SHARED_REGION_SIZE " >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + /bin/echo "" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h + fi +else + /bin/echo "ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'" + exit 1 +fi + +if [ -r "${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt" ]; then + mkdir -p "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin" + cp "${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt" "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin" +fi +if [ -r "${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt" ]; then + mkdir -p "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin" + cp "${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt" "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin" +fi + diff --git a/build-scripts/include.sh b/build-scripts/include.sh new file mode 100644 index 0000000..4b666a7 --- /dev/null +++ b/build-scripts/include.sh @@ -0,0 +1,10 @@ +exit_if_error() { + local exit_code=$1 + shift + [[ $exit_code ]] && # do nothing if no error code passed + ((exit_code != 0)) && { # do nothing if error code is 0 + printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here + exit "$exit_code" # we could also check to make sure + # error code is numeric when passed + } +} diff --git a/build-scripts/libdyld-generate-version-headers.sh b/build-scripts/libdyld-generate-version-headers.sh new file mode 100755 index 0000000..45685c8 --- /dev/null +++ b/build-scripts/libdyld-generate-version-headers.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ "${DRIVERKIT}" = 1 ]; then + RUNTIME_PREFIX="/System/DriverKit/Runtime" +else + RUNTIME_PREFIX="" +fi + +/bin/mkdir -p ${DERIVED_FILES_DIR} +/bin/mkdir -p ${DSTROOT}${RUNTIME_PREFIX}/usr/local/include/mach-o/ + +VERSIONS=${SDKROOT}${RUNTIME_PREFIX}/usr/local/include/dyld/for_dyld_priv.inc +DYLD_PRIV_IN=${SRCROOT}/include/mach-o/dyld_priv.h +DYLD_PRIV_OUT=${DSTROOT}${RUNTIME_PREFIX}/usr/local/include/mach-o/dyld_priv.h +TMPFILE=$(mktemp ${DERIVED_FILES_DIR}/dyld_priv.h.XXXXXX) + +/bin/chmod 0644 $TMPFILE + +while IFS="" read -r p || [ -n "$p" ] +do + case "$p" in + *@VERSION_DEFS* ) cat "$VERSIONS" >> $TMPFILE ;; + * ) echo "$p" >> $TMPFILE ;; + esac +done < $DYLD_PRIV_IN + +/usr/bin/rsync -vc $TMPFILE $DYLD_PRIV_OUT +/bin/rm -f $TMPFILE diff --git a/build-scripts/update_dyld_shared_cache-build.sh b/build-scripts/update_dyld_shared_cache-build.sh new file mode 100755 index 0000000..4031c59 --- /dev/null +++ b/build-scripts/update_dyld_shared_cache-build.sh @@ -0,0 +1,43 @@ + +if [ "${ACTION}" = "install" ] +then + OBJROOT_LOCAL="${TARGET_TEMP_DIR}/Objects_Local" + xcodebuild install -target dyld_shared_cache_builder SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_LOCAL}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + + # On macOS, also install dyld_shared_cache_builder to the platform so that root_util can find it. + if [ "${RC_PURPLE}" = "" ] + then + if [ "${PLATFORM_DIR}" != "" ] + then + # Note this is set to something like DEVELOPER_INSTALL_DIR=/Applications/Xcode.app/Contents/Developer + mkdir -p ${DSTROOT}/${DEVELOPER_INSTALL_DIR}/Platforms/MacOSX.platform/usr/local/bin/ + cp ${DSTROOT}/usr/local/bin/dyld_shared_cache_builder ${DSTROOT}/${DEVELOPER_INSTALL_DIR}/Platforms/MacOSX.platform/usr/local/bin/dyld_shared_cache_builder + fi + fi + + if [ "${RC_PURPLE}" = "YES" ] + then + OBJROOT_UTILS="${TARGET_TEMP_DIR}/Objects_Utils" + xcodebuild install -target dyld_closure_util -target dyld_shared_cache_util SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_UTILS}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + if [ "${RC_BRIDGE}" != "YES" ] + then + OBJROOT_SIM="${TARGET_TEMP_DIR}/Objects_Sim" + xcodebuild install -target update_dyld_sim_shared_cache SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_SIM}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + fi + else + OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects_Mac" + xcodebuild install -target update_dyld_shared_cache_tool SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects2_Mac" + xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES + fi +fi + +# On macOS build the kernel linker in to /usr/lib too. It defaults to the toolchain +if [ "${ACTION}" != "installhdrs" ] +then + if [ "${RC_PURPLE}" = "" ] + then + OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects_Linker_Mac" + xcodebuild ${ACTION} -target libKernelCollectionBuilder SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}/usr/lib" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" LD_DYLIB_INSTALL_NAME="/usr/lib/libKernelCollectionBuilder.dylib" INSTALL_PATH="/usr/lib" + fi +fi diff --git a/chroot_util.cpp b/chroot_util.cpp new file mode 100644 index 0000000..5c5fcc3 --- /dev/null +++ b/chroot_util.cpp @@ -0,0 +1,210 @@ +/* +* Copyright (c) 2019 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "StringUtils.h" +#include "MachOFile.h" + +std::set scanForDependencies(const std::string& path) { + __block std::set result; + struct stat stat_buf; + int fd = open(path.c_str(), O_RDONLY, 0); + if (fd == -1) { + return result; + } + + if (fstat(fd, &stat_buf) == -1) { + close(fd); + return result; + } + + const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + close(fd); + return result; + } + + auto scanner = ^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + if (isWeak) { return; } // We explicily avoid LC_LOAD_WEAK_DYLIB since we are trying to build a minimal chroot + if (loadPath[0] != '/') { return; } // Only include absolute dependencies + result.insert(loadPath); + }; + Diagnostics diag; + if ( dyld3::FatFile::isFatFile(buffer) ) { + const dyld3::FatFile* ff = (dyld3::FatFile*)buffer; + ff->forEachSlice(diag, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) { + const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart; + mf->forEachDependentDylib(scanner); + }); + } else { + const dyld3::MachOFile* mf = (dyld3::MachOFile*)buffer; + if (mf->isMachO(diag, stat_buf.st_size)) { + mf->forEachDependentDylib(scanner); + } + } + close(fd); + return result; +} + +std::string withoutPrefixPath(const std::string& path, const std::string& prefix ) { + std::string result = path; + size_t pos = result.find(prefix); + result.erase(pos, prefix.length()); + return result; +} + +void add_symlinks_to_dylib(const std::string path) { + static std::set alreadyMatched; + size_t pos = path.rfind(".framework/Versions/"); + auto prefixPath = path.substr(0, pos); + if (alreadyMatched.find(prefixPath) != alreadyMatched.end()) { return; } + + if (pos == std::string::npos) { return; } +// fprintf(stderr, "PATH: %s\n", path.c_str()); + size_t versionStart = pos+20; + size_t versionEnd = versionStart; + while (path[versionEnd] != '/') { + ++versionEnd; + } + size_t frameworkNameBegin = pos; + while (path[frameworkNameBegin-1] != '/') { + --frameworkNameBegin; + } + auto frameworkName = path.substr(frameworkNameBegin, pos-frameworkNameBegin); + auto version = path.substr(versionStart, versionEnd-versionStart); + std::string mainLinkPath = prefixPath + ".framework/" + frameworkName; + std::string mainLinkTarget = "Versions/Current/" + frameworkName; + std::string versionLinkPath = prefixPath + ".framework/Versions/Current"; + std::string versionLinkTarget = version;; + alreadyMatched.insert(prefixPath); + if (!std::filesystem::exists(versionLinkPath)) { + std::filesystem::create_symlink(version, versionLinkPath); + } + if (!std::filesystem::exists(mainLinkPath)) { + std::filesystem::create_symlink(mainLinkTarget, mainLinkPath); + } +} + +void add_symlink(const std::string& target, const std::string& path) { + if (!std::filesystem::exists(path)) { + std::filesystem::create_symlink(target, path); + } +} + +void buildChroot(const std::string& chroot, const std::string& fallback, const std::vector& binaries) { + auto chrootPath = std::filesystem::path(chroot); + auto fallbackPath = std::filesystem::path(fallback); + + for (const auto& binary : binaries) { + if (std::filesystem::exists(chroot + binary)) { continue; } + std::filesystem::create_directories(std::filesystem::path(chroot + binary).parent_path()); + std::filesystem::copy(fallback + binary, chroot + binary); + } + bool foundNewEntries = true; + std::set scannedFiles; + std::string devfsPath = chroot + "/dev"; + while (foundNewEntries) { + foundNewEntries = false; + for(auto file = std::filesystem::recursive_directory_iterator(chroot); + file != std::filesystem::recursive_directory_iterator(); + ++file ) { + auto filePath = file->path().string(); + if (filePath == devfsPath) { + file.disable_recursion_pending(); + continue; + } + if (scannedFiles.find(filePath) != scannedFiles.end()) { continue; } + scannedFiles.insert(filePath); + auto candidates = scanForDependencies(filePath); + for (const auto& candidate : candidates) { + if (std::filesystem::exists(chroot + candidate)) { continue; } + if (!std::filesystem::exists(fallback + candidate)) { continue; } + std::filesystem::create_directories(std::filesystem::path(chroot + candidate).parent_path()); + std::filesystem::copy(fallback + candidate, chroot + candidate); + add_symlinks_to_dylib(chroot + candidate); + foundNewEntries = true; + } + } + } + add_symlink("libSystem.B.dylib", chroot + "/usr/lib/libSystem.dylib"); + add_symlink("libSystem.dylib", chroot + "/usr/lib/libc.dylib"); +} + +int main(int argc, const char * argv[]) { + std::vector binaries; + std::vector overlays; + std::string fallback; + std::string chroot; + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-chroot") == 0) { + chroot = argv[++i]; + } else if (strcmp(arg, "-fallback") == 0) { + fallback = argv[++i]; + } else if (strcmp(arg, "-add_file") == 0) { + binaries.push_back(argv[++i]); + } else { + fprintf(stderr, "unknown option: %s\n", arg); + exit(-1); + } + } else { + fprintf(stderr, "unknown option: %s\n", arg); + exit(-1); + } + } + + if (chroot.length() == 0) { + fprintf(stderr, "No -chroot \n"); + exit(-1); + } + if (fallback.length() == 0) { + fprintf(stderr, "No -fallback \n"); + exit(-1); + } + buildChroot(chroot, fallback, binaries); + // insert code here... + return 0; +} diff --git a/configs/libdyld.xcconfig b/configs/libdyld.xcconfig index 69f4fb4..6e56b8a 100644 --- a/configs/libdyld.xcconfig +++ b/configs/libdyld.xcconfig @@ -4,5 +4,10 @@ LIBSYSTEM_LIBS[sdk=embedded*] = -Wl,-upward-lsystem_platform -Wl,-upwa 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-lcorecrypto -Wl,-upward-lcompiler_rt LIBSYSTEM_LIBS[sdk=driverkit*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lcompiler_rt +EXTRA_SECTIONS = +EXTRA_SECTIONS[arch=arm64e] = -Wl,-sectcreate,__SHARED_CACHE,__cfstring,/dev/null + +USE_CHAINED_FIXUPS = + INSTALL_PATH = /usr/lib/system diff --git a/configs/update_dyld_sim_shared_cache.xcconfig b/configs/update_dyld_sim_shared_cache.xcconfig index 1a04e7b..f744df6 100644 --- a/configs/update_dyld_sim_shared_cache.xcconfig +++ b/configs/update_dyld_sim_shared_cache.xcconfig @@ -1,6 +1,6 @@ - #include "/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig" +INSTALL_PATH = $(SIMULATOR_RUNTIME_BUNDLE_INSTALL_DIR)/Contents/Resources //:configuration = Debug GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1 DEBUG=1 diff --git a/darling-scripts/add-version-macros.sh b/darling-scripts/add-version-macros.sh deleted file mode 100755 index a423613..0000000 --- a/darling-scripts/add-version-macros.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -x - -pushd $(dirname "$0") > /dev/null -SCRIPTDIR=$(pwd -P) -popd > /dev/null - -export SRCROOT="$SCRIPTDIR/.." -export SDKROOT="$SRCROOT/../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" - -# copied and modified from dyld.xcodeproj -${SRCROOT}/bin/expand.rb < "${SRCROOT}/include/mach-o/dyld_priv.h" > "${SRCROOT}/include/mach-o/dyld_priv.h.tmp" - -mv "${SRCROOT}/include/mach-o/dyld_priv.h.tmp" "${SRCROOT}/include/mach-o/dyld_priv.h" diff --git a/doc/man/man1/closured.1 b/doc/man/man1/closured.1 deleted file mode 100644 index a13b005..0000000 --- a/doc/man/man1/closured.1 +++ /dev/null @@ -1,10 +0,0 @@ -.Dd 3/1/17 -.Dt closured 1 -.Os Darwin -.Sh NAME -.Nm closured -.Nd Daemon for building dyld closures. -.Sh SYNOPSIS -.Nm closured is a launchd managed daemon. -.Sh DESCRIPTION -.Nm closured is a launchd managed daemon. diff --git a/doc/man/man1/dyld.1 b/doc/man/man1/dyld.1 index 530f708..605f454 100644 --- a/doc/man/man1/dyld.1 +++ b/doc/man/man1/dyld.1 @@ -1,4 +1,4 @@ -.TH DYLD 1 "June 1, 2017" "Apple Inc." +.TH DYLD 1 "June 1, 2020" "Apple Inc." .SH NAME dyld \- the dynamic linker .SH SYNOPSIS @@ -51,8 +51,6 @@ DYLD_PRINT_DOFS DYLD_PRINT_RPATHS .br DYLD_SHARED_CACHE_DIR -.br -DYLD_SHARED_CACHE_DONT_VALIDATE .SH DESCRIPTION The dynamic linker checks the following environment variables during the launch of each process. @@ -229,13 +227,7 @@ that expansion was successful or not. .TP .B DYLD_SHARED_CACHE_DIR This is a directory containing dyld shared cache files. This variable can be used in -conjunction with DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE -to run a process with an alternate shared cache. -.TP -.B DYLD_SHARED_CACHE_DONT_VALIDATE -Causes dyld to not check that the inode and mod-time of files in the shared cache match -the requested dylib on disk. Thus a program can be made to run with the dylib in the -shared cache even though the real dylib has been updated on disk. +conjunction with DYLD_SHARED_REGION=private to run a process with an alternate shared cache. .TP .SH DYNAMIC LIBRARY LOADING Unlike many other operating systems, Darwin does not locate dependent dynamic libraries diff --git a/doc/man/man1/dyld_usage.1 b/doc/man/man1/dyld_usage.1 index e35e51b..c31d3c4 100644 --- a/doc/man/man1/dyld_usage.1 +++ b/doc/man/man1/dyld_usage.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "DYLD_USAGE" "1" "2018-07-28" "" "dyld" +.TH "DYLD_USAGE" "1" "2020-04-13" "" "dyld" .SH NAME dyld_usage \- report dynamic linker activity in real-time . @@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .. .SH SYNOPSIS .sp -\fBdyld_usage\fP \fB[\-e] [\-f mode] [\-t seconds] [\-R rawfile [\-S start_time] +\fBdyld_usage\fP \fB[\-e] [\-f mode] [\-j] [\-h] [\-t seconds] [\-R rawfile [\-S start_time] [\-E end_time]] [pid | cmd [pid | cmd] ...]\fP .SH DESCRIPTION .sp @@ -56,16 +56,27 @@ for maximum data display. .B \-e Exclude the specified list of pids and commands from the sample, and exclude \fBdyld_usage\fP by default. +.INDENT 7.0 +.TP +.B \-j +.UNINDENT +.sp +Display output in JSON format. +.UNINDENT +.INDENT 0.0 +.TP +.B \-h +Display usage information and exit. .UNINDENT .INDENT 0.0 .TP .B \-R -specifies a raw trace file to process. +Specify a raw trace file to process. .UNINDENT .INDENT 0.0 .TP .B \-t -specifies timeout in seconds (for use in automated tools). +Specify timeout in seconds (for use in automated tools). .UNINDENT .SH DISPLAY .sp @@ -107,6 +118,6 @@ processes named Mail. .SH AUTHOR Apple, Inc. .SH COPYRIGHT -2000-2018, Apple, Inc. +2000-2020, Apple, Inc. .\" Generated by docutils manpage writer. . diff --git a/doc/man/man1/update_dyld_shared_cache.1 b/doc/man/man1/update_dyld_shared_cache.1 deleted file mode 100644 index f1c2330..0000000 --- a/doc/man/man1/update_dyld_shared_cache.1 +++ /dev/null @@ -1,75 +0,0 @@ -.Dd June 1, 2017 -.Dt update_dyld_shared_cache 1 -.Os Darwin -.Sh NAME -.Nm update_dyld_shared_cache -.Nd "Updates dyld's shared cache" -.Sh SYNOPSIS -.Nm -.Op Fl root Ar directory -.Op Fl overlay Ar directory -.Op Fl arch Ar arch -.Op Fl force -.Op Fl debug -.Op Fl universal_boot -.Sh DESCRIPTION -.Nm update_dyld_shared_cache -ensures that dyld's shared cache is up-to-date. This tool is normally -only run by Apple's Installer and Software Update, as they are the only -official ways for OS dylibs to be updated. But if for some reason you -used another mechanism to alter an OS dylib, you should manually run -.Nm update_dyld_shared_cache . -.Pp -Note that the new cache does not take effect until the OS is rebooted. -.Pp -The dyld shared cache -is mapped by dyld into a process at launch time. Later, when loading -any mach-o image, dyld will first check if is in the share cache, and if -it is will use that pre-bound version instead of opening, mapping, and binding -the original file. This results in significant performance improvements to -launch time. -.Pp -.Nm update_dyld_shared_cache -scans the directory /System/Library/Receipts/ for .bom files which list all files -installed. From that info it creates the set of OS dylibs to build into the dyld cache. -.Pp -.Nm update_dyld_shared_cache -builds a separate cache file for each architecture. The cache files and a readable text -map of the cached are generated to /var/db/dyld. -.Pp -You must be root to run this tool. -.Pp -The options are as follows: -.Bl -tag -.It Fl root Ar directory -This option specifies the root of an OS installation for which dyld's -shared cache should be updated. This is used by the Installer to update the -dyld shared cache in a partition other than the one you into which you are currently -booted. The cache files are created in the var/db/dyld directory of the specified directory. -Note: if you are manually doing this, be sure to run the update_dyld_shared_cache tool -that is in the partition being updated. This assures the cache format created will -match that expected when booting off that partition. -.It Fl overlay Ar directory -This option specifies the root of a sparse directory tree. When building -the dyld shared cache, any corresponding mach-o files in the sparse directory -will override those in the boot partition. This is used by Software -Update to build a dyld shared cache for the update that is about to be -installed. The cache files -are created in the var/db/dyld directory of the specified directory. -.It Fl arch Ar arch -By default -.Nm update_dyld_shared_cache -generates cache files for all architecture that the current machine -can execute. You can override this behavior by specifying one or more -arch options and list -exactly which architectures should have their shared caches updated. -.It Fl force -This option will cause -.Nm update_dyld_shared_cache -to regenerated the shared cache files even if they appear to be already up-to-date. -.It Fl debug -This option prints out additional information about the work being done. -.It Fl universal_boot -This option builds caches for all machines. -.El -.Sh SEE ALSO -.Xr dyld 1 diff --git a/doc/man/man3/dlopen_preflight.3 b/doc/man/man3/dlopen_preflight.3 index aa27dbf..38ed7fc 100644 --- a/doc/man/man3/dlopen_preflight.3 +++ b/doc/man/man3/dlopen_preflight.3 @@ -1,4 +1,4 @@ -.Dd April 17, 2006 +.Dd May 11, 2020 .Os .Dt DLOPEN_PREFLIGHT 3 .Sh NAME @@ -13,7 +13,14 @@ examines the mach-o file specified by .Fa path . It checks if the file and libraries it depends on are all compatible with the current process. -That is, they contain the correct architecture and are not otherwise ABI incompatible. +That is, they contain the correct architecture and are not otherwise ABI incompatible. +.Pp +.Fn dlopen_preflight +was created for the PowerPC to Intel transition for use by apps with plugins that the user chooses to load. +The app could use dlopen_preflight() to show only loadable plugins to the user (such as in a menu). +.Pp +This is potentially an expensive call because it may internally do the same as dlopen/dlclose. Only +use dlopen_preflight() if you need to show the user a list of potentially loadable plugins. .Pp .Fn dlopen_preflight was first available in Mac OS X 10.5. diff --git a/doc/man/man3/dlsym.3 b/doc/man/man3/dlsym.3 index b981ad6..40d1fdf 100644 --- a/doc/man/man3/dlsym.3 +++ b/doc/man/man3/dlsym.3 @@ -85,7 +85,6 @@ is the name used in C source code. For example to find the address of function foo(), you would pass "foo" as the symbol name. This is unlike the older dyld APIs which required a leading underscore. If you are looking up a C++ symbol, you need to use the mangled C++ symbol name. -name. .Sh SEE ALSO .Xr dlopen 3 .Xr dlerror 3 diff --git a/doc/man/man3/dyld.3 b/doc/man/man3/dyld.3 index 8dfb86d..c01dab2 100644 --- a/doc/man/man3/dyld.3 +++ b/doc/man/man3/dyld.3 @@ -76,7 +76,7 @@ is out of range zero is returned. .Fn _dyld_get_image_name returns the name of the image indexed by .Fa image_index. -The C-string continues to be owned by dyld and should not deleted. +The C-string continues to be owned by dyld and should not be deleted. If .Fa image_index is out of range NULL is returned. diff --git a/doc/rst/conf.py b/doc/rst/conf.py index 9154517..5169a94 100644 --- a/doc/rst/conf.py +++ b/doc/rst/conf.py @@ -232,18 +232,15 @@ for name in os.listdir(command_guide_path): header = f.readline().rstrip('\n') if len(header) != len(title): - print >>sys.stderr, ( - "error: invalid header in %r (does not match title)" % ( - file_subpath,)) + print("error: invalid header in {} (does not match title)". + format(file_subpath), file=sys.stderr) if ' - ' not in title: - print >>sys.stderr, ( - ("error: invalid title in %r " - "(expected ' - ')") % ( - file_subpath,)) + print("error: invalid title in {} (expected ' - ')". + format(file_subpath), file=sys.stderr) # Split the name out of the title. name,description = title.split(' - ', 1) - man_pages.append((file_subpath.replace('.rst',''), name, + man_pages.append((name.replace('.rst',''), name, description, man_page_authors, 1)) # If true, show URL addresses after external links. diff --git a/doc/rst/dyld_usage.rst b/doc/rst/dyld_usage.rst index 18822d9..1527c2a 100644 --- a/doc/rst/dyld_usage.rst +++ b/doc/rst/dyld_usage.rst @@ -1,10 +1,10 @@ dyld_usage - report dynamic linker activity in real-time -========================================================== +======================================================== SYNOPSIS -------- -:program:`dyld_usage` **[-e] [-f mode] [-t seconds] [-R rawfile [-S start_time] +:program:`dyld_usage` **[-e] [-f mode] [-j] [-h] [-t seconds] [-R rawfile [-S start_time] [-E end_time]] [pid | cmd [pid | cmd] ...]** DESCRIPTION @@ -31,13 +31,21 @@ OPTIONS Exclude the specified list of pids and commands from the sample, and exclude :program:`dyld_usage` by default. + .. option:: -j + + Display output in JSON format. + +.. option:: -h + + Display usage information and exit. + .. option:: -R - specifies a raw trace file to process. + Specify a raw trace file to process. .. option:: -t - specifies timeout in seconds (for use in automated tools). + Specify timeout in seconds (for use in automated tools). DISPLAY ------- diff --git a/dyld.xcodeproj/ContainerizedTestRunner.xctestplan b/dyld.xcodeproj/ContainerizedTestRunner.xctestplan new file mode 100644 index 0000000..92776a4 --- /dev/null +++ b/dyld.xcodeproj/ContainerizedTestRunner.xctestplan @@ -0,0 +1,25 @@ +{ + "configurations" : [ + { + "id" : "0ED518CA-34AA-4808-822E-3532C949AFBA", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : false + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:dyld.xcodeproj", + "identifier" : "3715A2FD232320BC0059433D", + "name" : "ContainerizedTestRunner" + } + } + ], + "version" : 1 +} diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index 5bdd191..5d10c79 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -14,8 +14,7 @@ F94182D61E60E74E00D8EF25 /* pre-platform builds */, ); dependencies = ( - D8668AD01ECE335F005E7D31 /* PBXTargetDependency */, - F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */, + C1BF4DC32359390A00B0F1AE /* PBXTargetDependency */, F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */, F94182DC1E60F16900D8EF25 /* PBXTargetDependency */, C187B90C1FE067590042D3B7 /* PBXTargetDependency */, @@ -23,6 +22,21 @@ name = update_dyld_shared_cache; productName = update_dyld_shared_cache; }; + C1BDD43C234E8FA00095C7DC /* dyld_executables */ = { + isa = PBXAggregateTarget; + buildConfigurationList = C1BDD43F234E8FA00095C7DC /* Build configuration list for PBXAggregateTarget "dyld_executables" */; + buildPhases = ( + ); + dependencies = ( + F9A8E1B024120DD000CEB6BF /* PBXTargetDependency */, + C1B4759723E65F9600515793 /* PBXTargetDependency */, + C1C6403B23E4EC3000ED4B46 /* PBXTargetDependency */, + C1C6403923E4EC1C00ED4B46 /* PBXTargetDependency */, + C1C6403723E4EC1300ED4B46 /* PBXTargetDependency */, + ); + name = dyld_executables; + productName = dyld_executables; + }; F908134211D3ED0B00626CC1 /* libdyld */ = { isa = PBXAggregateTarget; buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */; @@ -56,9 +70,11 @@ buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */; buildPhases = ( F9F6F42B1C1FB0AE00BD8FED /* build */, + 37E2FC9D22F62FE1004AF213 /* install */, ); dependencies = ( - F97FF3661C237F97000ACDD2 /* PBXTargetDependency */, + 37382F6A230CB46500E375CE /* PBXTargetDependency */, + C14C3568230531EA0059E04C /* PBXTargetDependency */, ); name = dyld_tests; productName = dyld_tests; @@ -66,43 +82,63 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 37065AA82310889D00A20034 /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; }; + 37065AAB2310A18300A20034 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */; }; + 3715A303232320BD0059433D /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; }; + 37382F68230CADEE00E375CE /* test_support.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37382F67230CADEE00E375CE /* test_support.cpp */; }; 373C58F1219CE478003442D5 /* BootArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 373C58EF219CE478003442D5 /* BootArgs.cpp */; }; - 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; }; - 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; - 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; - 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; }; - 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; - 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; - 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; - 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; - 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; - 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; 37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; - 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 */; }; - 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; }; - 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; - 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; - 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; }; + 375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + 375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; 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 */; }; 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 */; }; + 37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */; }; 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; 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 */; }; + C116F19C23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C116F19D23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C116F19E23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C116F19F23F4B11B002D386B /* RootsChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = C116F19B23F4B11B002D386B /* RootsChecker.h */; }; + C116F1A223F4D73A002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C116F1A323F4D73B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C116F1A423F4D742002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; }; + C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; }; + C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; }; + C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; }; + C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + C14965E722BDCF8300568D15 /* dyld_app_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */; }; + C14965E822BEBF2300568D15 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + C14965E922BEBF2800568D15 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; }; + C14965EA22BEC04800568D15 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + C14965EB22BEC05000568D15 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; + C14965EC22BEC05800568D15 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; + C14965ED22C09B6100568D15 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + C14965EE22C31F7C00568D15 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + C14965F022C3203200568D15 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + C14965F122C3203E00568D15 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + C14965F622C327FB00568D15 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + C14965F722C3280B00568D15 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + C14965F822C3281A00568D15 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; + C14C3563230531830059E04C /* testing/run-static/run-static.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C14C3562230531830059E04C /* testing/run-static/run-static.cpp */; }; + C14C3569230537630059E04C /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + C14C356A2305376A0059E04C /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + C14C356B230539BE0059E04C /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; }; + C14C356C230539C20059E04C /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; + C176CB5C2321AB74009C1259 /* 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 */; }; @@ -116,29 +152,57 @@ 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 */; }; + C18839E523480866004E30FA /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; }; + C18F05372374D5B700DC6CCA /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; }; 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 */; }; + C1BDD443234EA7DD0095C7DC /* AppCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */; }; + C1BDD446234EAF500095C7DC /* MachOAppCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */; }; + C1BF4DAB2357B14700B0F1AE /* kernel_collection_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */; }; + C1BF4DB4235925A000B0F1AE /* kernel_collection_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */; }; + C1BF4DB6235925B400B0F1AE /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; + C1BF4DB7235925BA00B0F1AE /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + C1BF4DB82359385700B0F1AE /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + C1BF4DB92359386C00B0F1AE /* AppCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */; }; + C1BF4DBA2359389E00B0F1AE /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; }; + C1BF4DBB235938AC00B0F1AE /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + C1BF4DBC235938B000B0F1AE /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + C1BF4DBD235938B600B0F1AE /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; }; + C1BF4DBE235938BA00B0F1AE /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; + C1BF4DBF235938BD00B0F1AE /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + C1BF4DC0235938CB00B0F1AE /* MachOAppCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */; }; + C1BF4DC1235938D400B0F1AE /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + C1BF4DC623594FBC00B0F1AE /* kernel_collection_builder.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */; }; + C1BFD0502307CE99007D7CDC /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; }; 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 */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; - C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; - C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; - C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; - C1F003CD213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; C1F003CF213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; C1F003D0213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; + DE49C496238EC55300CD7FFB /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + DE49C497238EC57B00CD7FFB /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + DE49C499238EC59500CD7FFB /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + DE49C49A238EC5AD00CD7FFB /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; + DE49C49C238EC60D00CD7FFB /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; }; + DE49C4A3238EEE3400CD7FFB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; DE728E53210CD74E00EB5409 /* dyld_usage.1 in Install man page */ = {isa = PBXBuildFile; fileRef = DE728E52210CD6B700EB5409 /* dyld_usage.1 */; }; + DE9A811323982A3A00664840 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + E9C2FAD823AA8B5C0077E966 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; }; + E9EC3199240B0B95001705D6 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; }; + E9EC319A240B0BA8001705D6 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; }; + E9EC319D240B18DC001705D6 /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; }; + E9EC319E240B18E6001705D6 /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; }; + E9EC31A0240B19C1001705D6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9EC319F240B19C1001705D6 /* Foundation.framework */; }; 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 */; }; @@ -150,14 +214,7 @@ F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; F91BFAC821684FCC007F10AB /* fixup-chains.h in Headers */ = {isa = PBXBuildFile; fileRef = F91BFAC721684FCC007F10AB /* fixup-chains.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.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 */; }; F92C7DE721E59840000D12B5 /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; }; F92C7DE821E59840000D12B5 /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; }; @@ -193,23 +250,13 @@ 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 */; }; F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; - F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; }; F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; }; F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; }; F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */; }; @@ -238,39 +285,25 @@ F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; }; F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A51D9363D6007AF3CE /* APIs.cpp */; }; F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A61D9363D6007AF3CE /* AllImages.cpp */; }; + F971EC752342D373000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; }; + F971EC762342DF03000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; }; + F971EC772343CD46000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F971EC782343CD63000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; }; + F971EC7B2343CD9A000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; }; + F971EC7C2343CDA4000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; }; + F971EC7D2343CDB9000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.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 */; }; - F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; }; + F97D395523A1CBB600BD3B5A /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; + F97D395623A1CBB600BD3B5A /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; + F97D395723A1CBCA00BD3B5A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F97D395823A97B2C00BD3B5A /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; + F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.cpp */; }; F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; }; 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 */; }; - F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; }; - F98E37792332D048003706B4 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; - F98E377A2332D048003706B4 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; }; - F98E377B2332D048003706B4 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; }; - F98E377C2332D048003706B4 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; }; - F98E377D2332D048003706B4 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; }; - F98E377E2332D048003706B4 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; }; - F98E377F2332D048003706B4 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; }; - F98E37802332D048003706B4 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; }; - F98E37812332D048003706B4 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; }; F98E37822332D048003706B4 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; }; - F98E37832332D048003706B4 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; - F98E37842332D048003706B4 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; - F98E37852332D048003706B4 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; - F98E37862332D048003706B4 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; - F98E37872332D048003706B4 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; - F98E37882332D048003706B4 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; - F98E37892332D048003706B4 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; - F98E378A2332D048003706B4 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; - F98E378C2332D048003706B4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; }; - F98E378D2332D048003706B4 /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; - F98E378F2332D048003706B4 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; settings = {ATTRIBUTES = (Private, ); }; }; F99006DD1E411BA70013456D /* dyld_images.h in Headers */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; settings = {ATTRIBUTES = (Public, ); }; }; F99006DE1E411BBC0013456D /* dyld.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -290,11 +323,7 @@ F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9CE30791208F1B50098B590 /* dsc_extractor.h */; }; F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; - F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; - F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; }; - F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; - F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; 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 */; }; F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; @@ -324,8 +353,8 @@ 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 */; }; F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; }; - F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + F9FA17F4235A71DB009B0907 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -405,6 +434,20 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + 3715A30A23272F890059433D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3721B6A72321A75B006F6AB7; + remoteInfo = chroot_util; + }; + 37382F69230CB46500E375CE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3721A634230CABAF00594066; + remoteInfo = test_support; + }; 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; @@ -412,12 +455,12 @@ remoteGlobalIDString = 37A0AD0A1C15FFF500731E50; remoteInfo = update_dyld_shared_cache; }; - C1033EA722611306004407FB /* PBXContainerItemProxy */ = { + C14C3567230531EA0059E04C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; proxyType = 1; - remoteGlobalIDString = F9D1001114D8D0BA00099D91; - remoteInfo = dsc_extractor; + remoteGlobalIDString = C14C355F230531820059E04C; + remoteInfo = "run-static"; }; C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -426,7 +469,42 @@ remoteGlobalIDString = C187B8FF1FE063A40042D3B7; remoteInfo = libslc_builder; }; - D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = { + C18F05352374D5B100DC6CCA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3721A634230CABAF00594066; + remoteInfo = test_support; + }; + C1B4759623E65F9600515793 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 37F597CC2061EB4200F9B6F9; + remoteInfo = dyld_usage; + }; + C1BF4DC22359390A00B0F1AE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C1BF4DAF2359254500B0F1AE; + remoteInfo = libKernelCollectionBuilder; + }; + C1C6403623E4EC1300ED4B46 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9D1001114D8D0BA00099D91; + remoteInfo = dsc_extractor; + }; + C1C6403823E4EC1C00ED4B46 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + C1C6403A23E4EC3000ED4B46 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; proxyType = 1; @@ -440,13 +518,6 @@ remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; remoteInfo = libdyld.dylib; }; - F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F99B8E550FEC10F600701838; - remoteInfo = dyld_shared_cache_util; - }; F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; @@ -461,26 +532,12 @@ remoteGlobalIDString = F9D1001114D8D0BA00099D91; remoteInfo = dsc_extractor; }; - F96543A01E343601003C5540 /* PBXContainerItemProxy */ = { + F9A8E1AF24120DD000CEB6BF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; proxyType = 1; - remoteGlobalIDString = F97C61A61DBAD1A900A84CD7; - remoteInfo = dyld_closure_util; - }; - F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F97FF3551C23638F000ACDD2; - remoteInfo = nocr; - }; - F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F99B8E550FEC10F600701838; - remoteInfo = dyld_shared_cache_util; + remoteGlobalIDString = F9556D3820C1F896004DF62A; + remoteInfo = dyld_info; }; F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -515,7 +572,7 @@ ); runOnlyForDeploymentPostprocessing = 1; }; - 377685FE1AC4B27D00026E6C /* CopyFiles */ = { + 3721B6A62321A75B006F6AB7 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; @@ -535,6 +592,15 @@ name = "install ktrace codes file"; runOnlyForDeploymentPostprocessing = 1; }; + 37D47F8923EDFA2400C5C000 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 7; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 37F597CB2061EB4200F9B6F9 /* Install man page */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -546,6 +612,24 @@ name = "Install man page"; runOnlyForDeploymentPostprocessing = 1; }; + C14965DE22BDCF6800568D15 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + C14C355E230531820059E04C /* 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; @@ -556,6 +640,17 @@ name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; + C1BF4DC523594FA200B0F1AE /* usr|local|include */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include"; + dstSubfolderSpec = 0; + files = ( + C1BF4DC623594FBC00B0F1AE /* kernel_collection_builder.h in usr|local|include */, + ); + name = "usr|local|include"; + runOnlyForDeploymentPostprocessing = 1; + }; F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -626,17 +721,6 @@ name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F98E378E2332D048003706B4 /* usr|share|man|man1 */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; - dstSubfolderSpec = 0; - files = ( - F98E378F2332D048003706B4 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, - ); - name = "usr|share|man|man1"; - runOnlyForDeploymentPostprocessing = 1; - }; F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -648,42 +732,52 @@ name = "usr|local|include"; runOnlyForDeploymentPostprocessing = 1; }; - F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; - dstSubfolderSpec = 0; - files = ( - F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, - ); - name = "usr|share|man|man1"; - runOnlyForDeploymentPostprocessing = 1; - }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; + 37065AA72310856E00A20034 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; }; + 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ContainerizedTestRunner.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ContainerizedTestRunner.mm; sourceTree = ""; }; + 3715A302232320BD0059433D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3721A635230CABAF00594066 /* libtest_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libtest_support.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3721B6A82321A75B006F6AB7 /* chroot_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chroot_util; sourceTree = BUILT_PRODUCTS_DIR; }; + 37382F67230CADEE00E375CE /* test_support.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = test_support.cpp; path = testing/lib/test_support.cpp; sourceTree = ""; }; 373C58EF219CE478003442D5 /* BootArgs.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BootArgs.cpp; path = dyld3/BootArgs.cpp; sourceTree = ""; }; 373C58F0219CE478003442D5 /* BootArgs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BootArgs.h; path = dyld3/BootArgs.h; sourceTree = ""; }; + 376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = ContainerizedTestRunner.xctestplan; path = dyld.xcodeproj/ContainerizedTestRunner.xctestplan; sourceTree = ""; }; 376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; }; - 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = ""; }; - 37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = ""; }; - 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = ""; }; 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = ""; }; 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = ""; }; - 37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = ""; }; 37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = ""; }; 37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = ""; }; 37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; sourceTree = ""; }; - 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = ""; }; - 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = ""; }; + 37B5254F2475E91F00404300 /* generate-cache-config-header.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-cache-config-header.sh"; sourceTree = ""; }; + 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chroot_util.cpp; sourceTree = ""; }; + 37D47F8823EC961500C5C000 /* libdyld-generate-version-headers.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "libdyld-generate-version-headers.sh"; sourceTree = ""; }; + 37D5ACEC23A830C200AE4F57 /* Defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Defines.h; path = dyld3/Defines.h; sourceTree = ""; }; + 37D7995623AAA8AD00B314BC /* include.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = include.sh; sourceTree = ""; }; + 37D7995723AAA8AD00B314BC /* dyld_tests-build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "dyld_tests-build.sh"; sourceTree = ""; }; + 37D7995823AAA8AD00B314BC /* ContainerizedTestRunner-build-everything.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "ContainerizedTestRunner-build-everything.sh"; sourceTree = ""; }; + 37D7995923AAA8AD00B314BC /* dyld_tests-install.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "dyld_tests-install.sh"; sourceTree = ""; }; 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = ""; }; 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = ""; }; 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 = ""; }; 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; }; + A5E247802369485F00BDED2F /* dlfcn_private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dlfcn_private.h; path = include/dlfcn_private.h; sourceTree = ""; }; + C116F19A23F4B11B002D386B /* RootsChecker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RootsChecker.cpp; path = dyld3/RootsChecker.cpp; sourceTree = ""; }; + C116F19B23F4B11B002D386B /* RootsChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RootsChecker.h; path = dyld3/RootsChecker.h; sourceTree = ""; }; + C116F1A523F5BB39002D386B /* update_dyld_shared_cache-build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "update_dyld_shared_cache-build.sh"; sourceTree = ""; }; + C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheBuilder.cpp; path = "dyld3/shared-cache/SharedCacheBuilder.cpp"; sourceTree = ""; }; + C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SharedCacheBuilder.h; path = "dyld3/shared-cache/SharedCacheBuilder.h"; sourceTree = ""; }; + C141DF8825673EF00077621A /* PointerAuth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PointerAuth.h; path = dyld3/PointerAuth.h; sourceTree = ""; }; + C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_app_cache_util.cpp; path = dyld3/dyld_app_cache_util.cpp; sourceTree = ""; }; + C14965E022BDCF6800568D15 /* dyld_app_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_app_cache_util; sourceTree = BUILT_PRODUCTS_DIR; }; + C14C3560230531820059E04C /* run-static */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "run-static"; sourceTree = BUILT_PRODUCTS_DIR; }; + C14C3562230531830059E04C /* testing/run-static/run-static.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "testing/run-static/run-static.cpp"; sourceTree = ""; }; 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 = ""; }; C18A75F6209A18AC00DC01BB /* JSONReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONReader.h; path = dyld3/JSONReader.h; sourceTree = ""; }; @@ -691,6 +785,13 @@ C18A75F8209A1AF600DC01BB /* JSONReader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = JSONReader.mm; path = dyld3/JSONReader.mm; sourceTree = ""; }; C18F095221925E7600034B68 /* Map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Map.h; path = dyld3/Map.h; sourceTree = ""; }; C19D50142087E4BC00563DAF /* SupportedArchs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SupportedArchs.h; path = dyld3/SupportedArchs.h; sourceTree = ""; }; + C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AppCacheBuilder.cpp; path = "dyld3/shared-cache/AppCacheBuilder.cpp"; sourceTree = ""; }; + C1BDD442234EA7DD0095C7DC /* AppCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppCacheBuilder.h; path = "dyld3/shared-cache/AppCacheBuilder.h"; sourceTree = ""; }; + C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAppCache.cpp; path = dyld3/MachOAppCache.cpp; sourceTree = ""; }; + C1BDD445234EAF500095C7DC /* MachOAppCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOAppCache.h; path = dyld3/MachOAppCache.h; sourceTree = ""; }; + C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = kernel_collection_builder.cpp; path = "dyld3/shared-cache/kernel_collection_builder.cpp"; sourceTree = ""; }; + C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = kernel_collection_builder.h; path = "dyld3/shared-cache/kernel_collection_builder.h"; sourceTree = ""; }; + C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libKernelCollectionBuilder.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; 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 = ""; }; C1D268321FE09843009F115B /* ClosureFileSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureFileSystem.h; path = dyld3/ClosureFileSystem.h; sourceTree = ""; }; @@ -702,6 +803,10 @@ DE728E4E210CD6A100EB5409 /* conf.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = conf.py; sourceTree = ""; }; DE728E51210CD6A100EB5409 /* dyld_usage.rst */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld_usage.rst; sourceTree = ""; }; DE728E52210CD6B700EB5409 /* dyld_usage.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = dyld_usage.1; sourceTree = ""; }; + E90E790923D5F142005F5995 /* IMPCachesBuilder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = IMPCachesBuilder.hpp; path = "dyld3/shared-cache/IMPCachesBuilder.hpp"; sourceTree = ""; }; + E9C2FAD623AA8B5C0077E966 /* IMPCaches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = IMPCaches.hpp; path = "dyld3/shared-cache/IMPCaches.hpp"; sourceTree = ""; }; + E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = IMPCaches.cpp; path = "dyld3/shared-cache/IMPCaches.cpp"; sourceTree = ""; }; + E9EC319F240B19C1001705D6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; }; EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; }; @@ -711,6 +816,7 @@ 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 = ""; }; F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; + F91491A823EE52D300782334 /* configure-dyld-archives.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "configure-dyld-archives.sh"; sourceTree = ""; }; F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; F91BFAC721684FCC007F10AB /* fixup-chains.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "fixup-chains.h"; path = "include/mach-o/fixup-chains.h"; sourceTree = ""; }; F92756871F7098FB000820EE /* Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Array.h; path = dyld3/Array.h; sourceTree = ""; }; @@ -718,12 +824,8 @@ F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = ""; usesTabs = 1; }; F92C7E1421E59840000D12B5 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; - F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = ""; }; - F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = ""; }; - F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; - F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = ""; }; - F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = ""; }; + F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = "dyld3/shared-cache/Architectures.hpp"; sourceTree = ""; }; + F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/lib/execserver.defs; sourceTree = ""; }; F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = ""; }; F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = ""; }; F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; @@ -733,14 +835,12 @@ F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; 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 = ""; 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 = ""; usesTabs = 0; }; - F9556D3920C1F896004DF62A /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; }; + F9556D3920C1F896004DF62A /* dyld_info */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_info; sourceTree = BUILT_PRODUCTS_DIR; }; F9556D4120C20C79004DF62A /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = "dyld3/shared-cache/dyldinfo.cpp"; sourceTree = ""; }; 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 = ""; }; F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info_notify.cpp; path = src/dyld_process_info_notify.cpp; sourceTree = ""; usesTabs = 0; }; - F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; 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 = ""; 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; }; - F96D19711D7F63EE007AF3CE /* expand.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = expand.rb; path = bin/expand.rb; sourceTree = ""; }; F96D19A51D9363D6007AF3CE /* APIs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs.cpp; path = dyld3/APIs.cpp; sourceTree = ""; usesTabs = 0; }; F96D19A61D9363D6007AF3CE /* AllImages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AllImages.cpp; path = dyld3/AllImages.cpp; sourceTree = ""; usesTabs = 0; }; F96D19A71D9363D6007AF3CE /* AllImages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AllImages.h; path = dyld3/AllImages.h; sourceTree = ""; usesTabs = 0; }; @@ -749,6 +849,8 @@ F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = ""; }; F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = ""; }; + F971EC732342D2CF000BCEAA /* MachOAnalyzerSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachOAnalyzerSet.h; path = dyld3/MachOAnalyzerSet.h; sourceTree = ""; }; + F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAnalyzerSet.cpp; path = dyld3/MachOAnalyzerSet.cpp; sourceTree = ""; }; F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheRuntime.cpp; path = dyld3/SharedCacheRuntime.cpp; sourceTree = ""; }; F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCacheRuntime.h; path = dyld3/SharedCacheRuntime.h; sourceTree = ""; }; @@ -757,8 +859,8 @@ F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = ""; usesTabs = 0; }; F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = ""; 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; }; - F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = ""; }; + F97D394E23A08B5500BD3B5A /* dyld.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = dyld.modulemap; path = "include/mach-o/dyld.modulemap"; sourceTree = ""; }; + F97FF35F1C236402000ACDD2 /* nocr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nocr.cpp; path = testing/nocr/nocr.cpp; sourceTree = ""; }; F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = ""; }; F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = ""; }; F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = ""; usesTabs = 0; }; @@ -780,7 +882,7 @@ F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodeSigningTypes.h; path = dyld3/CodeSigningTypes.h; sourceTree = ""; usesTabs = 0; }; F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = ""; }; F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache_root_mode; sourceTree = BUILT_PRODUCTS_DIR; }; - F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = ""; }; + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_shared_cache_util.cpp; path = "dyld3/shared-cache/dyld_shared_cache_util.cpp"; sourceTree = ""; }; 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 = ""; }; F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; @@ -797,13 +899,12 @@ F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = ""; }; F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; }; F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; }; - 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 = ""; }; F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = ""; }; F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = ""; usesTabs = 0; }; F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = ""; usesTabs = 0; }; F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = ""; usesTabs = 0; }; - F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = ""; }; - F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = ""; }; + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dsc_extractor.cpp; path = "dyld3/shared-cache/dsc_extractor.cpp"; sourceTree = ""; }; + F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dsc_extractor.h; path = "dyld3/shared-cache/dsc_extractor.h"; sourceTree = ""; }; F9CF4C8121E59D060013ACDF /* libdyld_driverkit.exp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.exports; name = libdyld_driverkit.exp; path = src/libdyld_driverkit.exp; sourceTree = ""; }; F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = dsc_extractor.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = ""; }; @@ -840,8 +941,8 @@ 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 = ""; }; 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 = ""; }; - F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = ""; }; + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dsc_iterator.cpp; path = "dyld3/shared-cache/dsc_iterator.cpp"; sourceTree = ""; }; + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dsc_iterator.h; path = "dyld3/shared-cache/dsc_iterator.h"; sourceTree = ""; }; F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = ""; }; F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathOverrides.cpp; path = dyld3/PathOverrides.cpp; sourceTree = ""; }; F9F76FAF1E08CFF200828678 /* PathOverrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathOverrides.h; path = dyld3/PathOverrides.h; sourceTree = ""; }; @@ -852,17 +953,22 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */, - 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */, + C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 377685FD1AC4B27D00026E6C /* Frameworks */ = { + 3715A2FB232320BC0059433D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3715A303232320BD0059433D /* libtest_support.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3721B6A52321A75B006F6AB7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */, - 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -874,10 +980,35 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C14965DD22BDCF6800568D15 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1BFD0502307CE99007D7CDC /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C14C355D230531820059E04C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C18F05372374D5B700DC6CCA /* libtest_support.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C187B9031FE063A40042D3B7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E9EC31A0240B19C1001705D6 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C1BF4DAE2359254500B0F1AE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1BF4DBA2359389E00B0F1AE /* CoreFoundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -885,8 +1016,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */, - F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -915,6 +1044,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 37065AA82310889D00A20034 /* libtest_support.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -922,8 +1052,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F98E378C2332D048003706B4 /* CoreFoundation.framework in Frameworks */, - F98E378D2332D048003706B4 /* Bom.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -951,6 +1079,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3715A2FF232320BC0059433D /* local_test_runner */ = { + isa = PBXGroup; + children = ( + 3715A300232320BD0059433D /* ContainerizedTestRunner.mm */, + 3715A302232320BD0059433D /* Info.plist */, + ); + path = local_test_runner; + sourceTree = ""; + }; 37918ABF2058908000F39A77 /* tracing */ = { isa = PBXGroup; children = ( @@ -961,6 +1098,22 @@ path = doc/tracing; sourceTree = SOURCE_ROOT; }; + 37D7995523AAA86800B314BC /* build scripts */ = { + isa = PBXGroup; + children = ( + F91491A823EE52D300782334 /* configure-dyld-archives.sh */, + 37D7995823AAA8AD00B314BC /* ContainerizedTestRunner-build-everything.sh */, + 37D7995723AAA8AD00B314BC /* dyld_tests-build.sh */, + 37D47F8823EC961500C5C000 /* libdyld-generate-version-headers.sh */, + 37D7995923AAA8AD00B314BC /* dyld_tests-install.sh */, + 37D7995623AAA8AD00B314BC /* include.sh */, + C116F1A523F5BB39002D386B /* update_dyld_shared_cache-build.sh */, + 37B5254F2475E91F00404300 /* generate-cache-config-header.sh */, + ); + name = "build scripts"; + path = "build-scripts"; + sourceTree = ""; + }; DE728E4B210CD6A100EB5409 /* rst */ = { isa = PBXGroup; children = ( @@ -1037,6 +1190,7 @@ F91BFAC52166CED7007F10AB /* mach-o */ = { isa = PBXGroup; children = ( + F97D394E23A08B5500BD3B5A /* dyld.modulemap */, F95090D01C5AB89A0031F81D /* dyld_process_info.h */, F98D274C0AA79D7400416316 /* dyld_images.h */, F918691408B16D2500E0F9DB /* dyld-interposing.h */, @@ -1047,27 +1201,10 @@ name = "mach-o"; sourceTree = ""; }; - F939373D0A94FC4700070A07 /* launch-cache */ = { - isa = PBXGroup; - children = ( - F939373E0A94FC4700070A07 /* Architectures.hpp */, - F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */, - F93937400A94FC4700070A07 /* dyld_cache_format.h */, - F93937410A94FC4700070A07 /* FileAbstraction.hpp */, - F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */, - F95C95160E994796007B7CB8 /* MachOTrie.hpp */, - F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, - F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, - F9CE30781208F1B50098B590 /* dsc_extractor.cpp */, - F9CE30791208F1B50098B590 /* dsc_extractor.h */, - F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */, - ); - path = "launch-cache"; - sourceTree = ""; - }; F94C22231E513CA90079E5DD /* Frameworks */ = { isa = PBXGroup; children = ( + E9EC319F240B19C1001705D6 /* Foundation.framework */, 37F597D62061ED3200F9B6F9 /* libktrace.tbd */, 37F7A5961BB363820039043A /* Bom.framework */, 376ED1D71C46F2710051DD54 /* Metabom.framework */, @@ -1102,6 +1239,7 @@ F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */, F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */, F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */, + 37D5ACEC23A830C200AE4F57 /* Defines.h */, F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */, F98692001DC3EF4800CBEDE6 /* Diagnostics.h */, C18A75F7209A19E200DC01BB /* JSON.h */, @@ -1120,9 +1258,16 @@ F9A5E6161F5C967C0030C490 /* MachOLoaded.h */, F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */, F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.h */, + F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */, + F971EC732342D2CF000BCEAA /* MachOAnalyzerSet.h */, + C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */, + C1BDD445234EAF500095C7DC /* MachOAppCache.h */, C18F095221925E7600034B68 /* Map.h */, F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */, F9F76FAF1E08CFF200828678 /* PathOverrides.h */, + C141DF8825673EF00077621A /* PointerAuth.h */, + C116F19A23F4B11B002D386B /* RootsChecker.cpp */, + C116F19B23F4B11B002D386B /* RootsChecker.h */, F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */, F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */, C19D50142087E4BC00563DAF /* SupportedArchs.h */, @@ -1147,37 +1292,47 @@ F98692161DC3EF7700CBEDE6 /* shared-cache */ = { isa = PBXGroup; children = ( - 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */, - 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */, - F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */, F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */, - 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */, - 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */, - F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */, + C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */, + C1BDD442234EA7DD0095C7DC /* AppCacheBuilder.h */, + F939373E0A94FC4700070A07 /* Architectures.hpp */, F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */, - F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */, + F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */, + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */, + F9CE30791208F1B50098B590 /* dsc_extractor.h */, + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, + C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */, + F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */, + F9D862441DC9759C000A199A /* dyld_closure_util.cpp */, + 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */, + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */, + F9556D4120C20C79004DF62A /* dyldinfo.cpp */, F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */, - F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */, + F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */, + 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */, F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */, - 37908A2C1E3A85A4009613FA /* Manifest.h */, - 37908A281E3A853E009613FA /* Manifest.mm */, + F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */, + C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */, + C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */, + 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */, + C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */, + C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */, F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */, F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */, F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */, F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */, F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */, + E9C2FAD623AA8B5C0077E966 /* IMPCaches.hpp */, + E90E790923D5F142005F5995 /* IMPCachesBuilder.hpp */, + E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */, + C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */, + C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */, F902031F1DEE83C000AC3F76 /* StringUtils.h */, 37908A2D1E3A85A4009613FA /* Trie.hpp */, - F9D862441DC9759C000A199A /* dyld_closure_util.cpp */, - 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */, - 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */, - F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */, - F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */, - F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */, F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */, - C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */, - C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */, - F9556D4120C20C79004DF62A /* dyldinfo.cpp */, + F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */, + F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */, ); name = "shared-cache"; sourceTree = ""; @@ -1185,14 +1340,16 @@ F9ED4C870630A72200DF4E74 = { isa = PBXGroup; children = ( + 376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */, F9F6F4261C1FAF8000BD8FED /* testing */, + 37D7995523AAA86800B314BC /* build scripts */, F971DD121A4A0E0700BBDD52 /* configs */, F9ED4CBB0630A7AA00DF4E74 /* src */, F9ED4CC30630A7BE00DF4E74 /* doc */, F9ED4CBE0630A7B100DF4E74 /* include */, + 3715A2FF232320BC0059433D /* local_test_runner */, F9ED4C990630A76000DF4E74 /* Products */, F96D19A41D9363B7007AF3CE /* dyld3 */, - F939373D0A94FC4700070A07 /* launch-cache */, F94C22231E513CA90079E5DD /* Frameworks */, ); indentWidth = 4; @@ -1209,16 +1366,21 @@ F9F2A5590F7AEE9800B7C9EB /* libdsc.a */, F99B8E670FEC121100701838 /* dyld_shared_cache_util */, F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */, - 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */, - F97FF3561C23638F000ACDD2 /* nocr */, F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */, F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */, C187B90A1FE063A40042D3B7 /* slc_builder.dylib */, 37F597CD2061EB4200F9B6F9 /* dyld_usage */, F92C7E1421E59840000D12B5 /* libdyld.dylib */, - F9556D3920C1F896004DF62A /* dyldinfo */, + F9556D3920C1F896004DF62A /* dyld_info */, F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */, + 3721A635230CABAF00594066 /* libtest_support.a */, + 37065AA72310856E00A20034 /* nocr */, + 3721B6A82321A75B006F6AB7 /* chroot_util */, + 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */, + C14965E022BDCF6800568D15 /* dyld_app_cache_util */, + C14C3560230531820059E04C /* run-static */, + C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */, ); name = Products; sourceTree = ""; @@ -1226,9 +1388,11 @@ F9ED4CBB0630A7AA00DF4E74 /* src */ = { isa = PBXGroup; children = ( + 37382F67230CADEE00E375CE /* test_support.cpp */, F93F46511FA420630060D9F9 /* execserver.defs */, - F97FF35F1C236402000ACDD2 /* nocr.c */, + F97FF35F1C236402000ACDD2 /* nocr.cpp */, 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */, + 37CE9D192321A7EB001FBA91 /* chroot_util.cpp */, F9ED4CC60630A7F100DF4E74 /* dyld_debugger.cpp */, F9ED4CC70630A7F100DF4E74 /* dyld2.cpp */, F9ED4CC80630A7F100DF4E74 /* dyld2.h */, @@ -1267,6 +1431,7 @@ F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */, F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */, F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */, + C14C3562230531830059E04C /* testing/run-static/run-static.cpp */, ); name = src; sourceTree = ""; @@ -1274,9 +1439,9 @@ F9ED4CBE0630A7B100DF4E74 /* include */ = { isa = PBXGroup; children = ( - F96D19711D7F63EE007AF3CE /* expand.rb */, F91BFAC52166CED7007F10AB /* mach-o */, F99EE6AE06B48D4200BF1992 /* dlfcn.h */, + A5E247802369485F00BDED2F /* dlfcn_private.h */, F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */, ); name = include; @@ -1295,6 +1460,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 3721A631230CABAF00594066 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F92C7E0221E59840000D12B5 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1310,6 +1482,7 @@ F91BFAC821684FCC007F10AB /* fixup-chains.h in Headers */, F99006DD1E411BA70013456D /* dyld_images.h in Headers */, F99006DE1E411BBC0013456D /* dyld.h in Headers */, + C116F19F23F4B11B002D386B /* RootsChecker.h in Headers */, F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */, F960A78A1E40569400840176 /* dyld-interposing.h in Headers */, F9DFEA721F54BD83003BF8A7 /* ClosureWriter.h in Headers */, @@ -1339,22 +1512,56 @@ productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */; productType = "com.apple.product-type.tool"; }; - 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = { + 3715A2FD232320BC0059433D /* ContainerizedTestRunner */ = { isa = PBXNativeTarget; - buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */; + buildConfigurationList = 3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */; buildPhases = ( - 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */, - 377685F41AC4B27D00026E6C /* Sources */, - 377685FD1AC4B27D00026E6C /* Frameworks */, - 377685FE1AC4B27D00026E6C /* CopyFiles */, + 3715A3092327003C0059433D /* Build Everything */, + 3715A2FA232320BC0059433D /* Sources */, + 3715A2FB232320BC0059433D /* Frameworks */, + 3715A2FC232320BC0059433D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 3715A30B23272F890059433D /* PBXTargetDependency */, + ); + name = ContainerizedTestRunner; + productName = local_test_runner; + productReference = 3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 3721A634230CABAF00594066 /* test_support */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */; + buildPhases = ( + 3721A631230CABAF00594066 /* Headers */, + 3721A632230CABAF00594066 /* Sources */, ); buildRules = ( ); dependencies = ( ); - name = multi_dyld_shared_cache_builder; - productName = update_os_interlinked_dylib; - productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */; + name = test_support; + productName = test_support; + productReference = 3721A635230CABAF00594066 /* libtest_support.a */; + productType = "com.apple.product-type.library.static"; + }; + 3721B6A72321A75B006F6AB7 /* chroot_util */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */; + buildPhases = ( + 3721B6A42321A75B006F6AB7 /* Sources */, + 3721B6A52321A75B006F6AB7 /* Frameworks */, + 3721B6A62321A75B006F6AB7 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = chroot_util; + productName = chroot_builder; + productReference = 3721B6A82321A75B006F6AB7 /* chroot_util */; productType = "com.apple.product-type.tool"; }; 37F597CC2061EB4200F9B6F9 /* dyld_usage */ = { @@ -1374,6 +1581,41 @@ productReference = 37F597CD2061EB4200F9B6F9 /* dyld_usage */; productType = "com.apple.product-type.tool"; }; + C14965DF22BDCF6800568D15 /* dyld_app_cache_util */ = { + isa = PBXNativeTarget; + buildConfigurationList = C14965E422BDCF6900568D15 /* Build configuration list for PBXNativeTarget "dyld_app_cache_util" */; + buildPhases = ( + C14965DC22BDCF6800568D15 /* Sources */, + C14965DD22BDCF6800568D15 /* Frameworks */, + C14965DE22BDCF6800568D15 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_app_cache_util; + productName = dyld_app_cache_util; + productReference = C14965E022BDCF6800568D15 /* dyld_app_cache_util */; + productType = "com.apple.product-type.tool"; + }; + C14C355F230531820059E04C /* run-static */ = { + isa = PBXNativeTarget; + buildConfigurationList = C14C3564230531830059E04C /* Build configuration list for PBXNativeTarget "run-static" */; + buildPhases = ( + C14C355C230531820059E04C /* Sources */, + C14C355D230531820059E04C /* Frameworks */, + C14C355E230531820059E04C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + C18F05362374D5B100DC6CCA /* PBXTargetDependency */, + ); + name = "run-static"; + productName = "run-static"; + productReference = C14C3560230531820059E04C /* run-static */; + productType = "com.apple.product-type.tool"; + }; C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */ = { isa = PBXNativeTarget; buildConfigurationList = C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */; @@ -1392,12 +1634,30 @@ productReference = C187B90A1FE063A40042D3B7 /* slc_builder.dylib */; productType = "com.apple.product-type.library.dynamic"; }; + C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */ = { + isa = PBXNativeTarget; + buildConfigurationList = C1BF4DB12359254500B0F1AE /* Build configuration list for PBXNativeTarget "libKernelCollectionBuilder" */; + buildPhases = ( + C1BF4DAD2359254500B0F1AE /* Sources */, + C1BF4DAE2359254500B0F1AE /* Frameworks */, + C1BF4DC523594FA200B0F1AE /* usr|local|include */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libKernelCollectionBuilder; + productName = libKernelCollectionBuilder; + productReference = C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; F92C7DE521E59840000D12B5 /* libdyld_driverkit */ = { isa = PBXNativeTarget; buildConfigurationList = F92C7E1121E59840000D12B5 /* Build configuration list for PBXNativeTarget "libdyld_driverkit" */; buildPhases = ( F92C7DE621E59840000D12B5 /* Sources */, F92C7E0221E59840000D12B5 /* Headers */, + 37D47F8923EDFA2400C5C000 /* CopyFiles */, F951DA862228E5560057BA43 /* install headers */, ); buildRules = ( @@ -1415,12 +1675,9 @@ isa = PBXNativeTarget; buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */; buildPhases = ( - F91083C91702592700831889 /* create dyld_cache_config.h */, F939372F0A94FAF700070A07 /* Sources */, F93937300A94FAF700070A07 /* Frameworks */, - F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, F991E3030FF1A4EC0082CCC9 /* do not install duplicates */, - F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */, ); buildRules = ( ); @@ -1431,9 +1688,9 @@ productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */; productType = "com.apple.product-type.tool"; }; - F9556D3820C1F896004DF62A /* dyldinfo */ = { + F9556D3820C1F896004DF62A /* dyld_info */ = { isa = PBXNativeTarget; - buildConfigurationList = F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyldinfo" */; + buildConfigurationList = F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyld_info" */; buildPhases = ( F9556D3520C1F896004DF62A /* Sources */, F9556D3620C1F896004DF62A /* Frameworks */, @@ -1443,9 +1700,9 @@ ); dependencies = ( ); - name = dyldinfo; + name = dyld_info; productName = dyldinfo; - productReference = F9556D3920C1F896004DF62A /* dyldinfo */; + productReference = F9556D3920C1F896004DF62A /* dyld_info */; productType = "com.apple.product-type.tool"; }; F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */ = { @@ -1496,19 +1753,15 @@ ); name = nocr; productName = nocr; - productReference = F97FF3561C23638F000ACDD2 /* nocr */; + productReference = 37065AA72310856E00A20034 /* nocr */; productType = "com.apple.product-type.tool"; }; F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */ = { isa = PBXNativeTarget; buildConfigurationList = F98E37922332D048003706B4 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_root_mode_tool" */; buildPhases = ( - F98E37772332D048003706B4 /* create dyld_cache_config.h */, F98E37782332D048003706B4 /* Sources */, F98E378B2332D048003706B4 /* Frameworks */, - F98E378E2332D048003706B4 /* usr|share|man|man1 */, - F98E37902332D048003706B4 /* do not install duplicates */, - F98E37912332D048003706B4 /* mkdir /var/db/dyld */, ); buildRules = ( ); @@ -1558,8 +1811,6 @@ F9D050C811DD701A00FB0A29 /* configure archives */, F9ED4C950630A76000DF4E74 /* Sources */, F907E2490FA6469000BFEDBD /* install iPhone file */, - F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */, - 371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */, ); buildRules = ( F921D318070376B0000D1056 /* PBXBuildRule */, @@ -1567,9 +1818,6 @@ F921D3160703769A000D1056 /* PBXBuildRule */, ); dependencies = ( - C1033EA822611306004407FB /* PBXTargetDependency */, - F99B8EB20FEC220C00701838 /* PBXTargetDependency */, - F96543A11E343601003C5540 /* PBXTargetDependency */, ); name = dyld; productName = dyld; @@ -1581,9 +1829,8 @@ buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */; buildPhases = ( F9ED4C9C0630A76B00DF4E74 /* Sources */, - F959621018849DF20003E4D4 /* add dyld symlink */, F98F1FBB1E4029CA00EF868D /* Headers */, - F960A78C1E405E2300840176 /* expand dyld_priv.h macros */, + F960A78C1E405E2300840176 /* generate version headers */, F99006DF1E411C500013456D /* install dlfcn.h */, 37918AC32058912100F39A77 /* install ktrace codes file */, ); @@ -1622,8 +1869,20 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Latest; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1120; TargetAttributes = { + 3715A2FD232320BC0059433D = { + CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; + }; + 3721A634230CABAF00594066 = { + CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; + }; + 3721B6A72321A75B006F6AB7 = { + CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; + }; 37A0AD0A1C15FFF500731E50 = { CreatedOnToolsVersion = 8.0; }; @@ -1631,6 +1890,22 @@ CreatedOnToolsVersion = 10.0; ProvisioningStyle = Automatic; }; + C14965DF22BDCF6800568D15 = { + CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; + }; + C14C355F230531820059E04C = { + CreatedOnToolsVersion = 10.3; + ProvisioningStyle = Automatic; + }; + C1BDD43C234E8FA00095C7DC = { + CreatedOnToolsVersion = 11.2; + ProvisioningStyle = Automatic; + }; + C1BF4DAF2359254500B0F1AE = { + CreatedOnToolsVersion = 11.2; + ProvisioningStyle = Automatic; + }; F9556D3820C1F896004DF62A = { CreatedOnToolsVersion = 10.0; }; @@ -1649,13 +1924,11 @@ }; buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( - English, - Japanese, - French, - German, + en, + Base, ); mainGroup = F9ED4C870630A72200DF4E74; productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; @@ -1665,14 +1938,15 @@ F9ED4C920630A73900DF4E74 /* all */, 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */, F908134211D3ED0B00626CC1 /* libdyld */, + C1BDD43C234E8FA00095C7DC /* dyld_executables */, F9ED4C970630A76000DF4E74 /* dyld */, F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */, F92C7DE521E59840000D12B5 /* libdyld_driverkit */, F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */, - 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */, 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */, F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */, + C14965DF22BDCF6800568D15 /* dyld_app_cache_util */, F99B8E550FEC10F600701838 /* dyld_shared_cache_util */, F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */, F9F2A5580F7AEE9800B7C9EB /* libdsc */, @@ -1681,11 +1955,26 @@ F9F6F4271C1FB0A700BD8FED /* dyld_tests */, F97FF3551C23638F000ACDD2 /* nocr */, 37F597CC2061EB4200F9B6F9 /* dyld_usage */, - F9556D3820C1F896004DF62A /* dyldinfo */, + F9556D3820C1F896004DF62A /* dyld_info */, + 3721A634230CABAF00594066 /* test_support */, + 3721B6A72321A75B006F6AB7 /* chroot_util */, + 3715A2FD232320BC0059433D /* ContainerizedTestRunner */, + C14C355F230531820059E04C /* run-static */, + C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 3715A2FC232320BC0059433D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXShellScriptBuildPhase section */ 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = { isa = PBXShellScriptBuildPhase; @@ -1700,38 +1989,45 @@ ); 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[ \\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"; + shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\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 simulator\n# The target builds a dummy app that we delete\nif [ \"${PRODUCT_NAME}\" != \"dyld_sim\" ]\nthen\nOBJROOT_USAGE=\"${TARGET_TEMP_DIR}/Objects_Usage\"\nxcodebuild install -target dyld_usage SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_USAGE}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n"; - showEnvVarsInLog = 0; - }; - 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = { + 3715A3092327003C0059433D /* Build Everything */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( ); - name = "make dyld_cache_config.h"; + name = "Build Everything"; + outputFileListPaths = ( + ); outputPaths = ( - "$(DERIVED_FILE_DIR)/dyld_cache_config.h", ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64[ \\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"; + shellPath = /bin/sh; + shellScript = "$SRCROOT/build-scripts/ContainerizedTestRunner-build-everything.sh\n"; + showEnvVarsInLog = 0; + }; + 37E2FC9D22F62FE1004AF213 /* install */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = install; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "$SRCROOT/build-scripts/dyld_tests-install.sh\n"; showEnvVarsInLog = 0; }; C1225E3E21FA84BF0079CF9C /* create dyld_cache_config.h */ = { @@ -1751,7 +2047,7 @@ ); 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\nARM_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\necho -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\ngrep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\nif [ \"$?\" -eq \"0\" ]; then\necho -n \"#define ARM64_32_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_32_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nfi\nelse\necho \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\nexit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\nmkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\ncp \"${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\nmkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\ncp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n"; + shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\n"; showEnvVarsInLog = 0; }; F907E2490FA6469000BFEDBD /* install iPhone file */ = { @@ -1769,22 +2065,6 @@ shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n mkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n echo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n"; showEnvVarsInLog = 0; }; - F91083C91702592700831889 /* create dyld_cache_config.h */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "create dyld_cache_config.h"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/dyld_cache_config.h", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64[ \\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; - }; F94182D61E60E74E00D8EF25 /* pre-platform builds */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; @@ -1797,8 +2077,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "\nif [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n OBJROOT_LOCAL=\"${TARGET_TEMP_DIR}/Objects_Local\"\n xcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_LOCAL}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n if [ \"${RC_BRIDGE}\" != \"YES\" ]\n then\n OBJROOT_SIM=\"${TARGET_TEMP_DIR}/Objects_Sim\"\n xcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_SIM}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n fi\nelse\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects_Mac\"\n xcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects2_Mac\"\n xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n"; - showEnvVarsInLog = 0; + shellScript = "$SRCROOT/build-scripts/update_dyld_shared_cache-build.sh\n"; }; F951DA862228E5560057BA43 /* install headers */ = { isa = PBXShellScriptBuildPhase; @@ -1816,25 +2095,10 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "# dyld.h and dyld_priv.h are not for use by actual drivers, so they are both in the Runtime directory\nmkdir -p ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}\n${SRCROOT}/bin/expand.rb < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld_priv.h\ncp ${SRCROOT}/include/mach-o/dyld.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld.h\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dlfcn.h\n"; + shellScript = "# dyld.h and dyld_priv.h are not for use by actual drivers, so they are both in the Runtime directory\n\n$SRCROOT/build-scripts/libdyld-generate-version-headers.sh\nmkdir -p ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}\ncp ${SRCROOT}/include/mach-o/dyld.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld.h\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dlfcn.h\n"; showEnvVarsInLog = 0; }; - F959621018849DF20003E4D4 /* add dyld symlink */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "add dyld symlink"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/bash; - 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 */ = { + F960A78C1E405E2300840176 /* generate version headers */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( @@ -1842,13 +2106,13 @@ inputPaths = ( "${SRCROOT}/include/mach-o/dyld_priv.h", ); - name = "expand dyld_priv.h macros"; + name = "generate version headers"; outputPaths = ( "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h", ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "${SRCROOT}/bin/expand.rb < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n"; + shellScript = "$SRCROOT/build-scripts/libdyld-generate-version-headers.sh\n"; showEnvVarsInLog = 0; }; F96354301DCD74A400895049 /* create dyld_cache_config.h */ = { @@ -1864,68 +2128,7 @@ ); 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[ \\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; - }; - F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "mkdir /var/db/dyld"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld"; - showEnvVarsInLog = 0; - }; - F98E37772332D048003706B4 /* create dyld_cache_config.h */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "create dyld_cache_config.h"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/dyld_cache_config.h", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/bash; - shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64[ \\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; - }; - F98E37902332D048003706B4 /* do not install duplicates */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "do not install duplicates"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n rm -rf ${DSTROOT}/usr/local/include\n rm -rf ${DSTROOT}/usr/local/lib\n rm -rf ${DSTROOT}/usr/lib\nfi\n"; - showEnvVarsInLog = 0; - }; - F98E37912332D048003706B4 /* mkdir /var/db/dyld */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "mkdir /var/db/dyld"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld"; + shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\n"; showEnvVarsInLog = 0; }; F99006DF1E411C500013456D /* install dlfcn.h */ = { @@ -1940,7 +2143,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/usr/include/\n"; + shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/usr/include/\ncp ${SRCROOT}/include/dlfcn_private.h ${DSTROOT}/usr/local/include/\n# manual install of modulemap\ncp ${SRCROOT}/include/mach-o/dyld.modulemap ${DSTROOT}/usr/include/mach-o\n"; showEnvVarsInLog = 0; }; F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { @@ -1955,22 +2158,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n rm -rf ${DSTROOT}/usr/local/include\n rm -rf ${DSTROOT}/usr/local/lib\n rm -rf ${DSTROOT}/usr/lib\nfi\n"; - showEnvVarsInLog = 0; - }; - F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - name = "suppress macosx dyld_shared_cache_util"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\nrm -rf ${DSTROOT}/usr/local/bin/dyld_shared_cache_util\nrm -rf ${DSTROOT}/usr/local/bin/dyld_closure_util\nfi\n"; + shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_iterator.h\n rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_extractor.h\n rm -rf ${DSTROOT}/usr/local/lib/*.a\n rm -rf ${DSTROOT}/usr/lib/dsc_extractor.bundle\nfi\n"; showEnvVarsInLog = 0; }; F9D050C811DD701A00FB0A29 /* configure archives */ = { @@ -1986,7 +2174,7 @@ ); 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# 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"; + shellScript = "${SRCROOT}/build-scripts/configure-dyld-archives.sh\n"; showEnvVarsInLog = 0; }; F9F6F42B1C1FB0AE00BD8FED /* build */ = { @@ -2001,7 +2189,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n"; + shellScript = "$SRCROOT/build-scripts/dyld_tests-build.sh\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -2018,48 +2206,50 @@ C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */, F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */, F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */, + F971EC7B2343CD9A000BCEAA /* MachOAnalyzerSet.cpp in Sources */, 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */, C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */, + C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */, + C116F1A223F4D73A002D386B /* RootsChecker.cpp in Sources */, 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */, 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */, 37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */, 37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */, 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */, - 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */, 37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */, + E9C2FAD823AA8B5C0077E966 /* IMPCaches.cpp in Sources */, 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */, C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.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; }; - 377685F41AC4B27D00026E6C /* Sources */ = { + 3715A2FA232320BC0059433D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */, - F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */, - F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */, - C1F003CD213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */, - 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */, - 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */, - 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */, - 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */, - 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */, - 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */, - 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */, - 37554F3B1E3F0FD200407388 /* Manifest.mm 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 */, + 3715A301232320BD0059433D /* ContainerizedTestRunner.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3721A632230CABAF00594066 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37065AAB2310A18300A20034 /* execserver.defs in Sources */, + 37382F68230CADEE00E375CE /* test_support.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3721B6A42321A75B006F6AB7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 375A4C75233DE09E00CFBD6B /* Diagnostics.cpp in Sources */, + 375A4C74233DE07600CFBD6B /* MachOFile.cpp in Sources */, + 37CE9D1A2321A7EB001FBA91 /* chroot_util.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2071,31 +2261,90 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C14965DC22BDCF6800568D15 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1BF4DAB2357B14700B0F1AE /* kernel_collection_builder.cpp in Sources */, + C14965F822C3281A00568D15 /* ClosureFileSystemNull.cpp in Sources */, + C14965E822BEBF2300568D15 /* Diagnostics.cpp in Sources */, + C14965F622C327FB00568D15 /* PathOverrides.cpp in Sources */, + C14965F722C3280B00568D15 /* AdjustDylibSegments.cpp in Sources */, + C14965E722BDCF8300568D15 /* dyld_app_cache_util.cpp in Sources */, + C14965EA22BEC04800568D15 /* MachOFile.cpp in Sources */, + C14965ED22C09B6100568D15 /* FileUtils.cpp in Sources */, + C14965F022C3203200568D15 /* OptimizerLinkedit.cpp in Sources */, + C14965EE22C31F7C00568D15 /* CacheBuilder.cpp in Sources */, + C14965EC22BEC05800568D15 /* ClosureFileSystemPhysical.cpp in Sources */, + C14965F122C3203E00568D15 /* OptimizerBranches.cpp in Sources */, + C1BDD446234EAF500095C7DC /* MachOAppCache.cpp in Sources */, + C14965EB22BEC05000568D15 /* MachOLoaded.cpp in Sources */, + C1BDD443234EA7DD0095C7DC /* AppCacheBuilder.cpp in Sources */, + C14965E922BEBF2800568D15 /* MachOAnalyzer.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C14C355C230531820059E04C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C14C356B230539BE0059E04C /* MachOAnalyzer.cpp in Sources */, + C14C356A2305376A0059E04C /* Diagnostics.cpp in Sources */, + C14C3569230537630059E04C /* MachOFile.cpp in Sources */, + C18839E523480866004E30FA /* ClosureFileSystemPhysical.cpp in Sources */, + C14C356C230539C20059E04C /* MachOLoaded.cpp in Sources */, + C14C3563230531830059E04C /* testing/run-static/run-static.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; C187B9001FE063A40042D3B7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E9EC319A240B0BA8001705D6 /* IMPCaches.cpp in Sources */, C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */, C1F003D0213F3CB4002D9DC9 /* ClosureFileSystemNull.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 */, + C116F1A423F4D742002D386B /* RootsChecker.cpp in Sources */, C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */, C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */, C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */, + E9EC319E240B18E6001705D6 /* JSONReader.mm in Sources */, C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */, C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */, + F971EC7D2343CDB9000BCEAA /* MachOAnalyzerSet.cpp in Sources */, C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */, + C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */, C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */, C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + C1BF4DAD2359254500B0F1AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1BF4DC0235938CB00B0F1AE /* MachOAppCache.cpp in Sources */, + C1BF4DBF235938BD00B0F1AE /* MachOFile.cpp in Sources */, + C1BF4DBD235938B600B0F1AE /* MachOAnalyzer.cpp in Sources */, + C1BF4DB82359385700B0F1AE /* CacheBuilder.cpp in Sources */, + C1BF4DBB235938AC00B0F1AE /* OptimizerLinkedit.cpp in Sources */, + C1BF4DC1235938D400B0F1AE /* AdjustDylibSegments.cpp in Sources */, + C1BF4DB7235925BA00B0F1AE /* Diagnostics.cpp in Sources */, + C1BF4DB4235925A000B0F1AE /* kernel_collection_builder.cpp in Sources */, + C1BF4DBC235938B000B0F1AE /* OptimizerBranches.cpp in Sources */, + C1BF4DB92359386C00B0F1AE /* AppCacheBuilder.cpp in Sources */, + C1BF4DBE235938BA00B0F1AE /* MachOLoaded.cpp in Sources */, + C1BF4DB6235925B400B0F1AE /* ClosureFileSystemNull.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F92C7DE621E59840000D12B5 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2130,24 +2379,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */, - C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.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 */, - F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */, - F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */, - F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */, - F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */, - F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */, - F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */, - F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2159,7 +2391,9 @@ F9556D4520C21DD9004DF62A /* MachOFile.cpp in Sources */, F9556D4620C21DD9004DF62A /* MachOLoaded.cpp in Sources */, F9556D4720C21DD9004DF62A /* MachOAnalyzer.cpp in Sources */, + F971EC752342D373000BCEAA /* MachOAnalyzerSet.cpp in Sources */, F9556D4820C21DDF004DF62A /* ClosureFileSystemPhysical.cpp in Sources */, + F9FA17F4235A71DB009B0907 /* DyldSharedCache.cpp in Sources */, F9556D4920C21DF5004DF62A /* Diagnostics.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2169,6 +2403,9 @@ buildActionMask = 2147483647; files = ( F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */, + E9EC3199240B0B95001705D6 /* IMPCaches.cpp in Sources */, + E9EC319D240B18DC001705D6 /* JSONReader.mm in Sources */, + F971EC7C2343CDA4000BCEAA /* MachOAnalyzerSet.cpp in Sources */, C1F003CF213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */, F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */, F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */, @@ -2178,11 +2415,13 @@ F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */, F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */, F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */, + C116F1A323F4D73B002D386B /* RootsChecker.cpp in Sources */, F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */, F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */, F96354361DCD74A400895049 /* FileUtils.cpp in Sources */, F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */, F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */, + C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */, F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */, 37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */, F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */, @@ -2205,7 +2444,9 @@ F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */, F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */, F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */, + F971EC762342DF03000BCEAA /* MachOAnalyzerSet.cpp in Sources */, F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */, + C116F19E23F4B11B002D386B /* RootsChecker.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2213,8 +2454,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F93F46521FA420850060D9F9 /* execserver.defs in Sources */, - F97FF3611C23640C000ACDD2 /* nocr.c in Sources */, + F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2222,24 +2462,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F98E37792332D048003706B4 /* PathOverrides.cpp in Sources */, - F98E377A2332D048003706B4 /* ClosureFileSystemNull.cpp in Sources */, - F98E377B2332D048003706B4 /* Closure.cpp in Sources */, - F98E377C2332D048003706B4 /* ClosureWriter.cpp in Sources */, - F98E377D2332D048003706B4 /* ClosureFileSystemPhysical.cpp in Sources */, - F98E377E2332D048003706B4 /* ClosureBuilder.cpp in Sources */, - F98E377F2332D048003706B4 /* MachOFile.cpp in Sources */, - F98E37802332D048003706B4 /* MachOLoaded.cpp in Sources */, - F98E37812332D048003706B4 /* MachOAnalyzer.cpp in Sources */, F98E37822332D048003706B4 /* update_dyld_shared_cache.cpp in Sources */, - F98E37832332D048003706B4 /* DyldSharedCache.cpp in Sources */, - F98E37842332D048003706B4 /* CacheBuilder.cpp in Sources */, - F98E37852332D048003706B4 /* AdjustDylibSegments.cpp in Sources */, - F98E37862332D048003706B4 /* FileUtils.cpp in Sources */, - F98E37872332D048003706B4 /* Diagnostics.cpp in Sources */, - F98E37882332D048003706B4 /* OptimizerObjC.cpp in Sources */, - F98E37892332D048003706B4 /* OptimizerBranches.cpp in Sources */, - F98E378A2332D048003706B4 /* OptimizerLinkedit.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2247,6 +2470,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */, + C176CB5C2321AB74009C1259 /* ClosureFileSystemPhysical.cpp in Sources */, C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */, C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */, C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */, @@ -2254,7 +2479,7 @@ C1960ED22090D9FA007E3E6B /* MachOAnalyzer.cpp in Sources */, F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */, C1960ED12090D9F6007E3E6B /* MachOLoaded.cpp in Sources */, - F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */, + F97D395823A97B2C00BD3B5A /* dsc_extractor.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2262,8 +2487,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DE49C499238EC59500CD7FFB /* MachOFile.cpp in Sources */, + DE49C497238EC57B00CD7FFB /* Diagnostics.cpp in Sources */, + DE49C4A3238EEE3400CD7FFB /* dsc_iterator.cpp in Sources */, + DE49C49C238EC60D00CD7FFB /* Closure.cpp in Sources */, + DE49C496238EC55300CD7FFB /* DyldSharedCache.cpp in Sources */, + DE49C49A238EC5AD00CD7FFB /* MachOLoaded.cpp in Sources */, F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */, - F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2295,11 +2525,13 @@ F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */, F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */, F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */, + F971EC772343CD46000BCEAA /* MachOAnalyzerSet.cpp in Sources */, F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */, F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */, 373C58F1219CE478003442D5 /* BootArgs.cpp in Sources */, F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */, F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */, + C116F19C23F4B11B002D386B /* RootsChecker.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2329,8 +2561,10 @@ F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */, F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */, F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */, + F971EC782343CD63000BCEAA /* MachOAnalyzerSet.cpp in Sources */, F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */, F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */, + C116F19D23F4B11B002D386B /* RootsChecker.cpp in Sources */, F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */, F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */, ); @@ -2340,7 +2574,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */, + F97D395723A1CBCA00BD3B5A /* Diagnostics.cpp in Sources */, + F97D395523A1CBB600BD3B5A /* MachOFile.cpp in Sources */, + F97D395623A1CBB600BD3B5A /* MachOLoaded.cpp in Sources */, + DE9A811323982A3A00664840 /* dsc_iterator.cpp in Sources */, F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2348,36 +2585,66 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 3715A30B23272F890059433D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3721B6A72321A75B006F6AB7 /* chroot_util */; + targetProxy = 3715A30A23272F890059433D /* PBXContainerItemProxy */; + }; + 37382F6A230CB46500E375CE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3721A634230CABAF00594066 /* test_support */; + targetProxy = 37382F69230CB46500E375CE /* PBXContainerItemProxy */; + }; 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */; targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */; }; - C1033EA822611306004407FB /* PBXTargetDependency */ = { + C14C3568230531EA0059E04C /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F9D1001114D8D0BA00099D91 /* dsc_extractor */; - targetProxy = C1033EA722611306004407FB /* PBXContainerItemProxy */; + target = C14C355F230531820059E04C /* run-static */; + targetProxy = C14C3567230531EA0059E04C /* PBXContainerItemProxy */; }; C187B90C1FE067590042D3B7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */; targetProxy = C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */; }; - D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = { + C18F05362374D5B100DC6CCA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3721A634230CABAF00594066 /* test_support */; + targetProxy = C18F05352374D5B100DC6CCA /* PBXContainerItemProxy */; + }; + C1B4759723E65F9600515793 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 37F597CC2061EB4200F9B6F9 /* dyld_usage */; + targetProxy = C1B4759623E65F9600515793 /* PBXContainerItemProxy */; + }; + C1BF4DC32359390A00B0F1AE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */; + targetProxy = C1BF4DC22359390A00B0F1AE /* PBXContainerItemProxy */; + }; + C1C6403723E4EC1300ED4B46 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9D1001114D8D0BA00099D91 /* dsc_extractor */; + targetProxy = C1C6403623E4EC1300ED4B46 /* PBXContainerItemProxy */; + }; + C1C6403923E4EC1C00ED4B46 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = C1C6403823E4EC1C00ED4B46 /* PBXContainerItemProxy */; + }; + C1C6403B23E4EC3000ED4B46 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */; - targetProxy = D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */; + targetProxy = C1C6403A23E4EC3000ED4B46 /* PBXContainerItemProxy */; }; F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */; }; - F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; - targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */; - }; F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; @@ -2388,20 +2655,10 @@ target = F9D1001114D8D0BA00099D91 /* dsc_extractor */; targetProxy = F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */; }; - F96543A11E343601003C5540 /* PBXTargetDependency */ = { + F9A8E1B024120DD000CEB6BF /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */; - targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */; - }; - F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F97FF3551C23638F000ACDD2 /* nocr */; - targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */; - }; - F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; - targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */; + target = F9556D3820C1F896004DF62A /* dyld_info */; + targetProxy = F9A8E1AF24120DD000CEB6BF /* PBXContainerItemProxy */; }; F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -2465,7 +2722,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( @@ -2476,8 +2733,7 @@ SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = macosx; TOOLCHAINS = default; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; + USER_HEADER_SEARCH_PATHS = ""; }; name = Debug; }; @@ -2520,7 +2776,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = ( "-DBOM_SUPPORT=1", @@ -2530,126 +2786,245 @@ SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = macosx; TOOLCHAINS = default; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; + USER_HEADER_SEARCH_PATHS = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; - 377686001AC4B27D00026E6C /* Debug */ = { + 3715A307232320BD0059433D /* 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 = NO; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = YES; - ENABLE_STRICT_OBJC_MSGSEND = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", - "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", - "$(SDKROOT)/AppleInternal/Library/Frameworks", - ); - GCC_C_LANGUAGE_STANDARD = gnu99; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_CPP_EXCEPTIONS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", - "BUILDING_CACHE_BUILDER=1", ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = ( - "-DBOM_SUPPORT=1", - "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1", - ); - PRODUCT_NAME = multi_dyld_shared_cache_builder; + INFOPLIST_FILE = local_test_runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\""; + PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner"; + PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; - STRIP_INSTALLED_PRODUCT = NO; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; + STRIP_INSTALLED_PRODUCT = YES; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include"; }; name = Debug; }; - 377686011AC4B27D00026E6C /* Release */ = { + 3715A308232320BD0059433D /* 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 = NO; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", - "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", - "$(SDKROOT)/AppleInternal/Library/Frameworks", - ); - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_ENABLE_CPP_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1"; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + INFOPLIST_FILE = local_test_runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = ( - "-DBOM_SUPPORT=1", - "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1", - ); - PRODUCT_NAME = multi_dyld_shared_cache_builder; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = "-DCHROOT_PATH=\\\"$(DERIVED_FILES_DIR)/TestRoot\\\""; + PRODUCT_BUNDLE_IDENTIFIER = "com.apple.dyld.local-test-runner"; + PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = YES; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $SRCROOT/testing/include"; + }; + name = Release; + }; + 3721A636230CABAF00594066 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + EXECUTABLE_PREFIX = lib; + EXPORTED_SYMBOLS_FILE = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_MASTER_OBJECT_FILE = YES; + LLVM_LTO = NO; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SKIP_INSTALL = YES; STRIP_INSTALLED_PRODUCT = NO; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; - VERSIONING_SYSTEM = "apple-generic"; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include"; + }; + name = Debug; + }; + 3721A637230CABAF00594066 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_NS_ASSERTIONS = NO; + EXECUTABLE_PREFIX = lib; + EXPORTED_SYMBOLS_FILE = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_ENABLE_OBJC_EXCEPTIONS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_MASTER_OBJECT_FILE = YES; + LLVM_LTO = NO; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRELINK_FLAGS = "-exported_symbols_list $(SRCROOT)/testing/lib/test_support.exp"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SKIP_INSTALL = YES; + STRIP_INSTALLED_PRODUCT = NO; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include"; + }; + name = Release; + }; + 3721B6AC2321A75B006F6AB7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + 3721B6AD2321A75B006F6AB7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; }; name = Release; }; 37A0AD0C1C15FFF500731E50 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -2657,6 +3032,8 @@ 37A0AD0D1C15FFF500731E50 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -2698,7 +3075,6 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; - VALID_ARCHS = "arm64 arm64e x86_64 arm64_32 armv7k"; }; name = Debug; }; @@ -2733,7 +3109,118 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; - VALID_ARCHS = "arm64 arm64e x86_64 arm64_32 armv7k"; + }; + name = Release; + }; + C14965E522BDCF6900568D15 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "BUILDING_CACHE_BUILDER=1", + "BUILDING_APP_CACHE_UTIL=1", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + C14965E622BDCF6900568D15 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "BUILDING_CACHE_BUILDER=1", + "BUILDING_APP_CACHE_UTIL=1", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Release; + }; + C14C3565230531830059E04C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "BUILDING_RUN_STATIC=1", + "$(inherited)", + ); + INSTALL_PATH = /AppleInternal/CoreOS/tests/dyld; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.14; + OTHER_CODE_SIGN_FLAGS = "--entitlements $SRCROOT/testing/run-static/jit_entitlement.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; + }; + name = Debug; + }; + C14C3566230531830059E04C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_RUN_STATIC=1"; + INSTALL_PATH = /AppleInternal/CoreOS/tests/dyld; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.14; + OTHER_CODE_SIGN_FLAGS = "--entitlements $SRCROOT/testing/run-static/jit_entitlement.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; }; name = Release; }; @@ -2771,8 +3258,7 @@ SDKROOT = macosx.internal; STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = "non-global"; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; + USER_HEADER_SEARCH_PATHS = ""; }; name = Debug; }; @@ -2812,12 +3298,85 @@ PRODUCT_NAME = slc_builder; SDKROOT = macosx.internal; STRIP_STYLE = "non-global"; - USER_HEADER_SEARCH_PATHS = "../launch-cache/"; - VALID_ARCHS = "x86_64 x86_64h"; + USER_HEADER_SEARCH_PATHS = ""; ZERO_LINK = NO; }; name = Release; }; + C1BDD43D234E8FA00095C7DC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INSTALLHDRS_SCRIPT_PHASE = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + C1BDD43E234E8FA00095C7DC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INSTALLHDRS_SCRIPT_PHASE = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + C1BF4DB22359254500B0F1AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "BUILDING_CACHE_BUILDER=1", + "BUILDING_APP_CACHE_UTIL=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = "$(DT_TOOLCHAIN_DIR)/usr/lib"; + LD_DYLIB_INSTALL_NAME = "@rpath/$(PRODUCT_NAME).dylib"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_LDFLAGS = "-Wl,-no_warn_inits"; + OTHER_TAPI_FLAGS = "-extra-private-header $(SRCROOT)/dyld3/shared-cache/kernel_collection_builder.h"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; + }; + name = Debug; + }; + C1BF4DB32359254500B0F1AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "BUILDING_CACHE_BUILDER=1", + "BUILDING_APP_CACHE_UTIL=1", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = "$(DT_TOOLCHAIN_DIR)/usr/lib"; + LD_DYLIB_INSTALL_NAME = "@rpath/$(PRODUCT_NAME).dylib"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_TAPI_FLAGS = "-extra-private-header $(SRCROOT)/dyld3/shared-cache/kernel_collection_builder.h"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; + }; + name = Release; + }; F908134311D3ED0C00626CC1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2998,7 +3557,6 @@ GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -3010,7 +3568,6 @@ PRODUCT_NAME = update_dyld_shared_cache; SDKROOT = macosx.internal; USE_HEADERMAP = NO; - VALID_ARCHS = x86_64; }; name = Debug; }; @@ -3041,7 +3598,6 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = /usr/bin; @@ -3053,7 +3609,6 @@ STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; USE_HEADERMAP = NO; - VALID_ARCHS = x86_64; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -3075,6 +3630,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3082,6 +3638,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", + "BUILDING_DYLDINFO=1", "$(inherited)", ); GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -3089,6 +3646,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_STYLE = debugging; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; }; name = Debug; @@ -3110,14 +3668,17 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_DYLDINFO=1"; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_STYLE = debugging; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; }; name = Release; @@ -3146,12 +3707,10 @@ GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources"; MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = "-stdlib=libc++"; @@ -3159,7 +3718,6 @@ SDKROOT = macosx.internal; SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)"; USE_HEADERMAP = NO; - VALID_ARCHS = "x86_64 x86_64h"; }; name = Debug; }; @@ -3184,10 +3742,8 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources"; MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; OTHER_LDFLAGS = "-stdlib=libc++"; @@ -3197,7 +3753,6 @@ STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; USE_HEADERMAP = NO; - VALID_ARCHS = "x86_64 x86_64h"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -3235,6 +3790,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-DBUILDING_CLOSURE_UTIL=1"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; @@ -3267,6 +3823,7 @@ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = "-DBUILDING_CLOSURE_UTIL=1"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; @@ -3316,6 +3873,7 @@ MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include"; }; name = Debug; }; @@ -3358,6 +3916,7 @@ MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx.internal; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache ./testing/include"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -3392,7 +3951,6 @@ GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; GCC_WARN_MISSING_PARENTHESES = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; GCC_WARN_UNUSED_FUNCTION = YES; @@ -3434,7 +3992,6 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; - GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; GCC_WARN_SHADOW = YES; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = /usr/bin; @@ -3461,6 +4018,8 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1"; PRODUCT_NAME = dyld_shared_cache_util; SDKROOT = macosx.internal; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos"; @@ -3476,6 +4035,8 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.14; + OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1"; PRODUCT_NAME = dyld_shared_cache_util; SDKROOT = macosx.internal; SKIP_INSTALL = NO; @@ -3579,6 +4140,7 @@ "$(ENTRY)", "-Wl,-fixup_chains", "-Wl,-data_const", + "-fapple-link-rtlib", ); STRIPFLAGS = "-S"; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; @@ -3630,6 +4192,7 @@ "-Wl,-data_const", "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__auth_ptr:__const:__crash_info:__data:__bss:__common", "-Wl,-fixup_chains", + "-fapple-link-rtlib", ); STRIPFLAGS = "-S"; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; @@ -3667,6 +4230,7 @@ GCC_WARN_SHADOW = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GENERATE_TEXT_BASED_STUBS = YES; + HEADER_SEARCH_PATHS = ./include; INSTALLHDRS_COPY_PHASE = YES; INSTALLHDRS_SCRIPT_PHASE = YES; IS_ZIPPERED = YES; @@ -3678,14 +4242,17 @@ ); OTHER_LDFLAGS = ( "-Wl,-no_inits", + "$(USE_CHAINED_FIXUPS)", "-nostdlib", "-lCrashReporterClient", "$(LIBSYSTEM_LIBS)", "-umbrella", System, "-L$(SDKROOT)/usr/lib/system", + "$(EXTRA_SECTIONS)", + "-Wl,-unexported_symbol,__ZNSt3__18in_placeE", ); - OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.h -ObjC++ -std=c++11 -umbrella System"; + OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./include/dlfcn_private.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.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"; @@ -3693,6 +4260,7 @@ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; SUPPORTS_TEXT_BASED_API = YES; TAPI_VERIFY_MODE = Pedantic; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $(DERIVED_FILE_DIR)"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))"; WARNING_CFLAGS = ( @@ -3727,6 +4295,7 @@ GCC_WARN_SHADOW = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GENERATE_TEXT_BASED_STUBS = YES; + HEADER_SEARCH_PATHS = ./include; INSTALLHDRS_COPY_PHASE = YES; INSTALLHDRS_SCRIPT_PHASE = YES; IS_ZIPPERED = YES; @@ -3737,14 +4306,17 @@ ); OTHER_LDFLAGS = ( "-Wl,-no_inits", + "$(USE_CHAINED_FIXUPS)", "-nostdlib", "-lCrashReporterClient", "$(LIBSYSTEM_LIBS)", "-umbrella", System, "-L$(SDKROOT)/usr/lib/system", + "$(EXTRA_SECTIONS)", + "-Wl,-unexported_symbol,__ZNSt3__18in_placeE", ); - OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.h -ObjC++ -std=c++11 -umbrella System"; + OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./include/dlfcn_private.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.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"; @@ -3754,6 +4326,7 @@ SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; SUPPORTS_TEXT_BASED_API = YES; TAPI_VERIFY_MODE = Pedantic; + USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $(DERIVED_FILE_DIR)"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))"; WARNING_CFLAGS = ( @@ -3819,6 +4392,7 @@ HEADER_SEARCH_PATHS = ./include; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache"; WARNING_CFLAGS = "-Wimplicit-fallthrough"; }; @@ -3863,6 +4437,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ./include; SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = YES; USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache"; WARNING_CFLAGS = "-Wimplicit-fallthrough"; }; @@ -3878,6 +4453,7 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDSC=1"; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; OTHER_CPLUSPLUSFLAGS = ( @@ -3899,6 +4475,7 @@ GCC_ENABLE_OBJC_EXCEPTIONS = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_MODEL_TUNING = G5; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDSC=1"; GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -3924,6 +4501,8 @@ buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; + SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders ${SDKROOT)/usr/local/include"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include"; }; name = Debug; }; @@ -3932,6 +4511,8 @@ buildSettings = { PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator"; + SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders ${SDKROOT)/usr/local/include"; + USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include"; }; name = Release; }; @@ -3947,11 +4528,29 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = { + 3715A306232320BD0059433D /* Build configuration list for PBXNativeTarget "ContainerizedTestRunner" */ = { isa = XCConfigurationList; buildConfigurations = ( - 377686001AC4B27D00026E6C /* Debug */, - 377686011AC4B27D00026E6C /* Release */, + 3715A307232320BD0059433D /* Debug */, + 3715A308232320BD0059433D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3721A638230CABAF00594066 /* Build configuration list for PBXNativeTarget "test_support" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3721A636230CABAF00594066 /* Debug */, + 3721A637230CABAF00594066 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3721B6AE2321A75B006F6AB7 /* Build configuration list for PBXNativeTarget "chroot_util" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3721B6AC2321A75B006F6AB7 /* Debug */, + 3721B6AD2321A75B006F6AB7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -3974,6 +4573,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C14965E422BDCF6900568D15 /* Build configuration list for PBXNativeTarget "dyld_app_cache_util" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C14965E522BDCF6900568D15 /* Debug */, + C14965E622BDCF6900568D15 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C14C3564230531830059E04C /* Build configuration list for PBXNativeTarget "run-static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C14C3565230531830059E04C /* Debug */, + C14C3566230531830059E04C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3983,6 +4600,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C1BDD43F234E8FA00095C7DC /* Build configuration list for PBXAggregateTarget "dyld_executables" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C1BDD43D234E8FA00095C7DC /* Debug */, + C1BDD43E234E8FA00095C7DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C1BF4DB12359254500B0F1AE /* Build configuration list for PBXNativeTarget "libKernelCollectionBuilder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C1BF4DB22359254500B0F1AE /* Debug */, + C1BF4DB32359254500B0F1AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -4010,7 +4645,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyldinfo" */ = { + F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyld_info" */ = { isa = XCConfigurationList; buildConfigurations = ( F9556D3D20C1F896004DF62A /* Debug */, diff --git a/dyld3/APIs.cpp b/dyld3/APIs.cpp index 6f8550c..caae427 100644 --- a/dyld3/APIs.cpp +++ b/dyld3/APIs.cpp @@ -54,10 +54,13 @@ #include "ClosureBuilder.h" #include "ClosureFileSystemPhysical.h" +#include + #if __has_feature(ptrauth_calls) #include #endif +extern mach_header __dso_handle; namespace dyld { extern dyld_all_image_infos dyld_all_image_infos; @@ -77,10 +80,6 @@ static const void *stripPointer(const void *ptr) { 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"); @@ -121,6 +120,11 @@ const char* _dyld_get_image_name(uint32_t imageIndex) return gAllImages.imagePathByIndex(imageIndex); } +const struct mach_header * _dyld_get_prog_image_header() +{ + log_apis("_dyld_get_prog_image_header()\n"); + return gAllImages.mainExecutable(); +} static bool nameMatch(const char* installName, const char* libraryName) { @@ -304,7 +308,18 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) 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 result = dyld3::dyld_get_sdk_version(gAllImages.mainExecutable()); +#if TARGET_OS_OSX + // HACK: We didn't have time to fix all the zippered clients in the spring releases, so keep the mapping. We have resolved it for all new clients using the platform aware SPIs. Since we are doing to deprecate this SPI we will leave the hack in. + if (dyld_get_active_platform() == (dyld_platform_t)dyld3::Platform::iOSMac) { + if (result >= 0x000D0400) { + result = 0x000A0F04; + } else { + result = 0x000A0F00; + } + } +#endif + return result; } uint32_t dyld_get_min_os_version(const mach_header* mh) @@ -334,9 +349,7 @@ dyld_platform_t dyld_get_active_platform(void) { dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) { switch (platform) { -#ifdef PLATFORM_IOSMAC - case PLATFORM_IOSMAC: return PLATFORM_IOS; -#endif + case PLATFORM_MACCATALYST: return PLATFORM_IOS; case PLATFORM_IOSSIMULATOR: return PLATFORM_IOS; case PLATFORM_WATCHOSSIMULATOR: return PLATFORM_WATCHOS; case PLATFORM_TVOSSIMULATOR: return PLATFORM_TVOS; @@ -355,8 +368,24 @@ bool dyld_is_simulator_platform(dyld_platform_t platform) { } } +static +dyld_build_version_t mapFromVersionSet(dyld_build_version_t version) { + if (version.platform != 0xffffffff) return version; + auto i = std::lower_bound(sVersionMap.begin(), sVersionMap.end(), version.version); + assert(i != sVersionMap.end()); + switch(dyld3::dyld_get_base_platform(::dyld_get_active_platform())) { + case PLATFORM_MACOS: return { .platform = PLATFORM_MACOS, .version = i->macos }; + case PLATFORM_IOS: return { .platform = PLATFORM_IOS, .version = i->ios }; + case PLATFORM_WATCHOS: return { .platform = PLATFORM_WATCHOS, .version = i->watchos }; + case PLATFORM_TVOS: return { .platform = PLATFORM_TVOS, .version = i->tvos }; + case PLATFORM_BRIDGEOS: return { .platform = PLATFORM_BRIDGEOS, .version = i->bridgeos }; + default: return { .platform = 0, .version = 0 }; + } +} + bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) { __block bool retval = false; + version = mapFromVersionSet(version); 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; @@ -367,6 +396,7 @@ bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t versio bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) { __block bool retval = false; + version = mapFromVersionSet(version); 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; @@ -375,15 +405,7 @@ bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t vers return retval; } -bool dyld_program_sdk_at_least(dyld_build_version_t version) { - return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version); -} - -bool dyld_program_minos_at_least(dyld_build_version_t version) { - return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version); -} - -#if TARGET_OS_OSX || TARGET_OS_IOS +#if TARGET_OS_OSX static uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) { __block uint32_t retval = 0; @@ -401,17 +423,18 @@ uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) { #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) { +#if TARGET_OS_IOS + // 7.0 is the last version that was in iOSes mapping table, and it is the earliest version that support 64 bit binarie. + // Since we dropped 32 bit support, we know any binary with a version must be from 7.0 + return 0x00070000; +#elif TARGET_OS_OSX // This is a binary without a version load command, we need to infer things struct DylibToOSMapping { uint32_t dylibVersion; uint32_t osVersion; }; - uint32_t linkedVersion = 0; -#if TARGET_OS_OSX - linkedVersion = linkedDylibVersion(mh, "/usr/lib/libSystem.B.dylib"); + uint32_t 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 }, { PACKED_VERSION(159,0,0), 0x000A0700 }, { PACKED_VERSION(169,3,0), 0x000A0800 }, @@ -420,31 +443,6 @@ static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) { // We don't need to expand this table because all recent // binaries have LC_VERSION_MIN_ load command. }; -#elif TARGET_OS_IOS - linkedVersion = linkedDylibVersion(mh, "/System/Library/Frameworks/Foundation.framework/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=versionMapping; ; ++p) { @@ -457,6 +455,7 @@ static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) { lastOsVersion = p->osVersion; } } +#endif return 0; } @@ -471,9 +470,6 @@ static void dyld_get_image_versions_internal(const struct mach_header* mh, void if (sdk == 0) { sdk = deriveVersionFromDylibs(mh); } - if (platform == dyld3::Platform::iOSMac) { - sdk = 0x000A0F00; - } callback((const dyld_platform_t)platform, sdk, minOS); }); @@ -508,29 +504,24 @@ void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld // FIXME: Once dyld2 is gone gAllImages.mainExecutable() will be valid in all cases // and we can stop calling _NSGetMachExecuteHeader() if (mh == (const struct mach_header*)_NSGetMachExecuteHeader()) { - // Cache the main executable and short circuit parsing the - if (mainExecutablePlatform == 0) { - dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) { -#if 0 - //FIXME: Reenable this once Libc supports dynamic platforms. - if (platform == PLATFORM_MACOS && dyld_get_active_platform() == PLATFORM_IOSMAC) { - //FIXME: This version should be generated at link time - mainExecutablePlatform = PLATFORM_IOSMAC; - mainExecutableSDKVersion = 0x000D0000; - mainExecutableMinOSVersion = 0x000D0000; - } else { - mainExecutablePlatform = platform; - mainExecutableSDKVersion = sdk_version; - mainExecutableMinOSVersion = min_version; - } -#else - mainExecutablePlatform = platform; - mainExecutableSDKVersion = sdk_version; - mainExecutableMinOSVersion = min_version; -#endif - //FIXME: Assert if more than one command? - }); + if (mainExecutablePlatform) { + return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion); } + mainExecutablePlatform = ::dyld_get_active_platform(); + dyld_get_image_versions_internal(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) { + if (platform == PLATFORM_MACOS && dyld_get_base_platform(mainExecutablePlatform) == PLATFORM_IOS) { + // We are running with DYLD_FORCE_PLATFORM, use the current OSes values + dyld_get_image_versions_internal(&__dso_handle, ^(dyld_platform_t dyld_platform, uint32_t dyld_sdk_version, uint32_t dyld_min_version) { + if (dyld_get_base_platform(dyld_platform) == PLATFORM_IOS) { + mainExecutableSDKVersion = dyld_sdk_version; + mainExecutableMinOSVersion = dyld_min_version; + } + }); + } else { + mainExecutableSDKVersion = sdk_version; + mainExecutableMinOSVersion = min_version; + } + }); return callback(mainExecutablePlatform, mainExecutableSDKVersion, mainExecutableMinOSVersion); } #if TARGET_OS_EMBEDDED @@ -554,6 +545,120 @@ void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld dyld_get_image_versions_internal(mh, callback); } +struct VIS_HIDDEN VersionSPIDispatcher { + static bool dyld_program_minos_at_least(dyld_build_version_t version) { + return dyld_program_minos_at_least_active(version); + } + static bool dyld_program_sdk_at_least(dyld_build_version_t version) { + return dyld_program_sdk_at_least_active(version); + } +private: + // We put these into a struct to guarantee so we can control the placement to guarantee a version and the set equivalent + // Can be loaded via a single load pair instruction. + struct FastPathData { + uint32_t version; + uint32_t versionSetEquivalent; + dyld_platform_t platform; + }; + static uint32_t findVersionSetEquuivalent(uint32_t version) { + uint32_t candidateVersion = 0; + uint32_t candidateVersionEquivalent = 0; + uint32_t newVersionSetVersion = 0; + for (const auto& i : sVersionMap) { + switch (dyld_get_base_platform(::dyld_get_active_platform())) { + case PLATFORM_MACOS: newVersionSetVersion = i.macos; break; + case PLATFORM_IOS: newVersionSetVersion = i.ios; break; + case PLATFORM_WATCHOS: newVersionSetVersion = i.watchos; break; + case PLATFORM_TVOS: newVersionSetVersion = i.tvos; break; + case PLATFORM_BRIDGEOS: newVersionSetVersion = i.bridgeos; break; + default: newVersionSetVersion = 0xffffffff; // If we do not know about the platform it is newer than everything + } + if (newVersionSetVersion > version) { break; } + candidateVersion = newVersionSetVersion; + candidateVersionEquivalent = i.set; + } + return candidateVersionEquivalent; + }; + + static void setVersionMappingFastPathData(const struct mach_header* mh) { + dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) { + minosFastPathData.platform = dyld_get_base_platform(::dyld_get_active_platform()); + sdkFastPathData.platform = dyld_get_base_platform(::dyld_get_active_platform()); + minosFastPathData.version = min_version; + minosFastPathData.versionSetEquivalent = findVersionSetEquuivalent(min_version); + sdkFastPathData.version = sdk_version; + sdkFastPathData.versionSetEquivalent = findVersionSetEquuivalent(sdk_version); + }); + } + + static void setupFastPath(void) { + setVersionMappingFastPathData((const struct mach_header*)_NSGetMachExecuteHeader()); + dyld_program_minos_at_least_active = &dyld_program_minos_at_least_fast; + dyld_program_sdk_at_least_active = &dyld_program_sdk_at_least_fast; + } + + static bool dyld_program_minos_at_least_slow (dyld_build_version_t version) { + setupFastPath(); + return dyld_program_minos_at_least_fast(version); + } + + static bool dyld_program_sdk_at_least_slow (dyld_build_version_t version) { + setupFastPath(); + return dyld_program_sdk_at_least_fast(version); + } + + // Fast path implementation of version checks for main executables + // This works by using the fact that are essentially 3 cases we care about: + // 1. Comparing the exctuable against any other platform (which should always return false) + // 2. Comparing the exctuable against a version set (platform 0xfffffff) + // 3. Comparing the exctuable againstt our base platform + // + // We achieve this by setting up a single compare (currentVersion >= version.version) and a couple of + // of simple tests that will all compile to conditional moves to setup that compare: + // 1. We setup the comapreVersion as 0. It will only keep that value if it is not a version set and it + // it is not the platform we are testing against. 0 will be less than the value encoded in any well + // formed binary, so the test will end up returning false + // 2. If the platform is 0xffffffff it is a version set. In the fast path setup we we calculated a value + // that allows a direct comparison, so we set comapreVersion to that (versionSetEquivalent) + // 3. If it is a concrete platform and it matches the current platform running then we can set comapreVersion + // to the actual version number that the was embedded in the binary, which is we stashed in the fast + // path data + + static bool dyld_program_minos_at_least_fast (dyld_build_version_t version) { + uint32_t currentVersion = 0; + if (version.platform == 0xffffffff) { currentVersion = minosFastPathData.versionSetEquivalent; } + if (version.platform == minosFastPathData.platform) { currentVersion = minosFastPathData.version; } + return (currentVersion >= version.version); + } + + static bool dyld_program_sdk_at_least_fast (dyld_build_version_t version) { + uint32_t currentVersion = 0; + if (version.platform == 0xffffffff) { currentVersion = sdkFastPathData.versionSetEquivalent ; } + if (version.platform == sdkFastPathData.platform) { currentVersion = sdkFastPathData.version; } + return (currentVersion >= version.version); + } + + static bool (*dyld_program_minos_at_least_active)(dyld_build_version_t version); + static bool (*dyld_program_sdk_at_least_active)(dyld_build_version_t version); + static FastPathData minosFastPathData; + static FastPathData sdkFastPathData; +}; + +bool (*VersionSPIDispatcher::dyld_program_minos_at_least_active)(dyld_build_version_t version) = &VersionSPIDispatcher::dyld_program_minos_at_least_slow; +bool (*VersionSPIDispatcher::dyld_program_sdk_at_least_active)(dyld_build_version_t version) = &VersionSPIDispatcher::dyld_program_sdk_at_least_slow; +VersionSPIDispatcher::FastPathData VersionSPIDispatcher::minosFastPathData = {0, 0, 0}; +VersionSPIDispatcher::FastPathData VersionSPIDispatcher::sdkFastPathData = {0, 0, 0}; + + +// We handle this directly instead of dispatching through dyld3::dyld_program_sdk_at_least because they are very perf sensitive +bool dyld_program_minos_at_least(dyld_build_version_t version) { + return VersionSPIDispatcher::dyld_program_minos_at_least(version); +} + +bool dyld_program_sdk_at_least(dyld_build_version_t version) { + return VersionSPIDispatcher::dyld_program_sdk_at_least(version); +} + uint32_t dyld_get_program_min_os_version() { log_apis("dyld_get_program_min_os_version()\n"); @@ -853,7 +958,8 @@ void* dlopen_internal(const char* path, int mode, void* callerAddress) else leafName = path; -#if __IPHONE_OS_VERSION_MIN_REQUIRED + +#if TARGET_OS_IPHONE // dyld3: dlopen() not working with non-canonical paths char canonicalPath[PATH_MAX]; if ( leafName != path ) { @@ -914,8 +1020,8 @@ bool dlopen_preflight_internal(const char* path) DYLD_LOAD_LOCK_THIS_BLOCK log_apis("dlopen_preflight(%s)\n", path); - // check if path is in dyld shared cache - if ( gAllImages.dyldCacheHasPath(path) ) + // check if path is in dyld shared cache, or is a symlink to the cache + if ( _dyld_shared_cache_contains_path(path) ) return true; // check if file is loadable @@ -928,8 +1034,6 @@ bool dlopen_preflight_internal(const char* path) return true; } - // FIXME: may be symlink to something in dyld cache - return false; } @@ -1137,6 +1241,12 @@ bool _dyld_shared_cache_is_locally_built() return false; } +uint32_t _dyld_launch_mode() +{ + return gAllImages.launchMode(); +} + + void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[]) { log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos); @@ -1238,13 +1348,13 @@ void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple ar static void* mapStartOfCache(const char* path, size_t length) { struct stat statbuf; - if ( ::stat(path, &statbuf) == -1 ) + if ( dyld3::stat(path, &statbuf) == -1 ) return NULL; if ( statbuf.st_size < length ) return NULL; - int cache_fd = ::open(path, O_RDONLY); + int cache_fd = dyld3::open(path, O_RDONLY, 0); if ( cache_fd < 0 ) return NULL; @@ -1278,7 +1388,7 @@ static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) { uuid_t foundUuid; cache->getUUID(foundUuid); - if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) { + if ( (::memcmp(cache, "dyld_", 5) != 0) || (::memcmp(foundUuid, cacheUuid, 16) != 0) ) { // wrong uuid, unmap and keep looking ::munmap((void*)cache, 0x00100000); } @@ -1310,12 +1420,11 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr } if ( sharedCache == nullptr ) { // if not, look in default location for cache files - #if __IPHONE_OS_VERSION_MIN_REQUIRED - const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR; + #if TARGET_OS_IPHONE + sharedCache = findCacheInDirAndMap(cacheUuid, IPHONE_DYLD_SHARED_CACHE_DIR, sizeMapped); #else - const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR; - #endif - sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped); + sharedCache = findCacheInDirAndMap(cacheUuid, MACOSX_MRM_DYLD_SHARED_CACHE_DIR, sizeMapped); + #endif // if not there, look in extra search locations if ( sharedCache == nullptr ) { for (const char** p = extraSearchDirs; *p != nullptr; ++p) { @@ -1330,7 +1439,8 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr // get base address of cache __block uint64_t cacheUnslidBaseAddress = 0; - sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags) { if ( cacheUnslidBaseAddress == 0 ) cacheUnslidBaseAddress = vmAddr; }); @@ -1361,9 +1471,9 @@ int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(cons return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback); } -bool dyld_need_closure(const char* execPath, const char* tempDir) +bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir) { - log_apis("dyld_need_closure()\n"); + log_apis("dyld_need_closure(%s)\n", execPath); // We don't need to build a closure if the shared cache has it already const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress(); @@ -1372,11 +1482,29 @@ bool dyld_need_closure(const char* execPath, const char* tempDir) return false; } + // this SPI changed. Originally the second path was to $TMPDIR, now it is $HOME + // if called old way, adjust + size_t rootDirLen = strlen(dataContainerRootDir); + char homeFromTmp[PATH_MAX]; + if ( (rootDirLen > 5) && (strcmp(&dataContainerRootDir[rootDirLen-4], "/tmp") == 0) && (rootDirLen < PATH_MAX) ) { + strlcpy(homeFromTmp, dataContainerRootDir, PATH_MAX); + homeFromTmp[rootDirLen-4] = '\0'; + dataContainerRootDir = homeFromTmp; + } + + // dummy up envp needed by buildClosureCachePath() + char strBuf[PATH_MAX+8]; // room for HOME= and max path + strcpy(strBuf, "HOME="); + strlcat(strBuf, dataContainerRootDir, sizeof(strBuf)); + const char* envp[2]; + envp[0] = strBuf; + envp[1] = nullptr; char closurePath[PATH_MAX]; - if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath, closurePath, tempDir, false) ) { + if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath, envp, false, closurePath) ) { struct stat statbuf; - return (::stat(closurePath, &statbuf) != 0); + // if no file at location where closure would be stored, then need to build a closure + return (dyld3::stat(closurePath, &statbuf) != 0); } // Not containerized so no point in building a closure. @@ -1389,7 +1517,14 @@ void _dyld_missing_symbol_abort() // dyld3 binds all such missing symbols to this one handler. // We need the crash log to contain the backtrace so someone can // figure out the symbol. - abort_report_np("missing lazy symbol called"); + + auto allImageInfos = gAllImages.oldAllImageInfo(); + allImageInfos->errorKind = DYLD_EXIT_REASON_SYMBOL_MISSING; + allImageInfos->errorClientOfDylibPath = ""; + allImageInfos->errorTargetDylibPath = ""; + allImageInfos->errorSymbol = ""; + + halt("missing lazy symbol called"); } const char* _dyld_get_objc_selector(const char* selName) @@ -1410,6 +1545,12 @@ void _dyld_for_each_objc_protocol(const char* protocolName, gAllImages.forEachObjCProtocol(protocolName, callback); } +void _dyld_register_driverkit_main(void (*mainFunc)()) +{ + log_apis("_dyld_register_driverkit_main()\n"); + gAllImages.setDriverkitMain(mainFunc); +} + #if !TARGET_OS_DRIVERKIT struct dyld_func { const char* name; @@ -1424,6 +1565,10 @@ static const struct dyld_func dyld_funcs[] = { {"__dyld_get_image_name", (void*)dyld3::_dyld_get_image_name }, {"__dyld_get_image_header", (void*)dyld3::_dyld_get_image_header }, {"__dyld_get_image_vmaddr_slide", (void*)dyld3::_dyld_get_image_vmaddr_slide }, +#if TARGET_OS_OSX + // support old licenseware plug ins on macOS + {"__dyld_lookup_and_bind", (void*)dyld3::_dyld_lookup_and_bind }, +#endif }; #endif diff --git a/dyld3/APIs.h b/dyld3/APIs.h index 59b7f28..c6ccca8 100644 --- a/dyld3/APIs.h +++ b/dyld3/APIs.h @@ -82,6 +82,8 @@ intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) TEMP_HIDDEN; const char* _dyld_get_image_name(uint32_t imageIndex) TEMP_HIDDEN; +const struct mach_header * _dyld_get_prog_image_header() TEMP_HIDDEN; + int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) TEMP_HIDDEN; int32_t NSVersionOfRunTimeLibrary(const char* libraryName) TEMP_HIDDEN; @@ -157,7 +159,9 @@ bool _dyld_shared_cache_optimized() TEMP_HIDDEN; bool _dyld_shared_cache_is_locally_built() TEMP_HIDDEN; -bool dyld_need_closure(const char* execPath, const char* tempDir) TEMP_HIDDEN; +uint32_t _dyld_launch_mode() TEMP_HIDDEN; + +bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir) TEMP_HIDDEN; void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]) TEMP_HIDDEN; @@ -193,8 +197,10 @@ void _dyld_for_each_objc_class(const char* className, void _dyld_for_each_objc_protocol(const char* protocolName, void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) TEMP_HIDDEN; +void _dyld_register_driverkit_main(void (*mainFunc)())TEMP_HIDDEN; + // only in macOS and deprecated -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) TEMP_HIDDEN; NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) TEMP_HIDDEN; bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN; diff --git a/dyld3/APIs_macOS.cpp b/dyld3/APIs_macOS.cpp index ad87d47..0c673b9 100644 --- a/dyld3/APIs_macOS.cpp +++ b/dyld3/APIs_macOS.cpp @@ -38,6 +38,10 @@ #include +#if __has_feature(ptrauth_calls) + #include +#endif + #include "dlfcn.h" #include "AllImages.h" @@ -55,7 +59,7 @@ void parseDlHandle(void* h, const MachOLoaded** mh, bool* don // only in macOS and deprecated -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // macOS needs to support an old API that only works with fileype==MH_BUNDLE. // In this deprecated API (unlike dlopen), loading and linking are separate steps. @@ -70,7 +74,7 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NS // verify path exists struct stat statbuf; - if ( ::stat(path, &statbuf) == -1 ) + if ( dyld3::stat(path, &statbuf) == -1 ) return NSObjectFileImageFailure; // create ofi that just contains path. NSLinkModule does all the work @@ -98,13 +102,13 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memIma bool usable = false; const MachOFile* mf = (MachOFile*)memImage; if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) { - usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype) != 0); + usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype, false) != 0); } else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) { uint64_t sliceOffset; uint64_t sliceLen; bool missingSlice; - if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), sliceOffset, sliceLen, missingSlice) ) { + if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), false, sliceOffset, sliceLen, missingSlice) ) { mf = (MachOFile*)((long)memImage+sliceOffset); if ( mf->isMachO(diag, sliceLen) ) { usable = true; @@ -112,7 +116,7 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memIma } } if ( usable ) { - if ( !mf->supportsPlatform(Platform::macOS) ) + if ( !mf->builtForPlatform(Platform::macOS) ) usable = false; } if ( !usable ) { @@ -343,10 +347,15 @@ const char* NSLibraryNameForModule(NSModule m) static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress) { + // allow flat lookup to find "_memcpy" even though it is not implemented as that name in any dylib + MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) { + return gAllImages.findDependent(mh, depIndex); + }; + __block bool result = false; gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) { bool resultPointsToInstructions = false; - if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) { + if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, finder, symbolAddress, &resultPointsToInstructions) ) { *foundInImageAtLoadAddress = loadedImage.loadedAddress(); stop = true; result = true; @@ -446,8 +455,35 @@ void* NSAddressOfSymbol(NSSymbol symbol) { log_apis("NSAddressOfSymbol(%p)\n", symbol); + if ( symbol == nullptr ) + return nullptr; + // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table - return (void*)symbol; + void *result = (void*)symbol; + +#if __has_feature(ptrauth_calls) + __block const MachOLoaded *module = nullptr; + gAllImages.infoForImageMappedAt(symbol, ^(const LoadedImage& foundImage, uint8_t permissions) { + module = foundImage.loadedAddress(); + }); + + int64_t slide = module->getSlide(); + __block bool resultPointsToInstructions = false; + module->forEachSection(^(const MachOAnalyzer::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; + } + }); + + if (resultPointsToInstructions) { + result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0); + } +#endif + + return result; } NSModule NSModuleForSymbol(NSSymbol symbol) diff --git a/dyld3/AllImages.cpp b/dyld3/AllImages.cpp index 8202c97..2c41594 100644 --- a/dyld3/AllImages.cpp +++ b/dyld3/AllImages.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include "Closure.h" #include "ClosureBuilder.h" #include "ClosureFileSystemPhysical.h" +#include "RootsChecker.h" #include "objc-shared-cache.h" @@ -59,12 +61,8 @@ extern "C" void __cxa_finalize_ranges(const __cxa_range_t ranges[], unsigned int extern "C" int __cxa_atexit(void (*func)(void *), void* arg, void* dso); -#ifdef DARLING -#define kdebug_is_enabled(...) 0 -#endif - -VIS_HIDDEN bool gUseDyld3 = false; +VIS_HIDDEN void* __ptrauth_dyld_address_auth gUseDyld3 = nullptr; namespace dyld3 { @@ -87,8 +85,7 @@ void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCach _dyldCachePath = dyldCachePath; 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; + _dyldCacheSlide = (uint64_t)dyldCacheLoadAddress - dyldCacheLoadAddress->unslidLoadAddress(); _imagesArrays.push_back(dyldCacheLoadAddress->cachedDylibsImageArray()); if ( auto others = dyldCacheLoadAddress->otherOSImageArray() ) _imagesArrays.push_back(others); @@ -116,11 +113,25 @@ void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCach _processDOFs = Loader::dtraceUserProbesEnabled(); } -void AllImages::setProgramVars(ProgramVars* vars) +void AllImages::setProgramVars(ProgramVars* vars, bool keysOff, bool osBinariesOnly) { _programVars = vars; - const dyld3::MachOFile* mf = (dyld3::MachOFile*)_programVars->mh; - _archs = &GradedArchs::forCurrentOS(mf); + _archs = &GradedArchs::forCurrentOS(keysOff, osBinariesOnly); +} + +void AllImages::setLaunchMode(uint32_t flags) +{ + _launchMode = flags; +} + +AllImages::MainFunc AllImages::getDriverkitMain() +{ + return _driverkitMain; +} + +void AllImages::setDriverkitMain(MainFunc mainFunc) +{ + _driverkitMain = mainFunc; } void AllImages::setRestrictions(bool allowAtPaths, bool allowEnvPaths) @@ -221,7 +232,7 @@ void AllImages::mirrorToOldAllImageInfos() // update UUID array if needed uint32_t nonCachedCount = 1; // always add dyld for (const LoadedImage& li : _loadedImages) { - if ( !li.loadedAddress()->inDyldCache() ) + if ( _oldAllImageInfos->processDetachedFromSharedRegion || !li.loadedAddress()->inDyldCache()) ++nonCachedCount; } if ( nonCachedCount != _oldAllImageInfos->uuidArrayCount ) { @@ -242,7 +253,7 @@ void AllImages::mirrorToOldAllImageInfos() dyldMF->getUuid(_oldUUIDArray[0].imageUUID); index = 1; for (const LoadedImage& li : _loadedImages) { - if ( !li.loadedAddress()->inDyldCache() ) { + if ( _oldAllImageInfos->processDetachedFromSharedRegion || !li.loadedAddress()->inDyldCache() ) { _oldUUIDArray[index].imageLoadAddress = li.loadedAddress(); li.loadedAddress()->getUuid(_oldUUIDArray[index].imageUUID); ++index; @@ -260,13 +271,6 @@ void AllImages::addImages(const Array& newImages) // 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; - } - } }); } @@ -315,6 +319,13 @@ void AllImages::runImageNotifiers(const Array& newImages) _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo); } + // if any image not in the shared cache added, recompute bounds + for (const LoadedImage& li : newImages) { + if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) { + recomputeBounds(); + break; + } + } // update immutable ranges for (const LoadedImage& li : newImages) { @@ -322,7 +333,7 @@ void AllImages::runImageNotifiers(const Array& newImages) uintptr_t baseAddr = (uintptr_t)li.loadedAddress(); li.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool &stop) { if ( (permissions & (VM_PROT_READ|VM_PROT_WRITE)) == VM_PROT_READ ) { - addImmutableRange(baseAddr + vmOffset, baseAddr + vmOffset + vmSize); + addImmutableRange(baseAddr + (uintptr_t)vmOffset, (uintptr_t)(baseAddr + vmOffset + vmSize)); } }); } @@ -352,7 +363,7 @@ void AllImages::runImageNotifiers(const Array& newImages) image->getUuid(uuid); fsid_t fsid = {{ 0, 0 }}; fsobj_id_t fsobjid = { 0, 0 }; - if ( !li.loadedAddress()->inDyldCache() && (stat(path, &stat_buf) == 0) ) { + if ( !li.loadedAddress()->inDyldCache() && (dyld3::stat(path, &stat_buf) == 0) ) { fsobjid = *(fsobj_id_t*)&stat_buf.st_ino; fsid = {{ stat_buf.st_dev, 0 }}; } @@ -469,7 +480,7 @@ void AllImages::removeImages(const Array& unloadImages) image->getUuid(uuid); fsid_t fsid = {{ 0, 0 }}; fsobj_id_t fsobjid = { 0, 0 }; - if ( stat(path, &stat_buf) == 0 ) { + if ( dyld3::stat(path, &stat_buf) == 0 ) { fsobjid = *(fsobj_id_t*)&stat_buf.st_ino; fsid = {{ stat_buf.st_dev, 0 }}; } @@ -922,6 +933,12 @@ void AllImages::breadthFirstRecurseDependents(Array& visited, if ( !findImageNum(depImageNum, depLi) ) return; handler(depLi, depStop); + // if there is an override of some dyld cache dylib, we need to store the override ImageNum in the visited set + if ( depImageNum != depLi.image()->imageNum() ) { + depImageNum = depLi.image()->imageNum(); + if ( visited.contains(depImageNum) ) + return; + } visited.push_back(depImageNum); if ( depStop ) { stopped = true; @@ -959,7 +976,7 @@ const MachOLoaded* AllImages::mainExecutable() const const closure::Image* AllImages::mainExecutableImage() const { assert(_mainClosure != nullptr); - return _mainClosure->images()->imageForNum(_mainClosure->topImage()); + return _mainClosure->images()->imageForNum(_mainClosure->topImageNum()); } void AllImages::setMainPath(const char* path ) @@ -969,7 +986,7 @@ void AllImages::setMainPath(const char* path ) const char* AllImages::imagePath(const closure::Image* image) const { -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE // on iOS and watchOS, apps may be moved on device after closure built if ( _mainExeOverridePath != nullptr ) { if ( image == mainExecutableImage() ) @@ -980,14 +997,7 @@ const char* AllImages::imagePath(const closure::Image* image) const } dyld_platform_t AllImages::platform() const { - if (oldAllImageInfo()->version >= 16) { return (dyld_platform_t)oldAllImageInfo()->platform; } - - __block dyld_platform_t result; - // FIXME: Remove this once we only care about version 16 or greater all image infos - dyld_get_image_versions(mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) { - result = platform; - }); - return result; + return (dyld_platform_t)oldAllImageInfo()->platform; } const GradedArchs& AllImages::archs() const @@ -1029,7 +1039,7 @@ void AllImages::decRefCount(const mach_header* loadAddress) } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX NSObjectFileImage AllImages::addNSObjectFileImage(const OFIInfo& image) { __block uint64_t imageNum = 0; @@ -1202,13 +1212,15 @@ void Reaper::finalizeDeadImages() void Reaper::runTerminators(const LoadedImage& li) { + // Don't run static terminator for arm64e + const MachOAnalyzer* ma = (MachOAnalyzer*)li.loadedAddress(); + if ( ma->isArch("arm64e") ) + return; + if ( li.image()->hasTerminators() ) { typedef void (*Terminator)(); li.image()->forEachTerminator(li.loadedAddress(), ^(const void* terminator) { Terminator termFunc = (Terminator)terminator; -#if __has_feature(ptrauth_calls) - termFunc = (Terminator)__builtin_ptrauth_sign_unauthenticated((void*)termFunc, 0, 0); -#endif termFunc(); log_initializers("dyld: called static terminator %p in %s\n", termFunc, li.image()->path()); }); @@ -1470,13 +1482,20 @@ void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify } } -void AllImages::applyInterposingToDyldCache(const closure::Closure* closure) +void AllImages::applyInterposingToDyldCache(const closure::Closure* closure, mach_port_t mach_task_self) { 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; + + if ( closure->findAttributePayload(closure::TypedBytes::Type::cacheOverrides) == nullptr ) + return; + + // make the cache writable for this block + DyldSharedCache::DataConstScopedWriter patcher(_dyldCacheAddress, mach_task_self, (DyldSharedCache::DataConstLogFunc)&log_segments); + closure->forEachPatchEntry(^(const closure::Closure::PatchEntry& entry) { if ( entry.overriddenDylibInCache != lastCachedDylibImageNum ) { lastCachedDylibImage = closure::ImageArray::findImage(imagesArrays(), entry.overriddenDylibInCache); @@ -1491,7 +1510,9 @@ void AllImages::applyInterposingToDyldCache(const closure::Closure* closure) LoadedImage foundImage; switch ( entry.replacement.image.kind ) { case closure::Image::ResolvedSymbolTarget::kindImage: - assert(findImageNum(entry.replacement.image.imageNum, foundImage)); + if ( !findImageNum(entry.replacement.image.imageNum, foundImage) ) { + abort_report_np("cannot find replacement imageNum=0x%04X when patching cache to override imageNum=0x%04X\n", entry.replacement.image.imageNum, entry.overriddenDylibInCache); + } newValue = (uintptr_t)(foundImage.loadedAddress()) + (uintptr_t)entry.replacement.image.offset; break; case closure::Image::ResolvedSymbolTarget::kindSharedCache: @@ -1664,9 +1685,14 @@ void AllImages::runAllInitializersInImage(const closure::Image* image, const Mac }); } -const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI, const void* callerAddress) +// Note this is noinline to avoid having too much stack used if loadImage has to call due to an invalid closure +__attribute__((noinline)) +const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, + bool rtldNoDelete, bool rtldNow, bool fromOFI, const void* callerAddress, + bool canUsePrebuiltSharedCacheClosure) { bool sharedCacheFormatCompatible = (_dyldCacheAddress != nullptr) && (_dyldCacheAddress->header.formatVersion == dyld3::closure::kFormatVersion); + canUsePrebuiltSharedCacheClosure &= sharedCacheFormatCompatible; // quick check if path is in shared cache and already loaded if ( _dyldCacheAddress != nullptr ) { @@ -1690,7 +1716,7 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r sharedCacheFormatCompatible ) { const dyld3::closure::ImageArray* images = _dyldCacheAddress->cachedDylibsImageArray(); const dyld3::closure::Image* image = images->imageForNum(dyldCacheImageIndex+1); - return loadImage(diag, image->imageNum(), nullptr, rtldLocal, rtldNoDelete, rtldNow, fromOFI); + return loadImage(diag, path, image->imageNum(), nullptr, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress); } } } @@ -1712,10 +1738,11 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r // Then try again with forcing a new closure for (bool canUseSharedCacheClosure : { true, false }) { // We can only use a shared cache closure if the shared cache format is the same as libdyld. - canUseSharedCacheClosure &= sharedCacheFormatCompatible; + canUseSharedCacheClosure &= canUsePrebuiltSharedCacheClosure; closure::FileSystemPhysical fileSystem(nullptr, nullptr, _allowEnvPaths); + RootsChecker rootsChecker; closure::ClosureBuilder::AtPath atPathHanding = (_allowAtPaths ? closure::ClosureBuilder::AtPath::all : closure::ClosureBuilder::AtPath::onlyInRPaths); - closure::ClosureBuilder cb(_nextImageNum, fileSystem, _dyldCacheAddress, true, *_archs, closure::gPathOverrides, atPathHanding); + closure::ClosureBuilder cb(_nextImageNum, fileSystem, rootsChecker, _dyldCacheAddress, true, *_archs, closure::gPathOverrides, atPathHanding, true, nullptr, (dyld3::Platform)platform()); newClosure = cb.makeDlopenClosure(path, _mainClosure, _loadedImages.array(), callerImageNum, rtldNoLoad, rtldNow, canUseSharedCacheClosure, &topImageNum); if ( newClosure == closure::ClosureBuilder::sRetryDlopenClosure ) { log_apis(" dlopen: closure builder needs to retry: %s\n", path); @@ -1739,7 +1766,7 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r if ( const closure::ImageArray* newArray = newClosure->images() ) { appendToImagesArray(newArray); } - log_apis(" dlopen: made closure: %p\n", newClosure); + log_apis(" dlopen: made %s closure: %p\n", newClosure->topImage()->variantString(), newClosure); } // if already loaded, just bump refCount and return @@ -1775,14 +1802,16 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r } } - return loadImage(diag, topImageNum, newClosure, rtldLocal, rtldNoDelete, rtldNow, fromOFI); + return loadImage(diag, path, topImageNum, newClosure, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress); } // Note this is noinline to avoid having too much stack used in the parent // dlopen method __attribute__((noinline)) -const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure, - bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI) { +const MachOLoaded* AllImages::loadImage(Diagnostics& diag, const char* path, + closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure, + bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI, + const void* callerAddress) { // Note this array is used as the storage to Loader so needs to be at least // large enough to handle whatever total number of images we need to do the dlopen STACK_ALLOC_OVERFLOW_SAFE_ARRAY(LoadedImage, newImages, 1024); @@ -1793,9 +1822,10 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top dyld3::Array selectorImages; // run loader to load all new images + RootsChecker rootsChecker; Loader loader(_loadedImages.array(), newImages, _dyldCacheAddress, imagesArrays(), - selectorOpt, selectorImages, - &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs); + selectorOpt, selectorImages, rootsChecker, (dyld3::Platform)platform(), + &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs, !rtldNow); // find Image* for top image, look in new closure first const closure::Image* topImage = nullptr; @@ -1805,9 +1835,9 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top 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); + log_apis(" dlopen: using pre-built %s dlopen closure from dyld shared cache %p\n", topImage->variantString(), topImage); else - log_apis(" dlopen: using pre-built dlopen closure %p\n", topImage); + log_apis(" dlopen: using pre-built %s dlopen closure %p\n", topImage->variantString(), topImage); } LoadedImage topLoadedImage = LoadedImage::make(topImage); if ( rtldLocal && !topImage->inDyldCache() ) @@ -1822,9 +1852,19 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top loader.completeAllDependents(diag, someCacheImageOverridden); if ( diag.hasError() ) return nullptr; - loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI); - if ( diag.hasError() ) + bool closureOutOfDate; + bool recoverable; + loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI, &closureOutOfDate, &recoverable); + if ( diag.hasError() ) { + // If we used a pre-built shared cache closure, and now found that it was out of date, + // try again and rebuild a new closure + // Note, newClosure is null in the case where we used a prebuilt closure + if ( closureOutOfDate && recoverable && (newClosure == nullptr) ) { + diag.clearError(); + return dlopen(diag, path, false /* rtldNoLoad */, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress, false); + } return nullptr; + } // Record if we had a root _someImageOverridden |= someCacheImageOverridden; @@ -1845,7 +1885,7 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top // if closure adds images that override dyld cache, patch cache if ( newClosure != nullptr ) - applyInterposingToDyldCache(newClosure); + applyInterposingToDyldCache(newClosure, mach_task_self()); runImageCallbacks(newImages); diff --git a/dyld3/AllImages.h b/dyld3/AllImages.h index dc277ea..f50fe70 100644 --- a/dyld3/AllImages.h +++ b/dyld3/AllImages.h @@ -30,13 +30,16 @@ #include #include +#include + #include "Closure.h" #include "Loading.h" #include "MachOLoaded.h" #include "DyldSharedCache.h" +#include "PointerAuth.h" -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // only in macOS and deprecated struct VIS_HIDDEN OFIInfo { @@ -56,6 +59,7 @@ public: typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide); typedef void (*LoadNotifyFunc)(const mach_header* mh, const char* path, bool unloadable); typedef void (*BulkLoadNotifier)(unsigned count, const mach_header* mhs[], const char* paths[]); + typedef void (*MainFunc)(void); void init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath, const Array& initialImages); @@ -63,13 +67,14 @@ public: void setHasCacheOverrides(bool someCacheImageOverriden); bool hasCacheOverrides() const; void setMainPath(const char* path); + void setLaunchMode(uint32_t flags); void applyInitialImages(); void addImages(const Array& newImages); void removeImages(const Array& unloadImages); void runImageNotifiers(const Array& newImages); void runImageCallbacks(const Array& newImages); - void applyInterposingToDyldCache(const closure::Closure* closure); + void applyInterposingToDyldCache(const closure::Closure* closure, mach_port_t mach_task_self); void runStartupInitialzers(); void runInitialzersBottomUp(const closure::Image* topImage); void runLibSystemInitializer(LoadedImage& libSystem); @@ -99,7 +104,8 @@ public: const char* imagePath(const closure::Image*) const; dyld_platform_t platform() const; const GradedArchs& archs() const; - + uint32_t launchMode() const { return _launchMode; } + const Array& imagesArrays(); void incRefCount(const mach_header* loadAddress); @@ -119,7 +125,7 @@ public: void notifyMonitorLoads(const Array& newImages); void notifyMonitorUnloads(const Array& unloadingImages); -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX NSObjectFileImage addNSObjectFileImage(const OFIInfo&); void removeNSObjectFileImage(NSObjectFileImage); bool forNSObjectFileImage(NSObjectFileImage imageHandle, @@ -131,8 +137,13 @@ public: void (^callback)(void* classPtr, bool isLoaded, bool* stop)) const; void forEachObjCProtocol(const char* protocolName, void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) const; + + MainFunc getDriverkitMain(); + void setDriverkitMain(MainFunc mainFunc); - const MachOLoaded* dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool forceBindLazies, bool fromOFI, const void* callerAddress); + const MachOLoaded* dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, + bool forceBindLazies, bool fromOFI, const void* callerAddress, + bool canUsePrebuiltSharedCacheClosure = true); struct ProgramVars { @@ -142,7 +153,7 @@ public: const char*** environPtr; const char** __prognamePtr; }; - void setProgramVars(ProgramVars* vars); + void setProgramVars(ProgramVars* vars, bool keysOff, bool platformBinariesOnly); // Note these are to be used exclusively by forking void takeLockBeforeFork(); @@ -178,8 +189,10 @@ private: } array[2]; // programs with only main-exe and dyld cache fit in here }; - const MachOLoaded* loadImage(Diagnostics& diag, closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure, - bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI); + const MachOLoaded* loadImage(Diagnostics& diag, const char* path, + closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure, + bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI, + const void* callerAddress); typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars); @@ -203,17 +216,13 @@ private: uintptr_t resolveTarget(closure::Image::ResolvedSymbolTarget target) const; void addImmutableRange(uintptr_t start, uintptr_t end); - 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& images); - static void runAllStaticTerminatorsHelper(void*); typedef closure::ImageArray ImageArray; - const closure::LaunchClosure* _mainClosure = nullptr; + typedef const closure::LaunchClosure* __ptrauth_dyld_address_auth MainClosurePtrType; + + MainClosurePtrType _mainClosure = nullptr; const DyldSharedCache* _dyldCacheAddress = nullptr; const char* _dyldCachePath = nullptr; uint64_t _dyldCacheSlide = 0; @@ -236,8 +245,11 @@ private: bool _allowAtPaths = false; bool _allowEnvPaths = false; bool _someImageOverridden = false; + uint32_t _launchMode = 0; uintptr_t _lowestNonCached = 0; uintptr_t _highestNonCached = UINTPTR_MAX; + + MainFunc _driverkitMain = nullptr; #ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT mutable os_unfair_recursive_lock _globalLock = OS_UNFAIR_RECURSIVE_LOCK_INIT; #else @@ -250,9 +262,9 @@ private: GrowableArray _loadBulkNotifiers; GrowableArray _dlopenRefCounts; GrowableArray _loadedImages; -#if __MAC_OS_X_VERSION_MIN_REQUIRED - uint64_t _nextObjectFileImageNum = 0; - GrowableArray _objectFileImages; +#if TARGET_OS_OSX + uint64_t _nextObjectFileImageNum = 0; + GrowableArray _objectFileImages; #endif // ObjC selectors diff --git a/dyld3/Array.h b/dyld3/Array.h index e0cbd0c..3e2baff 100644 --- a/dyld3/Array.h +++ b/dyld3/Array.h @@ -26,9 +26,10 @@ #include #include +#include #include #include -#include +#include #if !TARGET_OS_DRIVERKIT && (BUILDING_LIBDYLD || BUILDING_DYLD) #include @@ -160,13 +161,13 @@ inline void OverflowSafeArray::growTo(uintptr_t n) #if BUILDING_LIBDYLD //FIXME We should figure out a way to do this in dyld char crashString[256]; - snprintf(crashString, 256, "OverflowSafeArray failed to allocate %lu bytes, vm_allocate returned: %d\n", - _overflowBufferSize, kr); + snprintf(crashString, 256, "OverflowSafeArray failed to allocate %llu bytes, vm_allocate returned: %d\n", + (uint64_t)_overflowBufferSize, kr); CRSetCrashLogMessage(crashString); #endif assert(0); } - ::memcpy((void*)_overflowBuffer, this->_elements, this->_usedCount*sizeof(T)); + ::memcpy((void*)_overflowBuffer, (void*)this->_elements, this->_usedCount*sizeof(T)); this->_elements = (T*)_overflowBuffer; this->_allocCount = _overflowBufferSize / sizeof(T); diff --git a/dyld3/BootArgs.cpp b/dyld3/BootArgs.cpp index 35fff96..ecaa89a 100644 --- a/dyld3/BootArgs.cpp +++ b/dyld3/BootArgs.cpp @@ -1,9 +1,25 @@ -// -// BootArgs.cpp -// dyld -// -// Created by Louis Gerbarg on 11/14/18. -// +/* +* 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 #include @@ -12,53 +28,36 @@ #include "Loading.h" // For internalInstall() #include "BootArgs.h" -namespace dyld3 { -/* -* Checks to see if there are any args that impact dyld. These args -* can be set sevaral ways. These will only be honored on development -* and Apple Internal builds. -*/ -bool BootArgs::contains(const char* arg) -{ - //FIXME: Use strnstr(). Unfortunately we are missing an imp in libc.a -#if TARGET_OS_SIMULATOR - return false; -#else - // don't check for boot-args on customer installs - if ( !internalInstall() ) - return false; - - // get length of full boot-args string - size_t len; - if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 ) - return false; - - // get copy of boot-args string - char bootArgsBuffer[len]; - if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 ) - return false; - - // return true if 'arg' is a sub-string of boot-args - return (strstr(bootArgsBuffer, arg) != nullptr); +namespace dyld { +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + bool isTranslated(); #endif } +namespace dyld3 { + uint64_t BootArgs::_flags = 0; bool BootArgs::forceCustomerCache() { return (_flags & kForceCustomerCacheMask); } +bool BootArgs::forceDevelopmentCache() { + return (_flags & kForceDevelopmentCacheMask); +} + bool BootArgs::forceDyld2() { +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if (dyld::isTranslated()) { return true; } +#endif // If both force dyld2 and dyld3 are set then use dyld3 if (_flags & kForceDyld3CacheMask) { return false; } if (_flags & kForceDyld2CacheMask) { return true; } - if (contains("force_dyld2=1")) { return true; } return false; } bool BootArgs::forceDyld3() { - return ((_flags & kForceDyld3CacheMask) || contains("force_dyld3=1")); + return (_flags & kForceDyld3CacheMask); } bool BootArgs::enableDyldTestMode() { @@ -69,6 +68,14 @@ bool BootArgs::enableCompactImageInfo() { return (_flags & kEnableCompactImageInfoMask); } +bool BootArgs::forceReadOnlyDataConst() { + return (_flags & kForceReadOnlyDataConstMask); +} + +bool BootArgs::forceReadWriteDataConst() { + return (_flags & kForceReadWriteDataConstMask); +} + void BootArgs::setFlags(uint64_t flags) { #if TARGET_IPHONE_SIMULATOR return; diff --git a/dyld3/BootArgs.h b/dyld3/BootArgs.h index 19278de..df4f04f 100644 --- a/dyld3/BootArgs.h +++ b/dyld3/BootArgs.h @@ -1,9 +1,26 @@ -// -// BootArgs.hpp -// dyld -// -// Created by Louis Gerbarg on 11/14/18. -// +/* +* 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_BOOTARGS_H__ #define __DYLD_BOOTARGS_H__ @@ -15,19 +32,24 @@ namespace dyld3 { #if BUILDING_DYLD struct VIS_HIDDEN BootArgs { - static bool contains(const char* arg); static bool forceCustomerCache(); + static bool forceDevelopmentCache(); static bool forceDyld2(); static bool forceDyld3(); static bool enableDyldTestMode(); static bool enableCompactImageInfo(); + static bool forceReadOnlyDataConst(); + static bool forceReadWriteDataConst(); static void setFlags(uint64_t flags); private: static const uint64_t kForceCustomerCacheMask = 1<<0; static const uint64_t kDyldTestModeMask = 1<<1; + static const uint64_t kForceDevelopmentCacheMask = 1<<2; static const uint64_t kForceDyld2CacheMask = 1<<15; static const uint64_t kForceDyld3CacheMask = 1<<16; static const uint64_t kEnableCompactImageInfoMask = 1<<17; + static const uint64_t kForceReadOnlyDataConstMask = 1<<18; + static const uint64_t kForceReadWriteDataConstMask = 1<<19; //FIXME: Move this into __DATA_CONST once it is enabled for dyld static uint64_t _flags; }; diff --git a/dyld3/Closure.cpp b/dyld3/Closure.cpp index 9d24a62..5b95550 100644 --- a/dyld3/Closure.cpp +++ b/dyld3/Closure.cpp @@ -31,6 +31,7 @@ #include #include #include +#include <_simple.h> extern "C" { #include @@ -142,7 +143,7 @@ bool Image::representsImageNum(ImageNum num) const return true; if ( !flags.isDylib ) return false; - if ( flags.inDyldCache ) + if ( !flags.hasOverrideImageNum ) return false; ImageNum cacheImageNum; if ( isOverrideOfDyldCacheImage(cacheImageNum) ) @@ -573,6 +574,16 @@ bool Image::hasPrecomputedObjC() const return getFlags().hasPrecomputedObjC; } +bool Image::fixupsNotEncoded() const +{ + return getFlags().fixupsNotEncoded; +} + +bool Image::rebasesNotEncoded() const +{ + return getFlags().rebasesNotEncoded; +} + void Image::forEachTerminator(const void* imageLoadAddress, void (^handler)(const void* terminator)) const { uint32_t size; @@ -643,17 +654,10 @@ void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop 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; - } + stop = this->forEachBind(bind); + if ( stop ) + return; + if (hasChainedFixups()) chainedFixups(chainedStartsOffset(), chainedTargets(), stop); @@ -729,6 +733,24 @@ void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop } } +bool Image::forEachBind(void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const +{ + const uint32_t pointerSize = is64() ? 8 : 4; + bool stop = false; + 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; + } + return stop; +} + void Image::forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop), void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const { @@ -888,6 +910,11 @@ void Image::forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& } } +const char* Image::variantString() const +{ + return (this->fixupsNotEncoded() ? "minimal" : "full"); +} + //////////////////////////// ImageArray //////////////////////////////////////// size_t ImageArray::size() const @@ -954,7 +981,11 @@ const Image* ImageArray::imageForNum(ImageNum num) const const Image* ImageArray::findImage(const Array imagesArrays, ImageNum imageNum) { - for (const ImageArray* ia : imagesArrays) { + // Search image arrays backwards as the main closure, or dlopen closures, may rebuild closures + // for shared cache images which are not roots, but whose initialisers must rebuild as they depend + // on a root + for (uintptr_t index = imagesArrays.count(); index > 0; --index) { + const ImageArray* ia = imagesArrays[index - 1]; if ( const Image* result = ia->imageForNum(imageNum) ) return result; } @@ -986,7 +1017,7 @@ const ImageArray* Closure::images() const return (ImageArray*)result; } -ImageNum Closure::topImage() const +ImageNum Closure::topImageNum() const { uint32_t size; const ImageNum* top = (ImageNum*)findAttributePayload(Type::topImage, &size); @@ -995,6 +1026,13 @@ ImageNum Closure::topImage() const return *top; } +const Image* Closure::topImage() const +{ + ImageNum imageNum = this->topImageNum(); + const dyld3::closure::ImageArray* closureImages = this->images(); + return closureImages->imageForNum(imageNum); +} + void Closure::forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const { forEachAttributePayload(Type::cacheOverrides, ^(const void* payload, uint32_t size, bool& stop) { @@ -1021,6 +1059,7 @@ void Closure::deallocate() const ::vm_deallocate(mach_task_self(), (long)this, size()); } + //////////////////////////// LaunchClosure //////////////////////////////////////// void LaunchClosure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const @@ -1074,12 +1113,6 @@ bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID) const 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) { @@ -1155,6 +1188,23 @@ bool LaunchClosure::hasInsertedLibraries() const return getFlags().hasInsertedLibraries; } +bool LaunchClosure::hasProgramVars(uint32_t& runtimeOffset) const +{ + if ( !getFlags().hasProgVars ) + return false; + uint32_t payloadSize = 0; + const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::progVars, &payloadSize); + if (buffer == nullptr) + return false; + runtimeOffset = *((uint32_t*)buffer); + return true; +} + +bool LaunchClosure::usedInterposing() const +{ + return getFlags().usedInterposing; +} + bool LaunchClosure::hasInterposings() const { __block bool result = false; @@ -1252,86 +1302,69 @@ void LaunchClosure::duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& dup duplicateClassesHashTable = (const ObjCClassDuplicatesOpt*)buffer; } -static void putHexNibble(uint8_t value, char*& p) -{ - if ( value < 10 ) - *p++ = '0' + value; - else - *p++ = 'A' + value - 10; -} -static void putHexByte(uint8_t value, char*& p) +static bool getContainerLibraryCachesDir(const char* envp[], char libCacheDir[]) { - value &= 0xFF; - putHexNibble(value >> 4, p); - putHexNibble(value & 0x0F, p); -} - -static bool hashBootAndFileInfo(const char* mainExecutablePath, char hashString[32]) -{ - struct stat statbuf; - if ( ::stat(mainExecutablePath, &statbuf) != 0) + // $HOME is root of writable data container + const char* homeDir = _simple_getenv(envp, "HOME"); + if ( homeDir == nullptr ) return false; -#if !DARLING && !TARGET_OS_DRIVERKIT // Temp until core crypto is available - const struct ccdigest_info* di = ccsha256_di(); - ccdigest_di_decl(di, hashTemp); // defines hashTemp array in stack - ccdigest_init(di, hashTemp); - // put boot time into hash - const uint64_t* bootTime = ((uint64_t*)_COMM_PAGE_BOOTTIME_USEC); - ccdigest_update(di, hashTemp, sizeof(uint64_t), bootTime); - - // put inode of executable into hash - ccdigest_update(di, hashTemp, sizeof(statbuf.st_ino), &statbuf.st_ino); - - // put mod-time of executable into hash - ccdigest_update(di, hashTemp, sizeof(statbuf.st_mtime), &statbuf.st_mtime); - - // complete hash computation and append as hex string - uint8_t hashBits[32]; - ccdigest_final(di, hashTemp, hashBits); - char* s = hashString; - for (size_t i=0; i < sizeof(hashBits); ++i) - putHexByte(hashBits[i], s); - *s = '\0'; + // Use realpath to block malicious values like HOME=/tmp/../usr/bin + char realHomePath[PATH_MAX]; + if ( realpath(homeDir, realHomePath) != nullptr ) + homeDir = realHomePath; +#if TARGET_OS_OSX + // iOS apps on Apple Silicon macOS have a different data container location + if ( strstr(homeDir, "/Library/Containers/") == nullptr ) + return false; +#else + // dyld3 should only save closures to disk for containerized apps + if ( strncmp(homeDir, "/private/var/mobile/Containers/Data/", 36) != 0 ) + return false; #endif + + // return $HOME/Library/Caches/ + strlcpy(libCacheDir, homeDir, PATH_MAX); + strlcat(libCacheDir, "/Library/Caches", PATH_MAX); return true; } -bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir, - bool makeDirsIfMissing) +bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath, const char* envp[], + bool makeDirsIfMissing, char closurePath[]) { - // dyld3 should only save closures to disk for containerized apps - if ( strstr(tempDir, "/Containers/Data/") == nullptr ) + // get path to data container's Library/Caches/ dir + if ( !getContainerLibraryCachesDir(envp, closurePath) ) return false; - strlcpy(closurePath, tempDir, PATH_MAX); - strlcat(closurePath, "/com.apple.dyld/", PATH_MAX); - - // make sure dyld sub-dir exists + // make sure XXX/Library/Caches/ exists + struct stat statbuf; + if ( dyld3::stat(closurePath, &statbuf) != 0 ) + return false; + + // add dyld sub-dir + strlcat(closurePath, "/com.apple.dyld", PATH_MAX); if ( makeDirsIfMissing ) { - struct stat statbuf; - if ( ::stat(closurePath, &statbuf) != 0 ) { + if ( dyld3::stat(closurePath, &statbuf) != 0 ) { if ( ::mkdir(closurePath, S_IRWXU) != 0 ) return false; } } - + + // add + ".closure" const char* leafName = strrchr(mainExecutablePath, '/'); if ( leafName == nullptr ) leafName = mainExecutablePath; else ++leafName; + strlcat(closurePath, "/", PATH_MAX); strlcat(closurePath, leafName, PATH_MAX); - strlcat(closurePath, "-", PATH_MAX); - - if ( !hashBootAndFileInfo(mainExecutablePath, &closurePath[strlen(closurePath)]) ) - return false; strlcat(closurePath, ".closure", PATH_MAX); return true; } + //////////////////////////// ObjCStringTable //////////////////////////////////////// uint32_t ObjCStringTable::hash(const char *key, size_t keylen) const diff --git a/dyld3/Closure.h b/dyld3/Closure.h index 4b4eb39..628208b 100644 --- a/dyld3/Closure.h +++ b/dyld3/Closure.h @@ -110,7 +110,6 @@ struct VIS_HIDDEN TypedBytes 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) @@ -119,8 +118,9 @@ struct VIS_HIDDEN TypedBytes selectorTable = 45, // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size classTable = 46, // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size warning = 47, // len = uint32_t + length path + 1, use one entry per warning - duplicateClassesTable = 48, // duplicateClassesHashTable - }; + duplicateClassesTable = 48, // duplicateClassesHashTable + progVars = 49, // sizeof(uint32_t) + }; Type type : 8; uint32_t payloadLength : 24; @@ -165,6 +165,8 @@ struct VIS_HIDDEN Image : ContainerTypedBytes bool hasObjC() const; bool hasInitializers() const; bool hasPrecomputedObjC() const; + bool fixupsNotEncoded() const; // minimal closure, dyld must parse and apply fixups + bool rebasesNotEncoded() const; bool hasTerminators() const; bool hasReadOnlyData() const; bool hasChainedFixups() const; @@ -176,7 +178,6 @@ struct VIS_HIDDEN Image : ContainerTypedBytes bool is64() const; bool neverUnload() const; bool cwdMustBeThisDir() const; - bool isPlatformBinary() const; bool overridableDylib() const; bool hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const; void forEachCDHash(void (^handler)(const uint8_t cdHash[20], bool& stop)) const; @@ -192,6 +193,7 @@ struct VIS_HIDDEN Image : ContainerTypedBytes bool hasPathWithHash(const char* path, uint32_t hash) const; bool isOverrideOfDyldCacheImage(ImageNum& cacheImageNum) const; uint64_t textSize() const; + const char* variantString() const; // "minimal" or "full" closure union ResolvedSymbolTarget { @@ -227,7 +229,7 @@ struct VIS_HIDDEN Image : ContainerTypedBytes return (raw != rhs.raw); } }; - static_assert(sizeof(ResolvedSymbolTarget) == 8); + static_assert(sizeof(ResolvedSymbolTarget) == 8, "Invalid size"); // ObjC optimisations @@ -359,6 +361,8 @@ struct VIS_HIDDEN Image : ContainerTypedBytes void forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop), void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const; + bool forEachBind(void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const; + static_assert(sizeof(ResolvedSymbolTarget) == 8, "Overflow in size of SymbolTargetLocation"); static uint32_t hashFunction(const char*); @@ -369,7 +373,9 @@ private: friend class ClosureBuilder; friend class ClosureWriter; friend class LaunchClosureWriter; - + friend class RebasePatternBuilder; + friend class BindPatternBuilder; + uint32_t pageSize() const; struct Flags @@ -395,7 +401,10 @@ private: hasReadOnlyData : 1, hasChainedFixups : 1, hasPrecomputedObjC : 1, - padding : 17; + fixupsNotEncoded : 1, + rebasesNotEncoded : 1, + hasOverrideImageNum : 1, + padding : 14; }; static_assert(sizeof(Flags) == sizeof(uint64_t), "Flags overflow"); @@ -656,7 +665,8 @@ struct VIS_HIDDEN Closure : public ContainerTypedBytes { size_t size() const; const ImageArray* images() const; - ImageNum topImage() const; + ImageNum topImageNum() const; + const Image* topImage() const; void deallocate() const; friend class ClosureWriter; @@ -697,7 +707,6 @@ 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 forEachSkipIfExistsFile(void (^handler)(const SkippedFile& file, bool& stop)) const; void forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const; @@ -709,6 +718,7 @@ struct VIS_HIDDEN LaunchClosure : public Closure void forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const; bool usedAtPaths() const; bool usedFallbackPaths() const; + bool usedInterposing() const; bool selectorHashTable(Array& imageNums, const ObjCSelectorOpt*& hashTable) const; bool classAndProtocolHashTables(Array& imageNums, @@ -717,9 +727,10 @@ struct VIS_HIDDEN LaunchClosure : public Closure void duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& duplicateClassesHashTable) const; bool hasInsertedLibraries() const; bool hasInterposings() const; + bool hasProgramVars(uint32_t& runtimeOffset) const; - static bool buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir, - bool makeDirsIfMissing); + static bool buildClosureCachePath(const char* mainExecutablePath, const char* envp[], + bool makeDirsIfMissing, char closurePath[]); private: friend class LaunchClosureWriter; @@ -730,7 +741,9 @@ private: usedFallbackPaths : 1, initImageCount : 16, hasInsertedLibraries : 1, - padding : 13; + hasProgVars : 1, + usedInterposing : 1, + padding : 11; }; const Flags& getFlags() const; }; diff --git a/dyld3/ClosureBuilder.cpp b/dyld3/ClosureBuilder.cpp index daef752..64d590c 100644 --- a/dyld3/ClosureBuilder.cpp +++ b/dyld3/ClosureBuilder.cpp @@ -39,28 +39,33 @@ #include "ClosureWriter.h" #include "ClosureBuilder.h" #include "MachOAnalyzer.h" +#include "MachOAnalyzerSet.h" #include "libdyldEntryVector.h" +#include "RootsChecker.h" #include "Tracing.h" #define CLOSURE_SELOPT_WRITE #include "objc-shared-cache.h" +#if BUILDING_DYLD +namespace dyld { void log(const char*, ...); } +#endif + namespace dyld3 { namespace closure { const DlopenClosure* ClosureBuilder::sRetryDlopenClosure = (const DlopenClosure*)(-1); -ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive, +ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const RootsChecker& rootsChecker, + const DyldSharedCache* dyldCache, bool dyldCacheIsLive, const GradedArchs& archs, const PathOverrides& pathOverrides, AtPath atPathHandling, bool allowRelativePaths, - LaunchErrorInfo* errorInfo, Platform platform, const CacheDylibsBindingHandlers* handlers) - : _fileSystem(fileSystem), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archs(archs), _platform(platform), _startImageNum(startImageNum), - _handlers(handlers), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive), _allowRelativePaths(allowRelativePaths) + LaunchErrorInfo* errorInfo, Platform platform, DylibFixupHandler handler) +: _fileSystem(fileSystem), _rootsChecker(rootsChecker), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archs(archs), _platform(platform), _startImageNum(startImageNum), + _dylibFixupHandler(handler), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive), _allowRelativePaths(allowRelativePaths) { if ( dyldCache != nullptr ) { _dyldImageArray = dyldCache->cachedDylibsImageArray(); - if ( (dyldCache->header.otherImageArrayAddr != 0) && (dyldCache->header.progClosuresSize == 0) ) - _makingClosuresInCache = true; } } @@ -74,6 +79,11 @@ ClosureBuilder::~ClosureBuilder() { PathPool::deallocate(_objcDuplicateClassWarnings); } +static bool iOSSupport(const char* path) +{ + return ( strncmp(path, "/System/iOSSupport/", 19) == 0 ); +} + bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, LinkageType linkageType, uint32_t compatVersion, bool canUseSharedCacheClosure) { @@ -90,6 +100,13 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for dylibsExpectedOnDisk = _dyldCache->header.dylibsExpectedOnDisk; } + // when building dyld cache for macOS, if requesting dylib is iOSMac unzippered twin, tell pathOverrides object to look in /System/iOSSupport first + dyld3::Platform targetPlatform = _platform; + if ( _makingDyldCacheImages && (_platform == dyld3::Platform::macOS) ) { + if ( forImageChain.image.loadAddress()->builtForPlatform(Platform::iOSMac, true) ) + targetPlatform = Platform::iOSMac; + } + _pathOverrides.forEachPathVariant(loadPath, pathIsInDyldCacheWhichCannotBeOverridden, ^(const char* possibleVariantPath, bool isFallbackPath, bool& stopPathVariant) { // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH @@ -143,6 +160,12 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for if ( _fileSystem.fileExists(possiblePath, &fileFoundINode, &fileFoundMTime, nullptr, &inodesMatchRuntime) ) { fileFound = true; for (BuilderLoadedImage& li: _loadedImages) { + if ( (li.loadedFileInfo.inode == 0) && (li.loadedFileInfo.mtime == 0) ) { + // Some already loaded image does not have an inode/mtime recorded, fix that if we can + if ( dylibsExpectedOnDisk || !li.loadAddress()->inDyldCache() ) { + _fileSystem.fileExists(li.path(), &li.loadedFileInfo.inode, &li.loadedFileInfo.mtime , nullptr, nullptr); + } + } if ( (li.loadedFileInfo.inode == fileFoundINode) && (li.loadedFileInfo.mtime == fileFoundMTime) ) { foundImage = &li; result = true; @@ -153,6 +176,23 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for } } + // We record the realpath of the file in the loaded images, but we might be loading via a symlink path. + // We need to search using the realpath just in case the dylib the symlink points to was overwritten while + // the process is running + if ( fileFound ) { + char realPath[MAXPATHLEN]; + if ( _fileSystem.getRealPath(possiblePath, realPath) ) { + for (BuilderLoadedImage& li: _loadedImages) { + if ( strcmp(li.path(), realPath) == 0 ) { + foundImage = &li; + result = true; + stop = true; + return; + } + } + } + } + bool unmapWhenDone = false; bool contentRebased = false; bool hasInits = false; @@ -198,26 +238,46 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for } } } - // 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 found in the cache, but not on-disk, this may be an already loaded image, but we are opening the alias. + // For example, we are trying to open .../AppKit but we already have a loaded root of .../Versions/C/AppKit + // This doesn't work with the calls to realpath when the symlinks themselves were removed from disk. + if ( foundInCache && !fileFound ) { + ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1; + for (BuilderLoadedImage& li: _loadedImages) { + if ( (li.overrideImageNum == dyldCacheImageNum) || (li.imageNum == dyldCacheImageNum) ) { + foundImage = &li; + result = true; + stop = true; + return; + } + } + } + + // if not found in cache, may be a symlink to something in cache + // We have to do this check even if the symlink target is not on disk as we may + // have symlinks in iOSMac dlopen paths which are resolved to a dylib removed from disk + if ( !foundInCache && (mh == nullptr) ) { + if ( _fileSystem.getRealPath(possiblePath, realPath) ) { + foundInCache = _dyldCache->hasImagePath(realPath, dyldCacheImageIndex); + if ( foundInCache ) { + ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1; + const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum); + filePath = image->path(); #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; - } + // handle case where OS dylib was updated after this process launched + if ( foundInCache ) { + for (BuilderLoadedImage& li: _loadedImages) { + if ( strcmp(li.path(), filePath) == 0 ) { + foundImage = &li; + result = true; + stop = true; + return; } } -#endif } +#endif } } } @@ -227,25 +287,32 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1; bool useCache = true; markNeverUnload = true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime + bool ignoreCacheDylib = false; const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum); if ( image->overridableDylib() ) { if ( fileFound ) { - 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 = ( (fileFoundINode == expectedInode) && (fileFoundMTime == expectedModTime) ); - } - else if ( _makingClosuresInCache ) { + if ( _makingClosuresInCache ) { // during iOS cache build, don't look at files on disk, use ones in cache useCache = true; - } - else { + } else if ( !_rootsChecker.onDiskFileIsRoot(filePath, _dyldCache, image, + &_fileSystem, fileFoundINode, fileFoundMTime) ) { + // file exists, but is not a root + useCache = true; + } else { // iOS internal build. Any disk on cache overrides cache useCache = false; } } - if ( !useCache ) { + if ( useCache && ((targetPlatform == Platform::iOSMac) || (targetPlatform == Platform::macOS)) ) { + // check this cached dylib is suitable for catalyst or mac program + mh = (MachOAnalyzer*)_dyldCache->getIndexedImageEntry(dyldCacheImageNum-1, loadedFileInfo.mtime, loadedFileInfo.inode); + if ( !mh->loadableIntoProcess(targetPlatform, possiblePath) ) { + useCache = false; + mh = nullptr; + ignoreCacheDylib = true; + } + } + if ( !useCache && !ignoreCacheDylib ) { overrideImageNum = dyldCacheImageNum; _foundDyldCacheRoots = true; } @@ -272,7 +339,7 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for return; } - // if not found yet, mmap file + // if not found yet, mmap file if ( mh == nullptr ) { loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, filePath, _archs, _platform, realPath); mh = (const MachOAnalyzer*)loadedFileInfo.fileContent; @@ -331,10 +398,17 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for loadedFileInfo.fileContent = mh; } + if ( mh->inDyldCache() ) { + // We may be loading from a symlink, so use the path in the cache which is the realpath + filePath = _dyldImageArray->imageForNum(foundImageNum)->path(); + } + // if path is not original path, or its an inserted path (as forEachInColonList uses a stack temporary) if ( (filePath != loadPath) || (linkageType == LinkageType::kInserted) ) { - // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent - filePath = strdup_temp(filePath); + if ( !mh->inDyldCache() ) { + // 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 // This is the case where we didn't find the image with the path in the shared cache, perhaps as it used library paths // but the path we requested had pointed in to the cache @@ -351,6 +425,17 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for } } + // check if this is an iOSMac dylib that is overriding a macOS dylib in the dyld cache + if ( mh->inDyldCache() && iOSSupport(filePath) ) { + const char* twinPath = &filePath[18]; + uint32_t dyldCacheImageIndex; + if ( (_dyldCache != nullptr) && _dyldCache->hasImagePath(twinPath, dyldCacheImageIndex) ) { + ImageNum possibleOverrideNum = dyldCacheImageIndex+1; + if ( possibleOverrideNum != foundImageNum ) + overrideImageNum = possibleOverrideNum; + } + } + if ( !markNeverUnload ) { switch (linkageType) { case LinkageType::kStatic: @@ -369,25 +454,13 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for 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; - } + markNeverUnload = mh->markNeverUnload(_diag); } // Set the path again just in case it was strdup'ed. loadedFileInfo.path = filePath; - // add new entry + // add new entry BuilderLoadedImage entry; entry.loadedFileInfo = loadedFileInfo; entry.imageNum = foundImageNum; @@ -399,7 +472,10 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for entry.isBadImage = false; entry.mustBuildClosure = mustBuildClosure; entry.hasMissingWeakImports = false; + entry.hasInterposingTuples = !mh->inDyldCache() && mh->hasInterposingTuples(); entry.overrideImageNum = overrideImageNum; + entry.exportsTrieOffset = 0; + entry.exportsTrieSize = 0; _loadedImages.push_back(entry); foundImage = &_loadedImages.back(); if ( isFallbackPath ) @@ -409,7 +485,7 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for }); if (result) stopPathVariant = true; - }, _platform); + }, targetPlatform); // If we found a file, but also had an error, then we must have logged a diagnostic for a file we couldn't use. // Clear that for now. @@ -435,19 +511,28 @@ bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, 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; + if ( strncmp(loadPath, "@loader_path/", 13) == 0 ) { + strlcpy(fixedPath, loadedImage.path(), PATH_MAX); + char* lastSlash = strrchr(fixedPath, '/'); + if ( lastSlash != nullptr ) { + strcpy(lastSlash+1, &loadPath[13]); + return true; + } } + else if ( fromLCRPATH && (strcmp(loadPath, "@loader_path") == 0) ) { + // in LC_RPATH allow "@loader_path" without trailing slash + strlcpy(fixedPath, loadedImage.path(), PATH_MAX); + char* lastSlash = strrchr(fixedPath, '/'); + if ( lastSlash != nullptr ) { + lastSlash[1] = '\0'; + return true; + } + } + return false; } -bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[]) +bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, bool fromLCRPATHinMain, char fixedPath[]) { switch ( _atPathHandling ) { case AtPath::none: @@ -455,22 +540,32 @@ bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPA case AtPath::onlyInRPaths: if ( !fromLCRPATH ) return false; + // main executables can always have an LC_RPATH that uses @executable_path, other images cannot if restricted + if ( !fromLCRPATHinMain ) + return false; break; case AtPath::all: break; } - if ( strncmp(loadPath, "@executable_path/", 17) != 0 ) - return false; - if ( _atPathHandling != AtPath::all ) - return false; - - strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX); - char* lastSlash = strrchr(fixedPath, '/'); - if ( lastSlash != nullptr ) { - strcpy(lastSlash+1, &loadPath[17]); - return true; + if ( strncmp(loadPath, "@executable_path/", 17) == 0 ) { + strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX); + char* lastSlash = strrchr(fixedPath, '/'); + if ( lastSlash != nullptr ) { + strcpy(lastSlash+1, &loadPath[17]); + return true; + } } + else if ( fromLCRPATH && (strcmp(loadPath, "@executable_path") == 0) ) { + // in LC_RPATH allow "@executable_path" without trailing slash + strlcpy(fixedPath, _mainProgLoadPath, PATH_MAX); + char* lastSlash = strrchr(fixedPath, '/'); + if ( lastSlash != nullptr ) { + lastSlash[1] = '\0'; + return true; + } + } + return false; } @@ -503,7 +598,7 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm // expand @executable_path // Note this is supported for DYLD_INSERT_LIBRARIES - if ( expandAtExecutablePath(loadPath, false, tempPath) ) { + if ( expandAtExecutablePath(loadPath, false, false, tempPath) ) { bool stop = false; handler(tempPath, stop); return; @@ -530,14 +625,28 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm // LC_RPATHS from each dylib as they are recursively loaded. Our imageChain represents that stack. __block bool done = false; for (const LoadedImageChain* link = &forImageChain; (link != nullptr) && !done; link = link->previous) { + bool mainExecutable = link->image.loadAddress()->isMainExecutable(); link->image.loadAddress()->forEachRPath(^(const char* rPath, bool& stop) { // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.loadedFileInfo.path); - if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, tempPath) ) { + if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, mainExecutable, tempPath) ) { // @loader_path allowed and expended strlcat(tempPath, rpathTail, PATH_MAX); handler(tempPath, stop); } else if ( rPath[0] == '/' ) { +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + if ( (_platform == Platform::iOS) && (strncmp(rPath, "/usr/lib/swift", 14) == 0) ) { + // LC_RPATH is to /usr/lib/swift, but running on macOS that is /System/iOSSupport/usr/lib/swift + strlcpy(tempPath, "/System/iOSSupport", PATH_MAX); + strlcat(tempPath, rPath, PATH_MAX); + strlcat(tempPath, rpathTail, PATH_MAX); + handler(tempPath, stop); + if (stop) { + done = true; + return; + } + } +#endif // LC_RPATH is an absolute path, not blocked by AtPath::none strlcpy(tempPath, rPath, PATH_MAX); strlcat(tempPath, rpathTail, PATH_MAX); @@ -567,7 +676,7 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm handler(loadPath, stop); } -const char* ClosureBuilder::strdup_temp(const char* path) +const char* ClosureBuilder::strdup_temp(const char* path) const { if ( _tempPaths == nullptr ) _tempPaths = PathPool::allocate(); @@ -579,7 +688,9 @@ void ClosureBuilder::addMustBeMissingPath(const char* path) //fprintf(stderr, "must be missing: %s\n", path); if ( _mustBeMissingPaths == nullptr ) _mustBeMissingPaths = PathPool::allocate(); - _mustBeMissingPaths->add(path); + // don't add path if already in list + if ( !_mustBeMissingPaths->contains(path) ) + _mustBeMissingPaths->add(path); } void ClosureBuilder::addSkippedFile(const char* path, uint64_t inode, uint64_t mtime) @@ -599,6 +710,21 @@ ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum ima return li; } } + assert(0 && "LoadedImage not found by num"); +} + +const ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum imageNum) const +{ + for (const BuilderLoadedImage& li : _loadedImages) { + if ( li.imageNum == imageNum ) { + return li; + } + } + for (const BuilderLoadedImage& li : _loadedImages) { + if ( li.overrideImageNum == imageNum ) { + return li; + } + } assert(0 && "LoadedImage not found"); } @@ -609,7 +735,7 @@ ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(const MachOA return li; } } - assert(0 && "LoadedImage not found"); + assert(0 && "LoadedImage not found by mh"); } const MachOAnalyzer* ClosureBuilder::machOForImageNum(ImageNum imageNum) @@ -774,20 +900,10 @@ void ClosureBuilder::loadDanglingUpwardLinks(bool canUseSharedCacheClosure) 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 macOS, the cache can be customer/development in the basesystem/main OS // 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 + // on customer caches, only allow libdispatch.dylib to be overridden + return _dyldCache->isOverridablePath(forImage.path()); } void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImage) @@ -803,7 +919,10 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag writer.setIs64(macho->is64()); writer.setIsExecutable(macho->isMainExecutable()); writer.setUses16KPages(macho->uses16KPages()); - writer.setOverridableDylib(overridableDylib(forImage)); + if ( macho->inDyldCache() ) { + // only set on dylibs in the dyld shared cache + writer.setOverridableDylib(overridableDylib(forImage)); + } writer.setInDyldCache(macho->inDyldCache()); if ( macho->hasObjC() ) { writer.setHasObjC(true); @@ -837,17 +956,15 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime); } #else - if ( _platform == Platform::macOS || MachOFile::isSimulatorPlatform(_platform) ) { - 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); - } + // in cache builder code + if ( !_dyldCache->header.dylibsExpectedOnDisk ) { + // don't add file info for shared cache files mastered out of final file system + // This also covers executable and dlopen closures as we are not running on a live + // file system. no we don't have access to accurate inode/mtime } else { - // all other platforms, cache is built off-device, so inodes are not known + // file is either not in cache or is in cache but not mastered out + writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime); } #endif @@ -891,6 +1008,16 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag // set segments addSegments(writer, macho); + // if shared cache contains two variants of same framework (macOS and iOS), mark iOS one as override of macOS one + if ( _makingDyldCacheImages && iOSSupport(forImage.path()) ) { + const char* truncName = forImage.path()+18; + for (const BuilderLoadedImage& li : _loadedImages) { + if ( strcmp(li.path(), truncName) == 0 ) { + writer.setAsOverrideOf(li.imageNum); + } + } + } + // record if this dylib overrides something in the cache if ( forImage.overrideImageNum != 0 ) { writer.setAsOverrideOf(forImage.overrideImageNum); @@ -902,46 +1029,65 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag _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 { - // Note we have to do binds before rebases so that we know if we have missing lazy binds - addBindInfo(writer, forImage); - if ( _diag.noError() ) - addRebaseInfo(writer, macho); - } - } + // record fix up info + if ( macho->inDyldCache() && !_makingDyldCacheImages ) { + // when building app closures, don't record fix up info about dylibs in the cache + } + else if ( _makeMinimalClosure ) { + // don't record fix up info in dyld3s mode + writer.setFixupsNotEncoded(); + } + else if ( !_makingDyldCacheImages && macho->hasChainedFixups() ) { + // when building app closures, just evaluate target of chain binds and record that table + addChainedFixupInfo(writer, forImage); + } + else { + // run rebase/bind opcodes or chained fixups + addFixupInfo(writer, forImage); } if ( _diag.hasError() ) { writer.setInvalid(); return; } - // Don't build iOSMac for now. Just add an invalid placeholder - if ( _makingDyldCacheImages && strncmp(forImage.path(), "/System/iOSSupport/", 19) == 0 ) { - writer.setInvalid(); - return; - } // add initializers - bool contentRebased = forImage.contentRebased; +#if BUILDING_CACHE_BUILDER + + // In the shared cache builder, we'll only ever see 'inDyldCache' images here for the shared + // cache dylibs themselves. These are in an intermediate state where the cache is not live, the pointers + // are unslid, but the pointers also don't contain fixup chains + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = macho->makeVMAddrConverter(forImage.contentRebased); + if ( macho->inDyldCache() ) { + vmAddrConverter.preferredLoadAddress = 0; + vmAddrConverter.slide = 0; + vmAddrConverter.chainedPointerFormat = 0; + vmAddrConverter.contentRebased = false; + vmAddrConverter.sharedCacheChainedPointerFormat = MachOAnalyzer::VMAddrConverter::SharedCacheFormat::none; + } + +#else + + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = macho->makeVMAddrConverter(forImage.contentRebased); +#if !(BUILDING_LIBDYLD || BUILDING_DYLD) + // The shared cache is always live in dyld/libdyld, but if we get here then we are an offline tool + // In that case, use the shared cache vmAddrConverter if we need it + if ( macho->inDyldCache() ) + vmAddrConverter = _dyldCache->makeVMAddrConverter(forImage.contentRebased); +#endif + +#endif // BUILDING_CACHE_BUILDER + __block unsigned initCount = 0; Diagnostics initializerDiag; - macho->forEachInitializer(initializerDiag, contentRebased, ^(uint32_t offset) { + macho->forEachInitializer(initializerDiag, vmAddrConverter, ^(uint32_t offset) { ++initCount; }, _dyldCache); if ( initializerDiag.noError() ) { if ( initCount != 0 ) { BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets, initCount); __block unsigned index = 0; - macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) { + macho->forEachInitializer(_diag, vmAddrConverter, ^(uint32_t offset) { initOffsets[index++] = offset; }, _dyldCache); writer.setInitOffsets(initOffsets, initCount); @@ -960,13 +1106,13 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag // add terminators (except for dylibs in the cache because they are never unloaded) if ( !macho->inDyldCache() ) { __block unsigned termCount = 0; - macho->forEachTerminator(_diag, contentRebased, ^(uint32_t offset) { + macho->forEachTerminator(_diag, vmAddrConverter, ^(uint32_t offset) { ++termCount; }); if ( termCount != 0 ) { BLOCK_ACCCESSIBLE_ARRAY(uint32_t, termOffsets, termCount); __block unsigned index = 0; - macho->forEachTerminator(_diag, contentRebased, ^(uint32_t offset) { + macho->forEachTerminator(_diag, vmAddrConverter, ^(uint32_t offset) { termOffsets[index++] = offset; }); writer.setTermOffsets(termOffsets, termCount); @@ -1091,8 +1237,9 @@ void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Ima if ( !isTupleFixup(sectVmOffset, sectVmEndOffset, fixupOffset, entrySize, tupleIndex) ) return; uint32_t bindOrdinal; + int64_t addend; uint64_t rebaseTargetOffset; - if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal) ) { + if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) { if ( bindOrdinal < targets.count() ) { resolvedTuples[tupleIndex].stockImplementation = targets[bindOrdinal]; } @@ -1133,6 +1280,7 @@ void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Ima goodTuples.push_back(resolvedTuples[i]); } writer.addInterposingTuples(goodTuples); + _interposingTuplesUsed = !goodTuples.empty(); // 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()); @@ -1152,72 +1300,376 @@ void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Ima }); } -void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh) +const Image::RebasePattern RebasePatternBuilder::_s_maxLeapPattern = { 0xFFFFF, 0, 0xF}; +const uint64_t RebasePatternBuilder::_s_maxLeapCount = _s_maxLeapPattern.repeatCount * _s_maxLeapPattern.skipCount; + + + +RebasePatternBuilder::RebasePatternBuilder(OverflowSafeArray& entriesStorage, uint64_t ptrSize) + : _rebaseEntries(entriesStorage), _lastLocation(-ptrSize), _ptrSize(ptrSize) { - 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, !_foundMissingLazyBinds, ^(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 }); +} + +void RebasePatternBuilder::add(uint64_t runtimeOffset) +{ + 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 { - 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 }); + // previous contiguous run already has max 255, so start a new run + _rebaseEntries.push_back({ 1, 1, 0 }); } - lastLocation = runtimeOffset; - }); - writer.setRebaseInfo(rebaseEntries); + } + 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 > _s_maxLeapCount ) { + _rebaseEntries.push_back(_s_maxLeapPattern); + advanceCount -= _s_maxLeapCount; + } + // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate + while ( advanceCount > _s_maxLeapPattern.repeatCount ) { + uint64_t count = advanceCount / _s_maxLeapPattern.skipCount; + _rebaseEntries.push_back({ (uint32_t)count, 0, _s_maxLeapPattern.skipCount }); + advanceCount -= (count*_s_maxLeapPattern.skipCount); + } + if ( advanceCount != 0 ) + _rebaseEntries.push_back({ (uint32_t)advanceCount, 0, 1 }); + _rebaseEntries.push_back({ 1, 1, 0 }); + } + _lastLocation = runtimeOffset; + +} + + +BindPatternBuilder::BindPatternBuilder(OverflowSafeArray& entriesStorage, uint64_t ptrSize) + : _bindEntries(entriesStorage), _ptrSize(ptrSize), _lastOffset(-ptrSize), _lastTarget({ {0, 0} }) +{ +} + +void BindPatternBuilder::add(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, bool weakBindCoalese) +{ + if ( weakBindCoalese ) { + // may be previous bind to this location + // if so, update that rather create new BindPattern + for (Image::BindPattern& aBind : _bindEntries) { + if ( (aBind.startVmOffset == runtimeOffset) && (aBind.repeatCount == 1) && (aBind.skipCount == 0) ) { + aBind.target = target; + return; + } + } + } + bool mergedIntoPrevious = false; + if ( !mergedIntoPrevious && (target == _lastTarget) && (runtimeOffset > _lastOffset) && !_bindEntries.empty() ) { + uint64_t skipAmount = (runtimeOffset - _lastOffset - _ptrSize)/_ptrSize; + if ( skipAmount*_ptrSize != (runtimeOffset - _lastOffset - _ptrSize) ) { + // misaligned pointer means we cannot optimize + } + else { + if ( (_bindEntries.back().repeatCount == 1) && (_bindEntries.back().skipCount == 0) && (skipAmount <= 255) ) { + _bindEntries.back().repeatCount = 2; + _bindEntries.back().skipCount = skipAmount; + assert(_bindEntries.back().skipCount == skipAmount); // check overflow + mergedIntoPrevious = true; + } + else if ( (_bindEntries.back().skipCount == skipAmount) && (_bindEntries.back().repeatCount < 0xfff) ) { + uint32_t prevRepeatCount = _bindEntries.back().repeatCount; + _bindEntries.back().repeatCount += 1; + assert(_bindEntries.back().repeatCount > prevRepeatCount); // check overflow + mergedIntoPrevious = true; + } + } + } + if ( (target == _lastTarget) && (runtimeOffset == _lastOffset) && !_bindEntries.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); + _bindEntries.push_back(pattern); + } + _lastTarget = target; + _lastOffset = runtimeOffset; +} + + +bool ClosureBuilder::mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const +{ + // when building dylibs into the dyld cache, there is no load-order, so we cannot use the standard algorithm + // otherwise call through to standard weak-def coalescing algorithm + if ( !_makingDyldCacheImages ) + return MachOAnalyzerSet::mas_fromImageWeakDefLookup(fromWmo, symbolName, addend, patcher, target); + + + // look first in /usr/lib/libc++, most will be here + Diagnostics diag; + for (const BuilderLoadedImage& li : _loadedImages) { + if ( li.loadAddress()->hasWeakDefs() && (strncmp(li.path(), "/usr/lib/libc++", 15) == 0) ) { + WrappedMachO libcxxWmo(li.loadAddress(), this, (void*)&li); + if ( libcxxWmo.findSymbolIn(diag, symbolName, addend, target) ) + return true; + } + } + + // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself + if ( fromWmo.findSymbolIn(diag, symbolName, addend, target) ) + return true; + + // if we link with something that also defines this weak-def, use it + ClosureBuilder::BuilderLoadedImage* fromImage = (ClosureBuilder::BuilderLoadedImage*)(fromWmo._other); + for (Image::LinkedImage child : fromImage->dependents) { + if (child.imageNum() == kMissingWeakLinkedImage) + continue; + if (child.kind() == Image::LinkKind::upward) + continue; + const BuilderLoadedImage& childLi = findLoadedImage(child.imageNum()); + if ( childLi.loadAddress()->hasWeakDefs() ) { + WrappedMachO childWmo(childLi.loadAddress(), this, (void*)&childLi); + if ( childWmo.findSymbolIn(diag, symbolName, addend, target) ) + return true; + } + } + return false; +} + +void ClosureBuilder::mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const +{ + bool stop = false; + for (const ClosureBuilder::BuilderLoadedImage& li : _loadedImages) { + WrappedMachO wmo(li.loadAddress(), this, (void*)&li); + handler(wmo, li.rtldLocal, stop); + if ( stop ) + break; + } +} + +bool ClosureBuilder::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const +{ + // if weakImport and missing, bind to NULL + if ( weakImport ) { + // construct NULL target + target.offsetInImage = 0; + target.kind = FixupTarget::Kind::bindAbsolute; + target.requestedSymbolName = symbolName; + target.foundSymbolName = nullptr; + // Record that we found a missing weak import so that the objc optimizer doens't have to check + ClosureBuilder::BuilderLoadedImage* fromBLI = (ClosureBuilder::BuilderLoadedImage*)(fromWmo->_other); + fromBLI->hasMissingWeakImports = true; + return true; + } + // dyld3 binds everything ahead of time, to simulator lazy failure + // if non-weakImport and lazy, then bind to __dyld_missing_symbol_abort() + if ( lazyBind && _allowMissingLazies ) { + for (const BuilderLoadedImage& li : _loadedImages) { + if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) { + WrappedMachO libdyldWmo(li.loadAddress(), this, (void*)&li); + Diagnostics diag; + if ( libdyldWmo.findSymbolIn(diag, "__dyld_missing_symbol_abort", 0, target) ) { + // closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace + return true; + } + break; + } + } + } + // support abort payload + if ( _launchErrorInfo != nullptr ) { + _launchErrorInfo->kind = DYLD_EXIT_REASON_SYMBOL_MISSING; + _launchErrorInfo->clientOfDylibPath = strdup_temp(clientPath); + _launchErrorInfo->targetDylibPath = strdup_temp(expectedInDylibPath); + _launchErrorInfo->symbol = symbolName; + } + return false; +} + +void ClosureBuilder::mas_mainExecutable(WrappedMachO& wmo) const +{ + const ClosureBuilder::BuilderLoadedImage& mainLi = _loadedImages[_mainProgLoadIndex]; + WrappedMachO mainWmo(mainLi.loadAddress(), this, (void*)&mainLi); + wmo = mainWmo; +} + +void* ClosureBuilder::mas_dyldCache() const +{ + return (void*)_dyldCache; +} + +bool ClosureBuilder::wmo_dependent(const WrappedMachO* wmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const +{ + ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other); + + if ( depIndex >= forImage->dependents.count() ) + return false; + + ImageNum childNum = forImage->dependents[depIndex].imageNum(); + if ( childNum == kMissingWeakLinkedImage ) { + missingWeakDylib = true; + return true; + } + const BuilderLoadedImage& depLoadedImage = this->findLoadedImage(childNum); + childWmo = WrappedMachO(depLoadedImage.loadAddress(), this, (void*)&depLoadedImage); + missingWeakDylib = false; + return true; +} + +const char* ClosureBuilder::wmo_path(const WrappedMachO* wmo) const +{ + ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other); + return forImage->loadedFileInfo.path; +} + +MachOAnalyzerSet::ExportsTrie ClosureBuilder::wmo_getExportsTrie(const WrappedMachO* wmo) const +{ + ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other); + if ( forImage->exportsTrieOffset == 0 ) { + // if trie location not already cached, look it up + wmo->_mh->hasExportTrie(forImage->exportsTrieOffset, forImage->exportsTrieSize); + } + const uint8_t* start = nullptr; + const uint8_t* end = nullptr; + if ( forImage->exportsTrieOffset != 0 ) { + start = (uint8_t*)wmo->_mh + forImage->exportsTrieOffset; + end = start + forImage->exportsTrieSize; + } + return { start, end }; +} + + +Image::ResolvedSymbolTarget ClosureBuilder::makeResolvedTarget(const FixupTarget& target) const +{ + Image::ResolvedSymbolTarget resolvedTarget; + switch ( target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + assert(0 && "target is a rebase"); + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + if ( target.foundInImage._mh->inDyldCache() ) { + resolvedTarget.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache; + resolvedTarget.sharedCache.offset = (uint8_t*)target.foundInImage._mh - (uint8_t*)_dyldCache + target.offsetInImage; + } + else { + ClosureBuilder::BuilderLoadedImage* targetBuildLoaderImage = (ClosureBuilder::BuilderLoadedImage*)(target.foundInImage._other); + resolvedTarget.image.kind = Image::ResolvedSymbolTarget::kindImage; + resolvedTarget.image.imageNum = targetBuildLoaderImage->imageNum; + resolvedTarget.image.offset = target.offsetInImage; + } + return resolvedTarget; + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + resolvedTarget.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute; + resolvedTarget.absolute.value = target.offsetInImage; + return resolvedTarget; + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + assert(0 && "unknown FixupTarget::Kind::bindMissingSymbol found in closure"); + break; + } + assert(0 && "unknown FixupTarget kind"); +} + +void ClosureBuilder::addFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage) +{ + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern, rebaseEntries, 1024); + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern, binds, 512); + __block RebasePatternBuilder rebaseBuilder(rebaseEntries, forImage.loadAddress()->pointerSize()); + __block BindPatternBuilder bindBuilder(binds, forImage.loadAddress()->pointerSize()); + + const bool stompedLazyOpcodes = forImage.loadAddress()->hasStompedLazyOpcodes(); + WrappedMachO forImage_wmo(forImage.loadAddress(), this, (void*)&forImage); + forImage_wmo.forEachFixup(_diag, + ^(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) { + if ( target.kind == MachOAnalyzerSet::FixupTarget::Kind::rebase ) { + // normally ignore rebase on lazy pointer because dyld3 will immediately bind that same pointer + // but if app is licensewared and stomps lazy bind opcodes, keep the rebases + if ( target.isLazyBindRebase && !stompedLazyOpcodes ) + return; + } + if ( _dylibFixupHandler ) { + // applying fixups to dylibs in dyld cache as the cache is being built + _dylibFixupHandler(forImage.loadAddress(), fixupLocRuntimeOffset, pmd, target); + return; + } + switch ( target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + if ( !_leaveRebasesAsOpcodes ) + rebaseBuilder.add(fixupLocRuntimeOffset); + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + bindBuilder.add(fixupLocRuntimeOffset, makeResolvedTarget(target), target.weakCoalesced); + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + // this is last call from forEachFixup() because a symbol could not be resolved + break; + } + }, + ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target) { + addWeakDefCachePatch(cachedDylibIndex, exportCacheOffset, target); + } + ); + + // check for __dyld section in main executable to support licenseware + if ( forImage.loadAddress()->filetype == MH_EXECUTE ) { + forImage.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) { + // find dyld3::compatFuncLookup in libdyld.dylib + assert(_libDyldImageNum != 0); + const BuilderLoadedImage& libdyldImage = findLoadedImage(_libDyldImageNum); + WrappedMachO libdyldWmo(libdyldImage.loadAddress(), this, (void*)&libdyldImage); + FixupTarget libdyldCompatTarget; + if ( libdyldWmo.findSymbolIn(_diag, "__ZN5dyld316compatFuncLookupEPKcPPv", 0, libdyldCompatTarget) ) { + // dyld_func_lookup is second pointer in __dyld section + uint64_t fixupLocRuntimeOffset = sectInfo.sectAddr - forImage.loadAddress()->preferredLoadAddress() + forImage.loadAddress()->pointerSize(); + bindBuilder.add(fixupLocRuntimeOffset, makeResolvedTarget(libdyldCompatTarget), false); + } + else { + _diag.error("libdyld.dylib is missing dyld3::compatFuncLookup"); + } + } + }); + } + + // add all rebase and bind info into closure, unless building dyld cache + if ( !_makingDyldCacheImages ) { + if ( _leaveRebasesAsOpcodes ) + writer.setRebasesNotEncoded(); + else + writer.setRebaseInfo(rebaseEntries); + writer.setBindInfo(binds); + } // i386 programs also use text relocs to rebase stubs - if ( mh->cputype == CPU_TYPE_I386 ) { + if ( (forImage.loadAddress()->cputype == CPU_TYPE_I386) && !_makingDyldCacheImages ) { STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern, textRebases, 512); __block uint64_t lastOffset = -4; - mh->forEachTextRebase(_diag, ^(uint64_t runtimeOffset, bool& stop) { + forImage.loadAddress()->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; @@ -1247,487 +1699,64 @@ void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh) }); 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), - void (^missingLazyBindHandler)()) -{ - __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, bool lazyBind, 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, lazyBind, addend, target, targetInfo) ) { - if ( !targetInfo.skippableWeakDef ) { - handler(runtimeOffset, target, targetInfo, stop); - lastSymbolName = symbolName; - lastLibOrdinal = libOrdinal; - lastAddend = addend; - } - } - else { - stop = true; - } - }, ^(const char* symbolName) { - strongHandler(symbolName); - }, ^() { - missingLazyBindHandler(); - }); -} -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, 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, 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); - } - } - } - } - } - }, ^() { - _foundMissingLazyBinds = true; - }); - // check for __dyld section in main executable to support licenseware - if ( forImage.loadAddress()->filetype == MH_EXECUTE ) { - forImage.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { - if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) { - // find dyld3::compatFuncLookup in libdyld.dylib - assert(_libDyldImageNum != 0); - Image::ResolvedSymbolTarget lookupFuncTarget; - ResolvedTargetInfo lookupFuncInfo; - if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld316compatFuncLookupEPKcPPv", 0, false, false, lookupFuncTarget, lookupFuncInfo) ) { - // add bind to set second pointer in __dyld section to be dyld3::compatFuncLookup - uint64_t runtimeOffset = sectInfo.sectAddr - forImage.loadAddress()->preferredLoadAddress() + forImage.loadAddress()->pointerSize(); - Image::BindPattern compatFuncPattern; - compatFuncPattern.target = lookupFuncTarget; - compatFuncPattern.startVmOffset = runtimeOffset; - compatFuncPattern.repeatCount = 1; - compatFuncPattern.skipCount = 0; - assert(compatFuncPattern.startVmOffset == runtimeOffset); - binds.push_back(compatFuncPattern); - } - else { - _diag.error("libdyld.dylib is dyld3::compatFuncLookup"); - } - } - }); +void ClosureBuilder::addWeakDefCachePatch(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget) +{ + // minimal closures don't need weak def patches, they are regenerated at launch + if ( _makeMinimalClosure ) + return; + + // don't add duplicates + for (const Closure::PatchEntry& aPatch : _weakDefCacheOverrides) { + if ( aPatch.exportCacheOffset == exportCacheOffset ) + return; } - - writer.setBindInfo(binds); + // add new patch entry + ClosureBuilder::BuilderLoadedImage* targetImage = (ClosureBuilder::BuilderLoadedImage*)(patchTarget.foundInImage._other); + Closure::PatchEntry patch; + patch.overriddenDylibInCache = cachedDylibIndex+1; // convert image index to ImageNum + patch.exportCacheOffset = exportCacheOffset; + patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage; + patch.replacement.image.imageNum = targetImage->imageNum; + patch.replacement.image.offset = patchTarget.offsetInImage; + _weakDefCacheOverrides.push_back(patch); } -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* const 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, BuilderLoadedImage& forImage) { + // as a side effect of building targets array, we discover if anything in dyld cache uses weak-defs that need + // to be redirected to an impl in some other dylib (cache patched) + auto patchAddr = ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget) { + addWeakDefCachePatch(cachedDylibIndex, exportCacheOffset, patchTarget); + }; + // 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, false, 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()); + FixupTarget target; + WrappedMachO forImageWmo(forImage.loadAddress(), this, (void*)&forImage); + if ( wmo_findSymbolFrom(&forImageWmo, _diag, libOrdinal, symbolName, weakImport, false, addend, patchAddr, target) ) + targets.push_back(makeResolvedTarget(target)); + else stop = true; - return; - } - if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { - // 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; + // C++ main executables can overide operator new, check for that + if ( forImage.loadAddress()->isMainExecutable() && forImage.loadAddress()->hasWeakDefs() ) { + WrappedMachO mainWmo(forImage.loadAddress(), this, (void*)&forImage); + wmo_findExtraSymbolFrom(&mainWmo, patchAddr); + } + uint64_t chainStartsOffset = forImage.loadAddress()->chainStartsOffset(); - if ( _handlers != nullptr ) { - forImage.loadAddress()->withChainStarts(_diag, chainStartsOffset, ^(const dyld_chained_starts_in_image* starts) { - _handlers->chainedBind(forImage.imageNum, forImage.loadAddress(), starts, targets, targetInfos); - }); - } - else { - writer.setChainedFixups(chainStartsOffset, targets); - } - - // 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, false, foundInfo, nullptr) ) { - _weakDefsFromChainedBinds.push_back(weakSymbolName); - } - } - } -} - - -bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports, - bool weakImport, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo) -{ - targetInfo.foundInDylib = nullptr; - targetInfo.requestedSymbolName = symbolName; - targetInfo.addend = addend; - targetInfo.weakBindCoalese = false; - targetInfo.weakBindSameImage = false; - targetInfo.isWeakDef = false; - targetInfo.skippableWeakDef = 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, weakImport, 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() ) { - uint64_t offsetValue = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend; - target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache; - target.sharedCache.offset = offsetValue; - assert(target.sharedCache.offset == offsetValue); - } - else { - uint64_t offsetValue = foundInfo.value + addend; - target.image.kind = Image::ResolvedSymbolTarget::kindImage; - target.image.imageNum = findLoadedImage(impDylib).imageNum; - target.image.offset = offsetValue; - assert(target.image.offset == offsetValue); - } - return true; - } - return false; -} - -bool ClosureBuilder::findSymbol(BuilderLoadedImage& fromImage, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, - uint64_t addend, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo) -{ - target.raw = 0; - targetInfo.weakBindCoalese = false; - targetInfo.weakBindSameImage = false; - targetInfo.isWeakDef = false; - targetInfo.skippableWeakDef = 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, weakImport, target, targetInfo) ) - return true; - } - if ( weakImport ) { - target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute; - target.absolute.value = 0; - // Record that we found a missing weak import so that the objc optimizer doens't have to check - fromImage.hasMissingWeakImports = true; - return true; - } - // closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace - if ( lazyBind && _allowMissingLazies ) { - if ( findMissingSymbolHandler(target, targetInfo) ) - return true; - } - _diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromImage.path()); - } - else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { - // 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; - - if ( _makingDyldCacheImages ) { - // _loadedImages is all dylibs in the dyld cache, it is not load-order, so need alterate weak-def binding algorithm - // look first in /usr/lib/libc++, most will be here - for (const BuilderLoadedImage& li : _loadedImages) { - if ( li.loadAddress()->hasWeakDefs() && (strncmp(li.path(), "/usr/lib/libc++", 15) == 0) ) { - if ( findSymbolInImage(li.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) { - foundImpl = true; - break; - } - } - } - // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself - if ( !foundImpl ) { - if ( findSymbolInImage(fromImage.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) { - foundImpl = true; - } - } - // if still not found, then this is the rare case of a simple use of a weak-def symbol - if ( !foundImpl ) { - // look in all direct dependents - for (Image::LinkedImage child : fromImage.dependents) { - if (child.imageNum() == kMissingWeakLinkedImage) - continue; - BuilderLoadedImage& childLi = findLoadedImage(child.imageNum()); - if ( childLi.loadAddress()->hasWeakDefs() && findSymbolInImage(childLi.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) { - foundImpl = true; - break; - } - } - } - targetInfo.weakBindCoalese = true; - } - else { - // walk images in load-order to find first that implements this symbol - 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, weakImport, 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 && 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, weakImport, 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; - if ( weakImport ) { - target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute; - target.absolute.value = 0; - return true; - } - if ( ! fromImage.loadAddress()->hasChainedFixups() ) { - // support old binaries where symbols have been stripped and have weak_bind to itself - targetInfo.skippableWeakDef = true; - return true; - } - - _diag.error("symbol '%s' not found, expected to be weak-def coalesced by '%s'", symbolName, fromImage.path()); - } - 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, weakImport, target, targetInfo) ) - return true; - } - - if ( weakImport ) { - target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute; - target.absolute.value = 0; - // Record that we found a missing weak import so that the objc optimizer doens't have to check - fromImage.hasMissingWeakImports = true; - return true; - } - - // closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld - if ( lazyBind && _allowMissingLazies ) { - if ( findMissingSymbolHandler(target, targetInfo) ) - return true; - } - - // symbol not found and not weak or lazy so error out - 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 = strdup_temp(fromImage.path()); - _launchErrorInfo->targetDylibPath = strdup_temp(expectedInPath); - _launchErrorInfo->symbol = symbolName; - } - } - return false; -} - - -bool ClosureBuilder::findMissingSymbolHandler(Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo) -{ - for (BuilderLoadedImage& li : _loadedImages) { - if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) { - if ( findSymbolInImage(li.loadAddress(), "__dyld_missing_symbol_abort", 0, false, false, target, targetInfo) ) { - return true; - } - break; - } - } - return false; -} + writer.setChainedFixups(chainStartsOffset, targets); + } void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError) { @@ -1740,11 +1769,10 @@ void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo i hasError = true; return; } - for (const Image::LinkedImage& dep : _loadedImages[loadIndex].dependents) { if ( dep.imageNum() == kMissingWeakLinkedImage ) continue; - ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum()); + const ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum()); uint32_t depLoadIndex = (uint32_t)_loadedImages.index(depLi); if ( dep.kind() == Image::LinkKind::upward ) { if ( !initInfos[depLoadIndex].visited ) @@ -1827,20 +1855,19 @@ void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter) // record which is libdyld assert(_libDyldImageNum != 0); - Image::ResolvedSymbolTarget entryLocation; - ResolvedTargetInfo entryInfo; - if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, false, entryLocation, entryInfo) ) { + const BuilderLoadedImage& libdyldImage = findLoadedImage(_libDyldImageNum); + WrappedMachO libdyldWmo(libdyldImage.loadAddress(), this, (void*)&libdyldImage); + FixupTarget libdyldEntryTarget; + if ( libdyldWmo.findSymbolIn(_diag, "__ZN5dyld318entryVectorForDyldE", 0, libdyldEntryTarget) ) { 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 ( libdyldEntryTarget.kind == MachOAnalyzerSet::FixupTarget::Kind::bindToImage ) { + libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)libdyldEntryTarget.foundInImage._mh + libdyldEntryTarget.offsetInImage); } - if ( (libDyldEntry != nullptr) && ((libDyldEntry->binaryFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == dyld3::closure::kFormatVersion) ) + // peak at entry vector to see if version is compatible + if ( (libDyldEntry != nullptr) && ((libDyldEntry->binaryFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == dyld3::closure::kFormatVersion) ) { + Image::ResolvedSymbolTarget entryLocation = makeResolvedTarget(libdyldEntryTarget); closureWriter.setLibDyldEntry(entryLocation); + } else _diag.error("libdyld.dylib entry vector is incompatible"); } @@ -1853,13 +1880,13 @@ void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter) closureWriter.setTopImageNum(mainProgImageNum); // add entry - uint32_t entryOffset; + uint64_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; + location.image.offset = (uint32_t)entryOffset; if ( usesCRT ) closureWriter.setStartEntry(location); else @@ -1897,7 +1924,7 @@ void ClosureBuilder::invalidateInitializerRoots() for (Image::LinkedImage depIndex : li.dependents) { if ( depIndex.imageNum() == kMissingWeakLinkedImage ) continue; - BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum()); + const BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum()); // If a dependent is bad, or a new image num, or an override, then we need this image to get a new closure if ( depImage.mustBuildClosure ) { li.mustBuildClosure = true; // mark bad @@ -1923,6 +1950,7 @@ bool ClosureBuilder::EqualCString::equal(const char* s1, const char* s2) { } + struct HashUInt64 { static size_t hash(const uint64_t& v) { return std::hash{}(v); @@ -2031,6 +2059,11 @@ bool ClosureBuilder::optimizeObjC(Array& writers) { Image::ResolvedSymbolTarget objcProtocolClassTarget; objcProtocolClassTarget.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache; if ( _dyldCacheIsLive ) { + // If we are on arm64e, the protocol ISA in the shared cache was signed. We don't + // want the signature bits in the encoded value +#if __has_feature(ptrauth_calls) + classProtocolVMAddr = (uint64_t)__builtin_ptrauth_strip((void*)classProtocolVMAddr, ptrauth_key_asda); +#endif objcProtocolClassTarget.sharedCache.offset = classProtocolVMAddr - (uint64_t)_dyldCache; } else { objcProtocolClassTarget.sharedCache.offset = classProtocolVMAddr - _dyldCache->unslidLoadAddress(); @@ -2078,6 +2111,12 @@ bool ClosureBuilder::optimizeObjC(Array& writers) { case DYLD_CHAINED_PTR_64: // We've tested the 64-bit chained fixups. break; + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + // FIXME: Test 64-bit offset chained fixups then enable this. + continue; case DYLD_CHAINED_PTR_32: case DYLD_CHAINED_PTR_32_CACHE: case DYLD_CHAINED_PTR_32_FIRMWARE: @@ -2108,12 +2147,17 @@ bool ClosureBuilder::optimizeObjC(Array& writers) { image.objcImageInfoVMOffset = (uint64_t)objcImageInfo - (uint64_t)ma; } + // objc supports a linker set which is a magic section of duplicate objc classes to ignore + // We need to match that behaviour + Map duplicateClassesToIgnore; + parseObjCClassDuplicates(duplicateClassesToIgnore); + OverflowSafeArray closureSelectorStrings; Map closureSelectorMap; OverflowSafeArray closureDuplicateSharedCacheClassNames; Map closureDuplicateSharedCacheClassMap; for (ObjCOptimizerImage& image : objcImages) { - optimizeObjCClasses(objcClassOpt, sharedCacheImagesMap, closureDuplicateSharedCacheClassMap, image); + optimizeObjCClasses(objcClassOpt, sharedCacheImagesMap, closureDuplicateSharedCacheClassMap, duplicateClassesToIgnore, image); if (image.diag.hasError()) continue; @@ -2312,8 +2356,43 @@ bool ClosureBuilder::optimizeObjC(Array& writers) { } // Method list fixups - // TODO: Implement this STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::MethodListFixup, methodListFixups, 512); + if ( !image.methodListFixups.empty() ) { + + __block uint64_t lastOffset = -pointerSize; + for (uint64_t runtimeOffset : image.methodListFixups) { + bool mergedIntoPrevious = false; + if ( (runtimeOffset > lastOffset) && !methodListFixups.empty() ) { + uint64_t skipAmount = (runtimeOffset - lastOffset - pointerSize)/pointerSize; + if ( skipAmount*pointerSize != (runtimeOffset - lastOffset - pointerSize) ) { + // misaligned pointer means we cannot optimize + } + else { + if ( (methodListFixups.back().repeatCount == 1) && (methodListFixups.back().skipCount == 0) && (skipAmount <= 255) ) { + methodListFixups.back().repeatCount = 2; + methodListFixups.back().skipCount = skipAmount; + assert(methodListFixups.back().skipCount == skipAmount); // check overflow + mergedIntoPrevious = true; + } + else if ( (methodListFixups.back().skipCount == skipAmount) && (methodListFixups.back().repeatCount < 0xfff) ) { + uint32_t prevRepeatCount = methodListFixups.back().repeatCount; + methodListFixups.back().repeatCount += 1; + assert(methodListFixups.back().repeatCount > prevRepeatCount); // check overflow + mergedIntoPrevious = true; + } + } + } + if ( !mergedIntoPrevious ) { + Image::MethodListFixup pattern; + pattern.startVmOffset = runtimeOffset; + pattern.repeatCount = 1; + pattern.skipCount = 0; + assert(pattern.startVmOffset == runtimeOffset); + methodListFixups.push_back(pattern); + } + lastOffset = runtimeOffset; + } + } image.writer->setObjCFixupInfo(objcProtocolClassTarget, image.objcImageInfoVMOffset, protocolFixups, selRefFixups, stableSwiftFixups, methodListFixups); @@ -2331,6 +2410,7 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe const dyld3::MachOAnalyzer* ma = li.loadAddress(); uint32_t pointerSize = ma->pointerSize(); const uint64_t loadAddress = ma->preferredLoadAddress(); + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased); // The legacy (objc1) codebase uses a bunch of sections we don't want to reason about. If we see them just give up. __block bool foundBadSection = false; @@ -2362,9 +2442,20 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe uint32_t sharedCacheSentinelIndex = objcSelOpt->getSentinelIndex(); + // Track the locations where we've updated selector references. With relative method lists, + // we share selref slots across classes, categories, protocols, and SEL() expressions, so we may + // visit a location more than once + __block Map seenSelectorReferenceImageOffsets; + auto visitReferenceToObjCSelector = ^void(uint64_t selectorStringVMAddr, uint64_t selectorReferenceVMAddr) { uint64_t selectorUseImageOffset = selectorReferenceVMAddr - loadAddress; + auto selUseItAndInserted = seenSelectorReferenceImageOffsets.insert({ selectorUseImageOffset, true }); + if ( !selUseItAndInserted.second ) { + // If we didn't insert the selector reference, then its already there so we should skip it + return; + } + if ( (selectorUseImageOffset & 3) != 0 ) { image.diag.error("Unaligned selector reference fixup"); return; @@ -2484,49 +2575,56 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe visitReferenceToObjCSelector(method.nameVMAddr, method.nameLocationVMAddr); }; + auto visitMethodList = ^(uint64_t methodListVMAddr) { + if ( methodListVMAddr == 0 ) + return; + bool isRelativeMethodList = false; + ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod, &isRelativeMethodList); + if (image.diag.hasError()) + return; + // Record the offset to the method list so that we can mark it as being uniqued + // We can only do this if we have a pointer based method list as relative method lists are + // in read-only memory + if ( !isRelativeMethodList ) + image.methodListFixups.push_back(methodListVMAddr - loadAddress); + }; + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { - ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), li.contentRebased, - visitMethod); + visitMethodList(objcClass.baseMethodsVMAddr(pointerSize)); }; auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { - ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, li.contentRebased, - visitMethod); - ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, li.contentRebased, - visitMethod); + visitMethodList(objcCategory.instanceMethodsVMAddr); + visitMethodList(objcCategory.classMethodsVMAddr); }; auto visitProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol) { - ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, li.contentRebased, - visitMethod); - ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, li.contentRebased, - visitMethod); - ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, li.contentRebased, - visitMethod); - ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, li.contentRebased, - visitMethod); + visitMethodList(objCProtocol.instanceMethodsVMAddr); + visitMethodList(objCProtocol.classMethodsVMAddr); + visitMethodList(objCProtocol.optionalInstanceMethodsVMAddr); + visitMethodList(objCProtocol.optionalClassMethodsVMAddr); }; // Walk the class list - ma->forEachObjCClass(image.diag, li.contentRebased, visitClass); + ma->forEachObjCClass(image.diag, vmAddrConverter, visitClass); if (image.diag.hasError()) return; // Walk the category list - ma->forEachObjCCategory(image.diag, li.contentRebased, visitCategory); + ma->forEachObjCCategory(image.diag, vmAddrConverter, visitCategory); if (image.diag.hasError()) return; // Walk the protocol list - ma->forEachObjCProtocol(image.diag, li.contentRebased, visitProtocol); + ma->forEachObjCProtocol(image.diag, vmAddrConverter, visitProtocol); if (image.diag.hasError()) return; // Visit the selector refs - ma->forEachObjCSelectorReference(image.diag, li.contentRebased, ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) { + ma->forEachObjCSelectorReference(image.diag, vmAddrConverter, ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) { visitReferenceToObjCSelector(selRefTargetVMAddr, selRefVMAddr); }); if (image.diag.hasError()) @@ -2584,6 +2682,7 @@ void ClosureBuilder::addDuplicateObjCClassWarning(const char* className, void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt, const Map& sharedCacheImagesMap, const Map& duplicateSharedCacheClasses, + const Map& duplicateClassesToIgnore, ObjCOptimizerImage& image) { BuilderLoadedImage& li = *image.loadedImage; @@ -2592,15 +2691,15 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas const dyld3::MachOAnalyzer* ma = li.loadAddress(); const uint32_t pointerSize = ma->pointerSize(); const uint64_t loadAddress = ma->preferredLoadAddress(); + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased); // Keep track of any missing weak imports so that we can tell if the superclasses are nil // This is necessary as the shared cache will be marked with 'no missing weak superclasses' // and so we need to continue to satisfy that constraint __block Map missingWeakImportOffets; if (li.hasMissingWeakImports) { - if (ma->hasChainedFixups()) { - const Image* closureImage = image.writer->currentImage(); - + const Image* closureImage = image.writer->currentImage(); + if ( closureImage->hasChainedFixups() ) { const Array targets = closureImage->chainedTargets(); if ( !targets.empty() ) { ma->withChainStarts(_diag, closureImage->chainedStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { @@ -2608,7 +2707,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) { uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; uint32_t bindOrdinal; - if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal) ) { + int64_t addend; + if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) { if ( bindOrdinal < targets.count() ) { const Image::ResolvedSymbolTarget& target = targets[bindOrdinal]; if ( (target.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (target.absolute.value == 0) ) @@ -2624,12 +2724,12 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas if (image.diag.hasError()) return; } - } else { - forEachBind(li, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) { - if ( (target.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (target.absolute.value == 0) ) - missingWeakImportOffets[runtimeOffset] = true; - }, ^(const char *strongSymbolName) { - }, ^() { }); + } + else { + closureImage->forEachBind(^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget bindTarget, bool &stop) { + if ( (bindTarget.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (bindTarget.absolute.value == 0) ) + missingWeakImportOffets[imageOffsetToBind] = true; + }); } } @@ -2637,9 +2737,9 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas __block MachOAnalyzer::SectionCache classNameSectionCache(ma); __block MachOAnalyzer::SectionCache classSectionCache(ma); - ma->forEachObjCClass(image.diag, li.contentRebased, ^(Diagnostics &diag, uint64_t classVMAddr, - uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, - const MachOAnalyzer::ObjCClassInfo &objcClass, bool isMetaClass) { + ma->forEachObjCClass(image.diag, vmAddrConverter, ^(Diagnostics &diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const MachOAnalyzer::ObjCClassInfo &objcClass, bool isMetaClass) { if (isMetaClass) return; // Make sure the superclass pointer is not nil @@ -2697,7 +2797,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas // exactly one matching class. Check if its loaded const dyld3::MachOAnalyzer* sharedCacheMA = getMachHeaderFromObjCHeaderInfo(hi, pointerSize); if (sharedCacheImagesMap.find(sharedCacheMA) != sharedCacheImagesMap.end()) { - addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName()); + if ( duplicateClassesToIgnore.find(className) == duplicateClassesToIgnore.end() ) + addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName()); // We have a duplicate class, so check if we've already got it in our map. if ( duplicateSharedCacheClasses.find(className) == duplicateSharedCacheClasses.end() ) { @@ -2717,7 +2818,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas for (uint32_t i = 0; i < count; i++) { const dyld3::MachOAnalyzer* sharedCacheMA = getMachHeaderFromObjCHeaderInfo(hilist[i], pointerSize); if (sharedCacheImagesMap.find(sharedCacheMA) != sharedCacheImagesMap.end()) { - addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName()); + if ( duplicateClassesToIgnore.find(className) == duplicateClassesToIgnore.end() ) + addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName()); // We have a duplicate class, so check if we've already got it in our map. if ( duplicateSharedCacheClasses.find(className) == duplicateSharedCacheClasses.end() ) { @@ -2805,18 +2907,14 @@ void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* const dyld3::MachOAnalyzer* ma = li.loadAddress(); const uint32_t pointerSize = ma->pointerSize(); const uint64_t loadAddress = ma->preferredLoadAddress(); + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased); // Protocol names and data may be in different sections depending on swift vs objc so handle multiple sections __block MachOAnalyzer::SectionCache protocolNameSectionCache(ma); __block MachOAnalyzer::SectionCache protocolSectionCache(ma); - ma->forEachObjCProtocol(image.diag, li.contentRebased, ^(Diagnostics &diag, uint64_t protocolVMAddr, - const dyld3::MachOAnalyzer::ObjCProtocol &objCProtocol) { - if ( objCProtocol.requiresObjCReallocation ) { - // We can't optimize this protocol as the runtime needs all fields to be present - diag.error("Protocol is too small to be optimized"); - return; - } + ma->forEachObjCProtocol(image.diag, vmAddrConverter, ^(Diagnostics &diag, uint64_t protocolVMAddr, + const dyld3::MachOAnalyzer::ObjCProtocol &objCProtocol) { if ( objCProtocol.isaVMAddr != 0 ) { // We can't optimize this protocol if it has an ISA as we want to override it diag.error("Protocol ISA cannot be non-zero"); @@ -2935,6 +3033,40 @@ void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* }); } +void ClosureBuilder::parseObjCClassDuplicates(Map& duplicateClassesToIgnore) { + const ClosureBuilder::BuilderLoadedImage& mainLi = _loadedImages[_mainProgLoadIndex]; + + const dyld3::MachOAnalyzer* ma = mainLi.loadAddress(); + + const uint32_t pointerSize = ma->pointerSize(); + const intptr_t slide = ma->getSlide(); + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(mainLi.contentRebased); + + uint64_t sectionSize = 0; + const void* section = ma->findSectionContent("__DATA", "__objc_dupclass", sectionSize); + + if ( !section ) + return; + + // Ignore sections which are the wrong size + if ( (sectionSize % pointerSize) != 0 ) + return; + + // Copied from objc-abi.h + typedef struct _objc_duplicate_class { + uint32_t version; + uint32_t flags; + const char name[64]; + } objc_duplicate_class; + + for (uint64_t offset = 0; offset != sectionSize; offset += pointerSize) { + uint64_t vmAddr = *(uint64_t*)((uint64_t)section + offset); + vmAddr = vmAddrConverter.convertToVMAddr(vmAddr); + const objc_duplicate_class* duplicateClass = (const objc_duplicate_class*)(vmAddr + slide); + duplicateClassesToIgnore.insert({ duplicateClass->name, true }); + } +} + // used at launch by dyld when kernel has already mapped main executable const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures) { @@ -2945,12 +3077,10 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil 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 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, _archs, _platform); @@ -2960,9 +3090,36 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil _diag.error("not a main executable"); return nullptr; } + if ( _platform == Platform::macOS ) { + // If this is an iOSMac program running on macOS, switch platforms + if ( mainExecutable->builtForPlatform(Platform::iOSMac, true) ) { + //_platform = Platform::iOSMac; + Platform* selfPlatform = const_cast(&_platform); + *selfPlatform = Platform::iOSMac; + } +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + else if ( mainExecutable->builtForPlatform(Platform::iOS, true) ) { + //_platform = Platform::iOS; + Platform* selfPlatform = const_cast(&_platform); + *selfPlatform = Platform::iOS; + } +#endif + if ( mainExecutable->usesObjCGarbageCollection() ) { + _diag.error("program requires ObjC Garbage Collection which is no longer supported"); + return nullptr; + } + } + // licenseware apps that zero out lazy bind opcodes cannot be pre-bound + if ( mainExecutable->hasStompedLazyOpcodes() ) + _makeMinimalClosure = true; + _isLaunchClosure = true; _allowMissingLazies = true; +#if BUILDING_CACHE_BUILDER + _makingClosuresInCache = true; +#endif + _nextIndex = 0; // add main executable @@ -2977,7 +3134,10 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil mainEntry.isBadImage = false; mainEntry.mustBuildClosure = true; mainEntry.hasMissingWeakImports = false; + mainEntry.hasInterposingTuples = false; // only dylibs not in the dyld cache can have interposing tuples mainEntry.overrideImageNum = 0; + mainEntry.exportsTrieOffset = 0; + mainEntry.exportsTrieSize = 0; // Set the executable load path so that @executable_path can use it later _mainProgLoadPath = fileInfo.path; @@ -2989,7 +3149,8 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil if ( !findImage(dylibPath, chainMain, foundTopImage, LinkageType::kInserted, 0, true) ) { if ( !allowInsertFailures ) { if ( _diag.noError() ) - _diag.error("could not load inserted dylib %s", dylibPath); + // if no other error was reported while trying to find the library, that means it is missing + _diag.error("could not load inserted dylib '%s' because image not found", dylibPath); stop = true; return; } @@ -3023,10 +3184,22 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil // now that everything loaded, set _libDyldImageNum and _libSystemImageNum for (BuilderLoadedImage& li : _loadedImages) { - 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; + if ( mainExecutable->builtForPlatform(Platform::driverKit) ) { + if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/System/DriverKit/usr/lib/system/libdyld.dylib") == 0) ) + _libDyldImageNum = li.imageNum; + else if ( strcmp(li.path(), "/System/DriverKit/usr/lib/libSystem.dylib") == 0 ) + _libSystemImageNum = li.imageNum; + } else { + 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; + } + // don't use minimal closures when interposing is in play because we don't have runtime support to do interposing + if ( li.hasInterposingTuples ) { + _makeMinimalClosure = false; + _leaveRebasesAsOpcodes = false; + } } // only some images need to go into closure (non-rooted ones from dyld cache do not) @@ -3040,7 +3213,8 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil } } - bool optimizedObjC = optimizeObjC(writers); + // only build objc closure info when building full closures + bool optimizedObjC = !_makeMinimalClosure && optimizeObjC(writers); // Note we have to compute the init order after buildImage as buildImage may set hasInits to true for (uintptr_t imageIndex = 0, writerIndex = 0; imageIndex != _loadedImages.count(); ++imageIndex) { @@ -3096,7 +3270,7 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) { return (const MachOLoaded*)findDependent(mh, depIndex); }; - //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path()); + //fprintf(stderr, "'%s' overrides something in cache\n", li.loadedFileInfo.path); _dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* symbolName) { dyld3::MachOAnalyzer::FoundSymbol foundInfo; Diagnostics patchDiag; @@ -3110,9 +3284,32 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil 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; + // this means the symbol is missing in the cache override dylib, see it moved to a sibling + // allow patched impls to move between re-export sibling dylibs + bool foundViaParent = false; + for (const BuilderLoadedImage& li2 : _loadedImages) { + if ( (li2.overrideImageNum != 0) && (li2.imageNum != li.imageNum) ) { + for (Image::LinkedImage aDep : li2.dependents) { + if ( (aDep.kind() == Image::LinkKind::reExport) && (aDep.imageNum() == li.imageNum) ) { + if ( li2.loadAddress()->findExportedSymbol(patchDiag, symbolName, false, foundInfo, reexportFinder) ) { + const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib; + patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage; + patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum; + patch.replacement.image.offset = foundInfo.value; + foundViaParent = true; + //fprintf(stderr, "found patch target '%s' previously in '%s', now in '%s'\n", symbolName, li.path(), li2.path()); + break; + } + } + } + } + } + if ( !foundViaParent ) { + // symbol is missing from override, set other cached dylibs that used it to NULL + //fprintf(stderr, "could not find symbol '%s' in %s \n", symbolName, li.path()); + patch.replacement.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute; + patch.replacement.absolute.value = 0; + } } patches.push_back(patch); }); @@ -3120,69 +3317,35 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil } } - // 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, 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() ) + 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); +#if TARGET_OS_OSX + uint32_t progVarsOffset; + if ( mainExecutable->hasProgramVars(_diag, progVarsOffset) ) { + // on macOS binaries may have a __dyld section that has ProgramVars to use + closureWriter.setHasProgramVars(progVarsOffset); + } + if ( _diag.hasError() ) + return nullptr; #endif - // record any interposing info - imageArray->forEachImage(^(const Image* image, bool &stop) { - if ( !image->inDyldCache() ) - addInterposingTuples(closureWriter, image, findLoadedImage(image->imageNum()).loadAddress()); - }); + // record any interposing info + if ( !_interposingDisabled ) { + 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((const LaunchClosure*)closureWriter.currentTypedBytes()); // set flags + closureWriter.setUsedInterposing(_interposingTuplesUsed); closureWriter.setUsedAtPaths(_atPathUsed); closureWriter.setUsedFallbackPaths(_fallbackPathUsed); closureWriter.setHasInsertedLibraries(_mainProgLoadIndex > 0); @@ -3206,10 +3369,10 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0); // set up stack based storage for all arrays - BuilderLoadedImage loadImagesStorage[300]; + BuilderLoadedImage loadImagesStorage[256]; Image::LinkedImage dependenciesStorage[128]; Closure::PatchEntry cachePatchStorage[64]; - _loadedImages.setInitialStorage(loadImagesStorage, 300); + _loadedImages.setInitialStorage(loadImagesStorage, 256); _dependencies.setInitialStorage(dependenciesStorage, 128); _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64); ArrayFinalizer scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} }); @@ -3238,8 +3401,11 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L entry.isBadImage = false; entry.mustBuildClosure = false; entry.hasMissingWeakImports = false; + entry.hasInterposingTuples = !inDyldCache && ma->isDylib() && ma->hasInterposingTuples(); entry.overrideImageNum = 0; - if ( !inDyldCache && image->isOverrideOfDyldCacheImage(overrideImageNum) ) { + entry.exportsTrieOffset = 0; + entry.exportsTrieSize = 0; + if ( image->isOverrideOfDyldCacheImage(overrideImageNum) ) { entry.overrideImageNum = overrideImageNum; canUseSharedCacheClosure = false; } @@ -3248,7 +3414,7 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L 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) @@ -3268,13 +3434,13 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L } // add top level dylib being dlopen()ed - BuilderLoadedImage* foundTopImage; + BuilderLoadedImage* foundTopImage = nullptr; _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, LinkageType::kDynamic, 0, canUseSharedCacheClosure) ) { + LoadedImageChain chainMain = { nullptr, _loadedImages[_mainProgLoadIndex] }; + LoadedImageChain chainCaller = { &chainMain, callerImage }; + if ( !findImage(path, chainCaller, foundTopImage, LinkageType::kDynamic, 0, canUseSharedCacheClosure) ) { // If we didn't find the image, it might be a symlink to something in the dyld cache that is not on disk if ( (_dyldCache != nullptr) && !_dyldCache->header.dylibsExpectedOnDisk ) { char resolvedPath[PATH_MAX]; @@ -3329,6 +3495,11 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L // RTLD_NOW means fail the dlopen() if a symbol cannot be bound _allowMissingLazies = !forceBindLazies; + // If we got this far, we are not using a prebuilt dlopen-closure + // Since dlopen closures are never saved to disk, don't put fixups into the closure + // Except if interposing is used, since we don't have plumbing to apply interposing dynamically + _makeMinimalClosure = !mainClosure->hasInterposings(); + // only some images need to go into closure (ones from dyld cache do not, unless the cache format changed) STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count()); if ( _foundNonCachedImage || _foundDyldCacheRoots ) { @@ -3442,18 +3613,14 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const char* mainPath, boo { char realerPath[MAXPATHLEN]; closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, mainPath, _archs, _platform, realerPath); - const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent; - loadedFileInfo.path = mainPath; - if (_diag.hasError()) + if ( _diag.hasError() ) return nullptr; + loadedFileInfo.path = mainPath; + const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent; 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)->setMainExecutable(mh, mainPath); const LaunchClosure* launchClosure = makeLaunchClosure(loadedFileInfo, allowInsertFailures); loadedFileInfo.unload(loadedFileInfo); @@ -3466,7 +3633,7 @@ void ClosureBuilder::setDyldCacheInvalidFormatVersion() { // used by dyld shared cache builder -const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, const Array& dylibs, const Array& aliases) +const ImageArray* ClosureBuilder::makeDyldCacheImageArray(const Array& dylibs, const Array& 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 @@ -3476,7 +3643,6 @@ const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, co _makingDyldCacheImages = true; _allowMissingLazies = false; - _makingCustomerCache = customerCache; _aliases = &aliases; // build _loadedImages[] with every dylib in cache @@ -3493,7 +3659,10 @@ const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, co entry.isBadImage = false; entry.mustBuildClosure = false; entry.hasMissingWeakImports = false; + entry.hasInterposingTuples = false; // dylibs in dyld cache cannot have interposing tuples entry.overrideImageNum = 0; + entry.exportsTrieOffset = 0; + entry.exportsTrieSize = 0; _loadedImages.push_back(entry); } @@ -3544,18 +3713,23 @@ const ImageArray* ClosureBuilder::makeOtherDylibsImageArray(const ArraymarkNeverUnload(_diag); entry.rtldLocal = false; entry.isBadImage = false; entry.mustBuildClosure = false; entry.hasMissingWeakImports = false; + entry.hasInterposingTuples = false; // all images here have passed canHavePrecomputedDlopenClosure() which does not allow interposing tuples entry.overrideImageNum = 0; + entry.exportsTrieOffset = 0; + entry.exportsTrieSize = 0; _loadedImages.push_back(entry); } @@ -3686,7 +3860,7 @@ void ClosureBuilder::buildLoadOrderRecurse(Array& loadedList, const void ClosureBuilder::buildLoadOrder(Array& loadedList, const Array& imagesArrays, const Closure* toAdd) { - const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImage()); + const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImageNum()); loadedList.push_back(LoadedImage::make(topImage)); buildLoadOrderRecurse(loadedList, imagesArrays, topImage); } @@ -3787,5 +3961,6 @@ void ObjCClassOpt::write(const PerfectHashT& phash, const Array& targets, const Array& targetInfos); - }; + typedef void (^DylibFixupHandler)(const MachOLoaded* fixupIn, uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target); enum class AtPath { none, all, onlyInRPaths }; - ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive, + ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const RootsChecker& rootsChecker, + const DyldSharedCache* dyldCache, bool dyldCacheIsLive, const GradedArchs& archs, const PathOverrides& pathOverrides, AtPath atPathHandling=AtPath::all, bool allowRelativePaths=true, LaunchErrorInfo* errorInfo=nullptr, - Platform platform=MachOFile::currentPlatform(), const CacheDylibsBindingHandlers* handlers=nullptr); + Platform platform=MachOFile::currentPlatform(), DylibFixupHandler dylibFixupHandler=nullptr); ~ClosureBuilder(); Diagnostics& diagnostics() { return _diag; } const LaunchClosure* makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures); const LaunchClosure* makeLaunchClosure(const char* mainPath,bool allowInsertFailures); - + void makeMinimalClosures() { _makeMinimalClosure = true; } + void setCanSkipEncodingRebases() { _leaveRebasesAsOpcodes = true; } static const DlopenClosure* sRetryDlopenClosure; const DlopenClosure* makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array& loadedList, @@ -102,8 +95,10 @@ public: closure::ImageNum* topImageNum); ImageNum nextFreeImageNum() const { return _startImageNum + _nextIndex; } - + Platform platform() const { return _platform; } + void setDyldCacheInvalidFormatVersion(); + void disableInterposing() { _interposingDisabled = true; } struct PatchableExport @@ -125,20 +120,20 @@ public: const char* aliasPath; }; - const ImageArray* makeDyldCacheImageArray(bool customerCache, const Array& dylibs, const Array& aliases); + const ImageArray* makeDyldCacheImageArray(const Array& dylibs, const Array& aliases); const ImageArray* makeOtherDylibsImageArray(const Array& otherDylibs, uint32_t cachedDylibsCount); static void buildLoadOrder(Array& loadedList, const Array& imagesArrays, const Closure* toAdd); private: - + friend ClosureAnalyzerSet; struct InitInfo { - uint32_t initOrder; - bool danglingUpward; - bool visited; + uint32_t initOrder : 30, + danglingUpward : 1, + visited : 1; }; struct BuilderLoadedImage @@ -153,8 +148,11 @@ private: isBadImage : 1, mustBuildClosure : 1, hasMissingWeakImports : 1, - padding : 12, + hasInterposingTuples : 1, + padding : 11, overrideImageNum : 12; + uint32_t exportsTrieOffset; + uint32_t exportsTrieSize; LoadedFileInfo loadedFileInfo; // Convenience method to get the information from the loadedFileInfo @@ -186,36 +184,27 @@ private: uint32_t compatVersion, bool canUseSharedCacheClosure); 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 addFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage); void addChainedFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage); void addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh); void computeInitOrder(ImageWriter& writer, uint32_t loadIndex); void addClosureInfo(LaunchClosureWriter& closureWriter); void depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError); - bool findSymbol(BuilderLoadedImage& fromImage, int libraryOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, - Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo); - bool findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports, - bool weakImport, 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); + const BuilderLoadedImage& findLoadedImage(ImageNum imageNum) const; 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[]); + bool expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, bool fromLCRPATHinMain, char fixedPath[]); void addMustBeMissingPath(const char* path); void addSkippedFile(const char* path, uint64_t inode, uint64_t mtime); - const char* strdup_temp(const char* path); + const char* strdup_temp(const char* path) const; 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), - void (^missingLazyBindHandler)()); - bool findMissingSymbolHandler(Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo); + void addOperatorCachePatches(BuilderLoadedImage& forImage); + void addWeakDefCachePatch(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget); struct HashCString { static size_t hash(const char* v); @@ -283,6 +272,7 @@ private: OverflowSafeArray selectorFixups; Map selectorMap; std::optional methodNameVMOffset; + OverflowSafeArray methodListFixups; }; bool optimizeObjC(Array& writers); @@ -292,6 +282,7 @@ private: void optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt, const Map& sharedCacheImagesMap, const Map& duplicateSharedCacheClasses, + const Map& duplicateClassesToIgnore, ObjCOptimizerImage& image); void optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* objcProtocolOpt, const Map& sharedCacheImagesMap, @@ -302,38 +293,52 @@ private: const char* duplicateDefinitionPath, const char* canonicalDefinitionPath); + void parseObjCClassDuplicates(Map& duplicateClassesToIgnore); + static bool inLoadedImageArray(const Array& loadedList, ImageNum imageNum); static void buildLoadOrderRecurse(Array& loadedList, const Array& imagesArrays, const Image* toAdd); void invalidateInitializerRoots(); + Image::ResolvedSymbolTarget makeResolvedTarget(const FixupTarget& target) const; + + // MachOAnalyzerSet implementations + void mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override; + bool mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const override; + void mas_mainExecutable(WrappedMachO& anImage) const override; + void* mas_dyldCache() const override; + bool wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override; + const char* wmo_path(const WrappedMachO* image) const override; + ExportsTrie wmo_getExportsTrie(const WrappedMachO* image) const override; + bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override; + const FileSystem& _fileSystem; + const RootsChecker& _rootsChecker; const DyldSharedCache* const _dyldCache; const PathOverrides& _pathOverrides; const GradedArchs& _archs; Platform const _platform; uint32_t const _startImageNum; const ImageArray* _dyldImageArray = nullptr; - const CacheDylibsBindingHandlers* _handlers = nullptr; + DylibFixupHandler _dylibFixupHandler = nullptr; const Array* _aliases = nullptr; const AtPath _atPathHandling = AtPath::none; uint32_t _mainProgLoadIndex = 0; const char* _mainProgLoadPath = nullptr; Diagnostics _diag; LaunchErrorInfo* _launchErrorInfo = nullptr; - PathPool* _tempPaths = nullptr; + mutable PathPool* _tempPaths = nullptr; PathPool* _mustBeMissingPaths = nullptr; OverflowSafeArray _skippedFiles; uint32_t _nextIndex = 0; - OverflowSafeArray _loadedImages; + OverflowSafeArray _loadedImages; OverflowSafeArray _dependencies; // all dylibs in cache need ~20,000 edges OverflowSafeArray _interposingTuples; OverflowSafeArray _weakDefCacheOverrides; - OverflowSafeArray _weakDefsFromChainedBinds; OverflowSafeArray _objcSelectorsHashTable; OverflowSafeArray _objcSelectorsHashTableImages; OverflowSafeArray _objcClassesHashTable; OverflowSafeArray _objcProtocolsHashTable; - OverflowSafeArray _objcClassesHashTableImages; + OverflowSafeArray _objcClassesHashTableImages; OverflowSafeArray _objcClassesDuplicatesHashTable; PathPool* _objcDuplicateClassWarnings = nullptr; uint32_t _alreadyInitedIndex = 0; @@ -341,21 +346,48 @@ private: bool _makingDyldCacheImages = false; bool _dyldCacheIsLive = false; // means kernel is rebasing dyld cache content being viewed bool _makingClosuresInCache = false; - bool _makingCustomerCache = false; bool _allowRelativePaths = false; bool _atPathUsed = false; + bool _interposingTuplesUsed = false; bool _fallbackPathUsed = false; bool _allowMissingLazies = false; bool _dyldCacheInvalidFormatVersion = false; bool _foundNonCachedImage = false; // true means we have one or more images from disk we need to build closure(s) for bool _foundDyldCacheRoots = false; // true if one or more images are roots of the shared cache bool _foundMissingLazyBinds = false; // true if one or more images having missing lazy binds + bool _makeMinimalClosure = false; + bool _interposingDisabled = false; + bool _leaveRebasesAsOpcodes = false; ImageNum _libDyldImageNum = 0; ImageNum _libSystemImageNum = 0; }; +class VIS_HIDDEN RebasePatternBuilder +{ +public: + RebasePatternBuilder(OverflowSafeArray& entriesStorage, uint64_t ptrSize); + void add(uint64_t runtimeOffset); +private: + OverflowSafeArray& _rebaseEntries; + uint64_t _lastLocation; + const uint64_t _ptrSize; + + static const Image::RebasePattern _s_maxLeapPattern; + static const uint64_t _s_maxLeapCount; +}; +class VIS_HIDDEN BindPatternBuilder +{ +public: + BindPatternBuilder(OverflowSafeArray& entriesStorage, uint64_t ptrSize); + void add(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, bool weakBindCoalese); +private: + OverflowSafeArray& _bindEntries; + const uint64_t _ptrSize; + uint64_t _lastOffset; + Image::ResolvedSymbolTarget _lastTarget; +}; } // namespace closure diff --git a/dyld3/ClosureFileSystem.h b/dyld3/ClosureFileSystem.h index 3f1a7ec..2c0ed4f 100644 --- a/dyld3/ClosureFileSystem.h +++ b/dyld3/ClosureFileSystem.h @@ -39,7 +39,7 @@ struct LoadedFileInfo { uint64_t fileContentLen = 0; uint64_t sliceOffset = 0; uint64_t sliceLen : 63, - isSipProtected : 1; + isOSBinary : 1; uint64_t inode = 0; uint64_t mtime = 0; void (*unload)(const LoadedFileInfo&) = nullptr; diff --git a/dyld3/ClosureFileSystemNull.cpp b/dyld3/ClosureFileSystemNull.cpp index a5f6705..7df22bb 100644 --- a/dyld3/ClosureFileSystemNull.cpp +++ b/dyld3/ClosureFileSystemNull.cpp @@ -34,9 +34,14 @@ bool FileSystemNull::loadFile(const char* path, LoadedFileInfo& info, char reale } void FileSystemNull::unloadFile(const LoadedFileInfo& info) const { + if (info.unload) + info.unload(info); } void FileSystemNull::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const { + // 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 FileSystemNull::fileExists(const char* path, uint64_t* inode, uint64_t* mtime, diff --git a/dyld3/ClosureFileSystemPhysical.cpp b/dyld3/ClosureFileSystemPhysical.cpp index cb620b3..8a63579 100644 --- a/dyld3/ClosureFileSystemPhysical.cpp +++ b/dyld3/ClosureFileSystemPhysical.cpp @@ -23,13 +23,12 @@ #include "ClosureFileSystemPhysical.h" +#include + #include #include #include #include -#if BUILDING_UPDATE_DYLD_CACHE_BUILDER - #include -#endif #include #include #include @@ -38,6 +37,9 @@ #include #include #endif +#include +#include "MachOFile.h" +#include "MachOAnalyzer.h" using dyld3::closure::FileSystemPhysical; @@ -45,7 +47,7 @@ bool FileSystemPhysical::getRealPath(const char possiblePath[MAXPATHLEN], char r __block bool success = false; // first pass: open file and ask kernel for canonical path forEachPath(possiblePath, ^(const char* aPath, unsigned prefixLen, bool& stop) { - int fd = ::open(aPath, O_RDONLY, 0); + int fd = dyld3::open(aPath, O_RDONLY, 0); if ( fd != -1 ) { char tempPath[MAXPATHLEN]; success = (fcntl(fd, F_GETPATH, tempPath) == 0); @@ -110,6 +112,8 @@ void FileSystemPhysical::forEachPath(const char* path, void (^handler)(const cha } if ( _rootPath != nullptr ) { strlcpy(altPath, _rootPath, PATH_MAX); + if ( path[0] != '/' ) + strlcat(altPath, "/", PATH_MAX); strlcat(altPath, path, PATH_MAX); handler(altPath, (unsigned)strlen(_rootPath), stop); if ( stop ) @@ -144,9 +148,8 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r // open file __block int fd; __block struct stat statBuf; - __block bool sipProtected = false; forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) { - fd = ::open(aPath, O_RDONLY, 0); + fd = dyld3::open(aPath, O_RDONLY, 0); if ( fd == -1 ) { int openErrno = errno; if ( (openErrno == EPERM) && sandboxBlockedOpen(path) ) @@ -183,9 +186,6 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r } else strcpy(realerPath, realPathWithin); - #if BUILDING_UPDATE_DYLD_CACHE_BUILDER - sipProtected = (rootless_check_trusted_fd(fd) == 0); - #endif stop = true; } else { @@ -217,7 +217,7 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r info.fileContentLen = statBuf.st_size; info.sliceOffset = 0; info.sliceLen = statBuf.st_size; - info.isSipProtected = sipProtected; + info.isOSBinary = false; info.inode = statBuf.st_ino; info.mtime = statBuf.st_mtime; info.path = path; @@ -240,6 +240,27 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r } info.fileContent = wholeFile; + // if this is an arm64e mach-o or a fat file with an arm64e slice we need to record if it is an OS binary +#if TARGET_OS_OSX && __arm64e__ + const MachOAnalyzer* ma = (MachOAnalyzer*)wholeFile; + if ( ma->hasMachOMagic() ) { + if ( (ma->cputype == CPU_TYPE_ARM64) && ((ma->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { + if ( ma->isOSBinary(fd, 0, info.fileContentLen) ) + info.isOSBinary = true; + } + } + else if ( const FatFile* fat = FatFile::isFatFile(wholeFile) ) { + Diagnostics diag; + fat->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) { + if ( (sliceCpuType == CPU_TYPE_ARM64) && ((sliceCpuSubType & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { + uint64_t sliceOffset = (uint8_t*)sliceStart-(uint8_t*)wholeFile; + const MachOAnalyzer* sliceMA = (MachOAnalyzer*)((uint8_t*)wholeFile + sliceOffset); + if ( sliceMA->isOSBinary(fd, sliceOffset, sliceSize) ) + info.isOSBinary = true; + } + }); + } +#endif // Set unmap as the unload method. info.unload = [](const LoadedFileInfo& info) { ::munmap((void*)info.fileContent, (size_t)info.fileContentLen); @@ -257,11 +278,11 @@ void FileSystemPhysical::unloadFile(const LoadedFileInfo& info) const { 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); + ::munmap((void*)info.fileContent, (size_t)trunc_page(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))); + uintptr_t start = round_page((uintptr_t)info.fileContent + keepStartOffset + keepLength); + uintptr_t end = (uintptr_t)info.fileContent + info.fileContentLen; + ::munmap((void*)start, end - start); } info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset); info.fileContentLen = keepLength; @@ -272,7 +293,7 @@ bool FileSystemPhysical::fileExists(const char* path, uint64_t* inode, uint64_t* __block bool result = false; forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) { struct stat statBuf; - if ( ::stat(aPath, &statBuf) == 0 ) { + if ( dyld3::stat(aPath, &statBuf) == 0 ) { if (inode) *inode = statBuf.st_ino; if (mtime) diff --git a/dyld3/ClosurePrinter.cpp b/dyld3/ClosurePrinter.cpp index 48d28b4..e73c9ad 100644 --- a/dyld3/ClosurePrinter.cpp +++ b/dyld3/ClosurePrinter.cpp @@ -49,7 +49,7 @@ static std::string printTarget(const Array& imagesArrays, Ima return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend); } else - return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset); + return std::string("bind to ") + hex4(target.image.imageNum) + "-" + targetImage->leafName() + " + " + hex8(target.image.offset); break; case Image::ResolvedSymbolTarget::kindSharedCache: return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset); @@ -139,8 +139,6 @@ static const char* nameForType(TypedBytes::Type type) { return "libDyldEntry"; case TypedBytes::Type::libSystemNum: return "libSystemNum"; - case TypedBytes::Type::bootUUID: - return "bootUUID"; case TypedBytes::Type::mainEntry: return "mainEntry"; case TypedBytes::Type::startEntry: @@ -159,6 +157,8 @@ static const char* nameForType(TypedBytes::Type type) { return "warning"; case TypedBytes::Type::duplicateClassesTable: return "duplicateClassesTable"; + case TypedBytes::Type::progVars: + return "programVars"; } } @@ -207,7 +207,6 @@ static Node buildImageNode(const Image* image, const Array& i imageNode.map["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false"); imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false"); imageNode.map["has-precomputed-objc"].value = (image->hasPrecomputedObjC() ? "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() ) { @@ -284,6 +283,12 @@ static Node buildImageNode(const Image* image, const Array& i } }); + uint64_t expectedInode; + uint64_t expectedMtime; + if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) { + imageNode.map["file-inode"].value = hex(expectedInode); + imageNode.map["file-mod-time"].value = hex(expectedMtime); + } if ( printFixups ) { image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) { @@ -424,6 +429,10 @@ static Node buildImageNode(const Image* image, const Array& i imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path(); } + if ( image->inDyldCache() && image->overridableDylib() ) { + imageNode.map["overridable-dylib"].value = "true"; + } + #if 0 // add things to init before this image @@ -476,7 +485,7 @@ static Node buildClosureNode(const DlopenClosure* closure, const ArrayforEachPatchEntry(^(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["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache); patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement); root.map["dyld-cache-fixups"].array.push_back(patchNode); }); @@ -514,7 +523,7 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayforEachPatchEntry(^(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["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache); patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement); root.map["dyld-cache-fixups"].array.push_back(patchNode); }); @@ -556,14 +565,6 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayforEachPatchEntry(^(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()); // add env-vars if they exist @@ -699,6 +700,11 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayhasProgramVars(progVarsOffset) ) + root.map["program-vars-offset"].value = hex8(progVarsOffset); + #if 0 diff --git a/dyld3/ClosureWriter.cpp b/dyld3/ClosureWriter.cpp index 7347506..32c5748 100644 --- a/dyld3/ClosureWriter.cpp +++ b/dyld3/ClosureWriter.cpp @@ -312,6 +312,16 @@ void ImageWriter::setBindInfo(const Array& fixups) append(TypedBytes::Type::bindFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::BindPattern)); } +void ImageWriter::setFixupsNotEncoded() +{ + getFlags().fixupsNotEncoded = true; +} + +void ImageWriter::setRebasesNotEncoded() +{ + getFlags().rebasesNotEncoded = true; +} + void ImageWriter::setChainedFixups(uint64_t runtimeStartsStructOffset, const Array& targets) { getFlags().hasChainedFixups = true; @@ -397,6 +407,7 @@ void ImageWriter::setAsOverrideOf(ImageNum imageNum) { uint32_t temp = imageNum; append(TypedBytes::Type::imageOverride, &temp, sizeof(temp)); + getFlags().hasOverrideImageNum = true; } void ImageWriter::setInitsOrder(const ImageNum images[], uint32_t count) @@ -521,6 +532,11 @@ void LaunchClosureWriter::setUsedAtPaths(bool value) getFlags().usedAtPaths = value; } +void LaunchClosureWriter::setUsedInterposing(bool value) +{ + getFlags().usedInterposing = value; +} + void LaunchClosureWriter::setHasInsertedLibraries(bool value) { getFlags().hasInsertedLibraries = value; @@ -616,15 +632,10 @@ void LaunchClosureWriter::setDyldCacheUUID(const uuid_t uuid) append(TypedBytes::Type::dyldCacheUUID, uuid, sizeof(uuid_t)); } -void LaunchClosureWriter::setBootUUID(const char* uuid) +void LaunchClosureWriter::setHasProgramVars(uint32_t offset) { - 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); + getFlags().hasProgVars = true; + append(TypedBytes::Type::progVars, &offset, sizeof(uint32_t)); } void LaunchClosureWriter::setObjCSelectorInfo(const Array& hashTable, const Array& hashTableImages) { diff --git a/dyld3/ClosureWriter.h b/dyld3/ClosureWriter.h index 1ec0fed..9d44fa0 100644 --- a/dyld3/ClosureWriter.h +++ b/dyld3/ClosureWriter.h @@ -97,6 +97,7 @@ public: void setMappingInfo(uint64_t sliceOffset, uint64_t vmSize); void setFileInfo(uint64_t inode, uint64_t modTime); void setRebaseInfo(const Array&); + void setRebasesNotEncoded(); void setTextRebaseInfo(const Array&); void setBindInfo(const Array&); void setObjCFixupInfo(const Image::ResolvedSymbolTarget& objcProtocolClassTarget, @@ -108,6 +109,7 @@ public: void setAsOverrideOf(ImageNum); void setInitsOrder(const ImageNum images[], uint32_t count); void setChainedFixups(uint64_t runtimeStartsStructOffset, const Array& targets); + void setFixupsNotEncoded(); const Image* currentImage(); @@ -154,17 +156,18 @@ public: void setStartEntry(Image::ResolvedSymbolTarget start); void setUsedFallbackPaths(bool); void setUsedAtPaths(bool); + void setUsedInterposing(bool); void setHasInsertedLibraries(bool); void setMustBeMissingFiles(const Array& paths); void setMustExistFiles(const Array& files); void addInterposingTuples(const Array& tuples); void setDyldCacheUUID(const uuid_t); - void setBootUUID(const char* uuid); void addEnvVar(const char* envVar); void setObjCSelectorInfo(const Array& hashTable, const Array& hashTableImages); void setObjCClassAndProtocolInfo(const Array& classHashTable, const Array& protocolHashTable, const Array& hashTableImages); void setObjCDuplicateClassesInfo(const Array& hashTable); + void setHasProgramVars(uint32_t offset); private: LaunchClosure::Flags& getFlags(); diff --git a/dyld3/Defines.h b/dyld3/Defines.h new file mode 100644 index 0000000..723da1a --- /dev/null +++ b/dyld3/Defines.h @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2019 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@ +*/ + +// For now just for compact info, but in the long run we can use this to centralize convenience macros and configuration options + +#ifndef DYLD_DEFINES_H +#define DYLD_DEFINES_H + +#define VIS_HIDDEN __attribute__((visibility("hidden"))) + +#ifndef BUILD_FOR_TESTING +#define BUILD_FOR_TESTING 0 +#endif + +#endif /* DYLD_DEFINES_H */ diff --git a/dyld3/Diagnostics.cpp b/dyld3/Diagnostics.cpp index 647053e..234f36c 100644 --- a/dyld3/Diagnostics.cpp +++ b/dyld3/Diagnostics.cpp @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -95,9 +97,9 @@ void Diagnostics::error(const char* format, va_list list) return; if (_prefix.empty()) { - fprintf(stderr, "%s", _simple_string(_buffer)); + fprintf(stderr, "%s\n", _simple_string(_buffer)); } else { - fprintf(stderr, "[%s] %s", _prefix.c_str(), _simple_string(_buffer)); + fprintf(stderr, "[%s] %s\n", _prefix.c_str(), _simple_string(_buffer)); } #endif } @@ -125,6 +127,14 @@ void Diagnostics::assertNoError() const abort_report_np("%s", _simple_string(_buffer)); } +bool Diagnostics::errorMessageContains(const char* subString) const +{ + if ( _buffer == nullptr ) + return false; + return (strstr(_simple_string(_buffer), subString) != nullptr); +} + + #if !BUILDING_CACHE_BUILDER const char* Diagnostics::errorMessage() const { @@ -213,5 +223,53 @@ void Diagnostics::clearWarnings() #endif } +#if BUILDING_CACHE_BUILDER +void TimeRecorder::pushTimedSection() { + openTimings.push_back(mach_absolute_time()); +} + +void TimeRecorder::recordTime(const char* format, ...) { + uint64_t t = mach_absolute_time(); + uint64_t previousTime = openTimings.back(); + openTimings.pop_back(); + + char* output_string = nullptr; + va_list list; + va_start(list, format); + vasprintf(&output_string, format, list); + va_end(list); + + if (output_string != nullptr) { + timings.push_back(TimingEntry { + .time = t - previousTime, + .logMessage = std::string(output_string), + .depth = (int)openTimings.size() + }); + } + + openTimings.push_back(mach_absolute_time()); +} + +void TimeRecorder::popTimedSection() { + openTimings.pop_back(); +} + +static inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + +void TimeRecorder::logTimings() { + for (const TimingEntry& entry : timings) { + for (int i = 0 ; i < entry.depth ; i++) { + std::cerr << " "; + } + std::cerr << "time to " << entry.logMessage << " " << absolutetime_to_milliseconds(entry.time) << "ms" << std::endl; + } + + timings.clear(); +} +#endif + #endif diff --git a/dyld3/Diagnostics.h b/dyld3/Diagnostics.h index 21cdf41..b7b5e51 100644 --- a/dyld3/Diagnostics.h +++ b/dyld3/Diagnostics.h @@ -57,6 +57,7 @@ public: bool noError() const; void clearError(); void assertNoError() const; + bool errorMessageContains(const char* subString) const; #if !BUILDING_CACHE_BUILDER const char* errorMessage() const; @@ -68,6 +69,7 @@ public: #endif private: + void* _buffer = nullptr; #if BUILDING_CACHE_BUILDER std::string _prefix; @@ -76,7 +78,37 @@ private: #endif }; +#if BUILDING_CACHE_BUILDER +class VIS_HIDDEN TimeRecorder +{ +public: + // Call pushTimedSection(), then mark events with recordTime. Call popTimedSection() to stop the current timing session. + // This is stack-based, so you can start a sub-timer with pushTimedSection() / recordTime / recordTime... / popTimedSection() + // inside a first timed section. + // Call logTimings() to print everything. + // Start a new timed section. + void pushTimedSection(); + + // Records the time taken since the last pushTimedSection() / recordTime() at the current level + void recordTime(const char* format, ...); + + // Stop the current timed section and pop back one level. + void popTimedSection(); + + void logTimings(); +private: + struct TimingEntry { + uint64_t time; + std::string logMessage; + int depth; + }; + + std::vector openTimings; + std::vector timings; +}; + +#endif /* BUILDING_CACHE_BUILDER */ #endif // Diagnostics_h diff --git a/dyld3/JSON.h b/dyld3/JSON.h index 5c98739..aea4de6 100644 --- a/dyld3/JSON.h +++ b/dyld3/JSON.h @@ -30,18 +30,59 @@ #include #include +#include #include namespace dyld3 { namespace json { +enum class NodeValueType { + Default, + String, + RawValue, +}; + struct Node { + NodeValueType type = NodeValueType::Default; std::string value; std::map map; std::vector array; + + inline Node() + : type(NodeValueType::Default), value(), map(), array() { } + + inline Node(std::string string) + : type(NodeValueType::String), value(string), map(), array() { } + + inline Node(const char *string) : Node(std::string{string}) { } + + inline Node(bool b) + : type(NodeValueType::RawValue), value(b ? "true" : "false") + , map(), array() { } + + inline Node(int64_t i64) + : type(NodeValueType::RawValue), value(), map(), array() + { + std::ostringstream os{}; + os << i64; + value = os.str(); + } + + inline Node(uint64_t u64) + : type(NodeValueType::RawValue), value(), map(), array() + { + std::ostringstream os{}; + os << u64; + value = os.str(); + } }; +static inline Node makeNode(std::string value) { + Node node; + node.value = value; + return node; +} } // namespace json } // namespace dyld3 diff --git a/dyld3/JSONReader.h b/dyld3/JSONReader.h index f8725dd..739c544 100644 --- a/dyld3/JSONReader.h +++ b/dyld3/JSONReader.h @@ -34,6 +34,7 @@ namespace dyld3 { namespace json { Node readJSON(Diagnostics& diags, const char* filePath); +Node readJSON(Diagnostics& diags, const void* contents, size_t length); // Given a map node, returns the node representing the given value. // If it is missing, returns a sentinel node and sets an error on the diagnostic @@ -46,6 +47,9 @@ const Node* getOptionalValue(Diagnostics& diags, const Node& node, const char* k // Parses an int from the given node, or throws an error if its not an integer payload uint64_t parseRequiredInt(Diagnostics& diags, const Node& node); +// Parses a bool from the given node, or throws an error if its not a boolean payload +bool parseRequiredBool(Diagnostics& diags, const Node& node); + // Parses a string from the given node, or throws an error if its not a string payload const std::string& parseRequiredString(Diagnostics& diags, const Node& node); diff --git a/dyld3/JSONReader.mm b/dyld3/JSONReader.mm index 2f422d7..62b9281 100644 --- a/dyld3/JSONReader.mm +++ b/dyld3/JSONReader.mm @@ -94,6 +94,33 @@ uint64_t parseRequiredInt(Diagnostics& diags, const Node& node) { return atoi(node.value.c_str()); } +bool parseRequiredBool(Diagnostics& diags, const Node& node) { + if (diags.hasError()) + return false; + + if (!node.array.empty()) { + diags.error("Cannot get integer value from array node\n"); + return false; + } + if (!node.map.empty()) { + diags.error("Cannot get integer value from value node\n"); + return false; + } + if (node.value.empty()) { + diags.error("Cannot get integer value from empty node\n"); + return false; + } + + if ( (node.value == "true") || (node.value == "1") ) + return true; + + if ( (node.value == "false") || (node.value == "0") ) + return false; + + diags.error("Boolean value should be true/false/0/1\n"); + return false; +} + const std::string& parseRequiredString(Diagnostics& diags, const Node& node) { static std::string sentinelString = ""; @@ -168,6 +195,12 @@ Node parseNode(Diagnostics& diags, id jsonObject) { return node; } + // NSBoolean -> string + if ([jsonObject isKindOfClass:[NSNumber class]]) { + node.value = [[(NSNumber*)jsonObject stringValue] UTF8String]; + return node; + } + diags.error("Unknown json deserialized type\n"); return Node(); } @@ -193,5 +226,17 @@ Node readJSON(Diagnostics& diags, const char* filePath) { return node; } +Node readJSON(Diagnostics& diags, const void * contents, size_t length) { + NSData* data = [NSData dataWithBytes:contents length:length]; + NSError* error = nil; + id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; + if (!jsonObject) { + diags.error("Could not deserialize json because '%s'\n", [[error localizedFailureReason] UTF8String]); + return Node(); + } + + return parseNode(diags, jsonObject); +} + } //namespace json } //namespace dyld3 diff --git a/dyld3/JSONWriter.h b/dyld3/JSONWriter.h index 788ffae..b473155 100644 --- a/dyld3/JSONWriter.h +++ b/dyld3/JSONWriter.h @@ -61,7 +61,7 @@ static inline void indentBy(uint32_t spaces, std::ostream& out) { } } -static inline void printJSON(const Node& node, uint32_t indent, std::ostream& out) +static inline void printJSON(const Node& node, uint32_t indent = 0, std::ostream& out = std::cout) { if ( !node.map.empty() ) { out << "{"; @@ -95,19 +95,54 @@ static inline void printJSON(const Node& node, uint32_t indent, std::ostream& ou out << "]"; } else { - std::string escapedString; - escapedString.reserve(node.value.size()); - for (char c : node.value) { - if (c == '"') - escapedString += '\\'; - escapedString += c; + auto &value = node.value; + switch (node.type) { + case NodeValueType::Default: + case NodeValueType::String: + if (value.find('"') == std::string::npos) { + out << "\"" << value << "\""; + } else { + std::string escapedString; + escapedString.reserve(value.size()); + for (char c : value) { + if (c == '"') + escapedString += '\\'; + escapedString += c; + } + out << "\"" << escapedString << "\""; + } + break; + case NodeValueType::RawValue: + out << value; + break; } - out << "\"" << escapedString << "\""; } if ( indent == 0 ) out << "\n"; } +static inline void streamArrayBegin(bool &needsComma, std::ostream& out = std::cout) +{ + out << "["; + needsComma = false; +} + +static inline void streamArrayNode(bool &needsComma, Node &node, std::ostream& out = std::cout) +{ + if (needsComma) + out << ","; + out << "\n"; + indentBy(2, out); + printJSON(node, 2, out); + needsComma = true; +} + +static inline void streamArrayEnd(bool &needsComma, std::ostream& out = std::cout) +{ + if (needsComma) + out << "\n"; + out << "]\n"; +} } // namespace json } // namespace dyld3 diff --git a/dyld3/Loading.cpp b/dyld3/Loading.cpp index 895cb09..ffc92e5 100644 --- a/dyld3/Loading.cpp +++ b/dyld3/Loading.cpp @@ -46,14 +46,17 @@ //#include #include +#include "ClosureFileSystemPhysical.h" #include "MachOFile.h" #include "MachOLoaded.h" #include "MachOAnalyzer.h" #include "Logging.h" #include "Loading.h" +#include "RootsChecker.h" #include "Tracing.h" #include "dyld2.h" #include "dyld_cache_format.h" +#include "libdyldEntryVector.h" #include "objc-shared-cache.h" @@ -103,11 +106,15 @@ namespace dyld3 { Loader::Loader(const Array& existingImages, Array& newImagesStorage, const void* cacheAddress, const Array& imagesArrays, const closure::ObjCSelectorOpt* selOpt, const Array& selImages, - LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs) + const RootsChecker& rootsChecker, dyld3::Platform platform, + LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDof, + bool allowMissingLazies, dyld3::LaunchErrorInfo* launchErrorInfo) : _existingImages(existingImages), _newImages(newImagesStorage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress), _dyldCacheSelectorOpt(nullptr), _closureSelectorOpt(selOpt), _closureSelectorImages(selImages), - _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs) + _rootsChecker(rootsChecker), _allowMissingLazies(allowMissingLazies), _platform(platform), + _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDof), _launchErrorInfo(launchErrorInfo) + { #if BUILDING_DYLD // This is only needed for dyld and the launch closure, not the dlopen closures @@ -122,7 +129,7 @@ void Loader::addImage(const LoadedImage& li) _newImages.push_back(li); } -LoadedImage* Loader::findImage(closure::ImageNum targetImageNum) +LoadedImage* Loader::findImage(closure::ImageNum targetImageNum) const { #if BUILDING_DYLD // The launch images are different in dyld vs libdyld. In dyld, the new images are @@ -131,7 +138,7 @@ LoadedImage* Loader::findImage(closure::ImageNum targetImageNum) return info; } - for (uint64_t index = 0; index != _newImages.count(); ++index) { + for (uintptr_t index = 0; index != _newImages.count(); ++index) { LoadedImage& info = _newImages[index]; if ( info.image()->representsImageNum(targetImageNum) ) { // Try cache this entry for next time @@ -180,11 +187,16 @@ uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target) void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden) { - // accumulate all image overrides - STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount()); + bool iOSonMac = (_platform == Platform::iOSMac); +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + if ( _platform == Platform::iOS ) + iOSonMac = true; +#endif + // accumulate all image overrides (512 is placeholder for max unzippered twins in dyld cache) + STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount() + 512); for (const auto anArray : _imagesArrays) { - // ignore prebuilt Image* in dyld cache - if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum ) + // ignore prebuilt Image* in dyld cache, except for MacCatalyst apps where unzipped twins can override each other + if ( (anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum) && !iOSonMac ) continue; anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) { ImageOverride overrideEntry; @@ -207,7 +219,7 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri uintptr_t index = 0; while ( (index < _newImages.count()) && diag.noError() ) { const closure::Image* image = _newImages[index].image(); - //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path()); + //dyld::log("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) { @@ -221,7 +233,7 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri // 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()); + //dyld::log(" load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path()); if ( _newImages.freeCount() == 0 ) { diag.error("too many initial images"); stop = true; @@ -241,8 +253,11 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri } } -void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI) +void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, bool* closureOutOfDate, bool* recoverable) { + *closureOutOfDate = false; + *recoverable = true; + // scan array and map images not already loaded for (LoadedImage& info : _newImages) { if ( info.loadedAddress() != nullptr ) { @@ -267,21 +282,21 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from 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) ) { + if ( dyld3::stat(info.image()->path(), &statBuf) == 0 ) { + dyld3::closure::FileSystemPhysical fileSystem; + if ( _rootsChecker.onDiskFileIsRoot(info.image()->path(), (const DyldSharedCache*)_dyldCacheAddress, info.image(), + &fileSystem, statBuf.st_ino, statBuf.st_mtime) ) { + if ( ((const DyldSharedCache*)_dyldCacheAddress)->header.dylibsExpectedOnDisk ) { 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 { - diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path()); + *closureOutOfDate = true; } } else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) { diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path()); + *closureOutOfDate = true; } } if ( diag.noError() ) { @@ -297,28 +312,28 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from } } else { - mapImage(diag, info, fromOFI); + mapImage(diag, info, fromOFI, closureOutOfDate); 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 : _newImages) { - if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) { - _logSegments("dyld: unmapping %s\n", info.image()->path()); - unmapImage(info); - } - } + if ( diag.hasError() ) { + // need to clean up by unmapping any images just mapped + unmapAllImages(); return; } - // apply fixups + // apply fixups to all but main executable + LoadedImage* mainInfo = nullptr; for (LoadedImage& info : _newImages) { // images in shared cache do not need fixups applied if ( info.image()->inDyldCache() ) continue; + if ( info.loadedAddress()->filetype == MH_EXECUTE ) { + mainInfo = &info; + continue; + } // previously loaded images were previously fixed up if ( info.state() < LoadedImage::State::fixedUp ) { applyFixupsToImage(diag, info); @@ -327,6 +342,26 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from info.setState(LoadedImage::State::fixedUp); } } + if ( diag.hasError() ) { + // need to clean up by unmapping any images just mapped + unmapAllImages(); + return; + } + + if ( mainInfo != nullptr ) { + // now apply fixups to main executable + // we do it in this order so that if there is a problem with the dylibs in the closure + // the main executable is left untouched so the closure can be rebuilt + applyFixupsToImage(diag, *mainInfo); + if ( diag.hasError() ) { + // need to clean up by unmapping any images just mapped + unmapAllImages(); + // we have already started fixing up the main executable, so we cannot retry the launch again + *recoverable = false; + return; + } + mainInfo->setState(LoadedImage::State::fixedUp); + } // find and register dtrace DOFs if ( processDOFs ) { @@ -344,6 +379,18 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from } } +void Loader::unmapAllImages() +{ + for (LoadedImage& info : _newImages) { + if ( !info.image()->inDyldCache() && !info.leaveMapped() ) { + if ( (info.state() == LoadedImage::State::mapped) || (info.state() == LoadedImage::State::fixedUp) ) { + _logSegments("dyld: unmapping %s\n", info.image()->path()); + unmapImage(info); + } + } + } +} + bool Loader::sandboxBlocked(const char* path, const char* kind) { #if TARGET_OS_SIMULATOR || TARGET_OS_DRIVERKIT @@ -370,7 +417,7 @@ bool Loader::sandboxBlockedStat(const char* path) return sandboxBlocked(path, "file-read-metadata"); } -void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) +void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI, bool* closureOutOfDate) { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0); @@ -382,11 +429,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) bool isCodeSigned = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize); // open file -#if BUILDING_DYLD - int fd = dyld::my_open(info.image()->path(), O_RDONLY, 0); -#else - int fd = ::open(info.image()->path(), O_RDONLY, 0); -#endif + int fd = dyld3::open(info.image()->path(), O_RDONLY, 0); if ( fd == -1 ) { int openErr = errno; if ( (openErr == EPERM) && sandboxBlockedOpen(image->path()) ) @@ -399,7 +442,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) // get file info struct stat statBuf; #if TARGET_OS_SIMULATOR - if ( stat(image->path(), &statBuf) != 0 ) { + if ( dyld3::stat(image->path(), &statBuf) != 0 ) { #else if ( fstat(fd, &statBuf) != 0 ) { #endif @@ -418,6 +461,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) 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()); + *closureOutOfDate = true; close(fd); return; } @@ -433,6 +477,23 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) } } + if ( isCodeSigned && (sliceOffset == 0) ) { + uint64_t expectedFileSize = round_page_kernel(codeSignFileOffset+codeSignFileSize); + uint64_t actualFileSize = round_page_kernel(statBuf.st_size); + if ( actualFileSize < expectedFileSize ) { + diag.error("File size too small for code signature"); + *closureOutOfDate = true; + close(fd); + return; + } + if ( actualFileSize != expectedFileSize ) { + diag.error("File size doesn't match code signature"); + *closureOutOfDate = true; + close(fd); + return; + } + } + // register code signature uint64_t coveredCodeLength = UINT64_MAX; if ( isCodeSigned ) { @@ -447,6 +508,13 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) 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()); +#if BUILDING_LIBDYLD + if ( errnoCopy == EBADEXEC ) { + // dlopen closures many be prebuilt in to the shared cache with a code signature, but the dylib is replaced + // with one without a code signature. In that case, lets build a new closure + *closureOutOfDate = true; + } +#endif } else { diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'", @@ -603,6 +671,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) } } if ( diag.hasError() ) { + *closureOutOfDate = true; ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); return; } @@ -610,7 +679,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI) #endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if (__arm__ || __arm64__) && !TARGET_OS_SIMULATOR // tell kernel about fairplay encrypted regions uint32_t fpTextOffset; uint32_t fpSize; @@ -644,7 +713,7 @@ void Loader::registerDOFs(const Array& dofs) if ( dofs.empty() ) return; - int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + int fd = ::open("/dev/" DTRACEMNR_HELPER, O_RDWR); if ( fd < 0 ) { _logDofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n"); } @@ -682,18 +751,18 @@ void Loader::registerDOFs(const Array& dofs) bool Loader::dtraceUserProbesEnabled() { -#ifdef DARLING - return false; -#else +#if !TARGET_OS_SIMULATOR uint8_t dofEnabled = *((uint8_t*)_COMM_PAGE_DTRACE_DOF_ENABLED); return ( (dofEnabled & 1) ); +#else + return false; #endif } void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger) { -#if __arm__ || __arm64__ +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // 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; @@ -704,6 +773,21 @@ void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger) #endif } +static const char* targetString(const MachOAnalyzerSet::FixupTarget& target) +{ + switch (target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + return "rebase"; + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + return "abolute"; + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + return target.foundSymbolName; + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + return "missing"; + } + return ""; +} + void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info) { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0); @@ -716,93 +800,192 @@ void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info) if ( overrideOfCache ) vmAccountingSetSuspended(true, _logFixups); - image->forEachFixup(^(uint64_t imageOffsetToRebase, bool& stop) { - // this is a rebase, add slide - 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) { - // this is a bind, set to target - 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 imageOffsetToStartsInfo, const Array& targets, bool& stop) { - // this is a chain of fixups, fix up all - STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128); - targetAddrs.reserve(targets.count()); - for (uint32_t i=0; i < targets.count(); ++i) - targetAddrs.push_back((void*)resolveTarget(targets[i])); - ((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) { - info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) { - _logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue); + if ( image->fixupsNotEncoded() ) { + // make the cache writable for this block + // We do this lazily, only if we find a symbol which needs to be overridden + DyldSharedCache::DataConstLazyScopedWriter patcher((const DyldSharedCache*)_dyldCacheAddress, mach_task_self(), (DyldSharedCache::DataConstLogFunc)_logSegments); + auto* patcherPtr = &patcher; + + WrappedMachO wmo((MachOAnalyzer*)info.loadedAddress(), this, (void*)info.image()); + wmo.forEachFixup(diag, + ^(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const FixupTarget& target, bool& stop) { + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + fixupLocRuntimeOffset); + uintptr_t value; + switch ( target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + value = (uintptr_t)(target.foundInImage._mh) + target.offsetInImage; + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + value = (uintptr_t)target.offsetInImage; + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + if ( _launchErrorInfo ) { + _launchErrorInfo->kind = DYLD_EXIT_REASON_SYMBOL_MISSING; + _launchErrorInfo->clientOfDylibPath = info.image()->path(); + _launchErrorInfo->targetDylibPath = target.foundInImage.path(); + _launchErrorInfo->symbol = target.requestedSymbolName; + } + // we have no value to set, and forEachFixup() is about to finish + return; + } + #if __has_feature(ptrauth_calls) + if ( pmd.authenticated ) + value = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(value, fixUpLoc, pmd.usesAddrDiversity, pmd.diversity, pmd.key); + #endif + if ( pmd.high8 ) + value |= ((uint64_t)pmd.high8 << 56); + _logFixups("dyld: fixup: %s:%p = %p (%s)\n", leafName, fixUpLoc, (void*)value, targetString(target)); + *fixUpLoc = value; + }, + ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target) { +#if BUILDING_LIBDYLD && __x86_64__ + // Full dlopen closures don't patch weak defs. Bail out early if we are libdyld to match this behaviour + return; +#endif + patcherPtr->makeWriteable(); + ((const DyldSharedCache*)_dyldCacheAddress)->forEachPatchableUseOfExport(cachedDylibIndex, exportCacheOffset, ^(dyld_cache_patchable_location patchLoc) { + uintptr_t* loc = (uintptr_t*)(((uint8_t*)_dyldCacheAddress)+patchLoc.cacheOffset); + uintptr_t newImpl = (uintptr_t)(target.foundInImage._mh) + target.offsetInImage + DyldSharedCache::getAddend(patchLoc); + #if __has_feature(ptrauth_calls) + if ( patchLoc.authenticated ) + newImpl = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(newImpl, loc, patchLoc.usesAddressDiversity, patchLoc.discriminator, patchLoc.key); + #endif + // ignore duplicate patch entries + if ( *loc != newImpl ) { + _logFixups("dyld: cache patch: %p = 0x%0lX\n", loc, newImpl); + *loc = newImpl; + } }); }); - }, - ^(uint64_t imageOffsetToFixup) { - uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); - _logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName); - - MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc; - ((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized; - }, - ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) { - // this is a bind, set to target - uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind); - uintptr_t value = resolveTarget(bindTarget); - _logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); - *fixUpLoc = value; - }, - ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) { - // fixupObjCSelRefs - closure::Image::ResolvedSymbolTarget fixupTarget; - if ( inSharedCache ) { - const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex); - fixupTarget.sharedCache.kind = closure::Image::ResolvedSymbolTarget::kindSharedCache; - fixupTarget.sharedCache.offset = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress; - } else { - closure::ImageNum imageNum; - uint64_t vmOffset; - bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset); - assert(gotLocation); - fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage; - fixupTarget.image.imageNum = imageNum; - fixupTarget.image.offset = vmOffset; +#if BUILDING_LIBDYLD && TARGET_OS_OSX + // support old licenseware plugins on macOS using minimal closures + __block bool oldBinary = true; + info.loadedAddress()->forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + if ( (platform == Platform::macOS) && (sdk >= 0x000A0F00) ) + oldBinary = false; + }); + if ( oldBinary ) { + // look for __DATA,__dyld section + info.loadedAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) { + // dyld_func_lookup is second pointer in __dyld section + uintptr_t* dyldSection = (uintptr_t*)(sectInfo.sectAddr + (uintptr_t)info.loadedAddress()); + _logFixups("dyld: __dyld section: %p = %p\n", &dyldSection[1], &dyld3::compatFuncLookup); + dyldSection[1] = (uintptr_t)&dyld3::compatFuncLookup; + } + }); } +#endif + } + else { + if ( image->rebasesNotEncoded() ) { + // some apps have so many rebases the closure file is too big, instead we go back to rebase opcodes + ((MachOAnalyzer*)imageLoadAddress)->forEachRebase(diag, true, ^(uint64_t imageOffsetToRebase, bool& stop) { + // this is a rebase, add slide + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase); + *fixUpLoc += slide; + _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide); + }); + } + image->forEachFixup(^(uint64_t imageOffsetToRebase, bool& stop) { + // this is a rebase, add slide + 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) { + // this is a bind, set to target + 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 imageOffsetToStartsInfo, const Array& targets, bool& stop) { + // this is a chain of fixups, fix up all + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128); + targetAddrs.reserve(targets.count()); + for (uint32_t i=0; i < targets.count(); ++i) + targetAddrs.push_back((void*)resolveTarget(targets[i])); + ((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) { + info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) { + _logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue); + }); + }); + }, + ^(uint64_t imageOffsetToFixup) { + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); + _logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName); - uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); - uintptr_t value = resolveTarget(fixupTarget); - _logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value); - *fixUpLoc = value; - }, ^(uint64_t imageOffsetToFixup, bool &stop) { - // fixupObjCStableSwift - // Class really is stable Swift, pretending to be pre-stable. - // Fix its lie. - uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); - uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY; - _logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); - *fixUpLoc = value; - }, ^(uint64_t imageOffsetToFixup, bool &stop) { - // TODO: Implement this - }); + MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc; + ((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized; + }, + ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) { + // this is a bind, set to target + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind); + uintptr_t value = resolveTarget(bindTarget); +#if __has_feature(ptrauth_calls) + // Sign the ISA on arm64e. + // Unfortunately a hard coded value here is not ideal, but this is ABI so we aren't going to change it + // This matches the value in libobjc __objc_opt_ptrs: .quad x@AUTH(da, 27361, addr) + value = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(value, fixUpLoc, true, 27361, 2); +#endif + _logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); + *fixUpLoc = value; + }, + ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) { + // fixupObjCSelRefs + closure::Image::ResolvedSymbolTarget fixupTarget; + if ( inSharedCache ) { + const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex); + fixupTarget.sharedCache.kind = closure::Image::ResolvedSymbolTarget::kindSharedCache; + fixupTarget.sharedCache.offset = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress; + } else { + closure::ImageNum imageNum; + uint64_t vmOffset; + bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset); + assert(gotLocation); + fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage; + fixupTarget.image.imageNum = imageNum; + fixupTarget.image.offset = vmOffset; + } + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); + uintptr_t value = resolveTarget(fixupTarget); + _logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value); + *fixUpLoc = value; + }, ^(uint64_t imageOffsetToFixup, bool &stop) { + // fixupObjCStableSwift + // Class really is stable Swift, pretending to be pre-stable. + // Fix its lie. + uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); + uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY; + _logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); + *fixUpLoc = value; + }, ^(uint64_t imageOffsetToFixup, bool &stop) { + // fixupObjCMethodList + // Set the method list to have the uniqued bit set + uint32_t* fixUpLoc = (uint32_t*)(imageLoadAddress + imageOffsetToFixup); + uint32_t value = (*fixUpLoc) | MachOAnalyzer::ObjCMethodList::methodListIsUniqued; + _logFixups("dyld: fixup objc method list: %s:%p = 0x%08x\n", leafName, fixUpLoc, value); + *fixUpLoc = value; + }); #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); + __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 + } // make any read-only data segments read-only if ( image->hasReadOnlyData() && !image->inDyldCache() ) { @@ -832,13 +1015,112 @@ void Loader::setSegmentProtects(const LoadedImage& info, bool write) } #endif + +void Loader::forEachImage(void (^handler)(const LoadedImage& li, bool& stop)) const +{ + bool stop = false; + for (const LoadedImage& li : _existingImages) { + handler(li, stop); + if ( stop ) + return; + } + for (const LoadedImage& li : _newImages) { + handler(li, stop); + if ( stop ) + return; + } +} + +void Loader::mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const +{ + forEachImage(^(const LoadedImage& li, bool& stop) { + WrappedMachO wmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image()); + handler(wmo, li.hideFromFlatSearch(), stop); + }); +} + + +bool Loader::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const +{ + if ( weakImport ) { + target.offsetInImage = 0; + target.kind = FixupTarget::Kind::bindAbsolute; + return true; + } + + if ( lazyBind && _allowMissingLazies ) { + __block bool result = false; + forEachImage(^(const LoadedImage& li, bool& stop) { + if ( li.loadedAddress()->isDylib() && (strcmp(li.loadedAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) { + WrappedMachO libdyldWmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image()); + Diagnostics diag; + if ( libdyldWmo.findSymbolIn(diag, "__dyld_missing_symbol_abort", 0, target) ) { + // closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace + result = true; + } + stop = true; + } + }); + return result; + } + + // FIXME + return false; +} + + +void Loader::mas_mainExecutable(WrappedMachO& mainWmo) const +{ + forEachImage(^(const LoadedImage& li, bool& stop) { + if ( li.loadedAddress()->isMainExecutable() ) { + WrappedMachO wmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image()); + mainWmo = wmo; + stop = true; + } + }); +} + +void* Loader::mas_dyldCache() const +{ + return (void*)_dyldCacheAddress; +} + + +bool Loader::wmo_dependent(const WrappedMachO* wmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const +{ + const closure::Image* image = (closure::Image*)(wmo->_other); + closure::ImageNum depImageNum = image->dependentImageNum(depIndex); + if ( depImageNum == closure::kMissingWeakLinkedImage ) { + missingWeakDylib = true; + return true; + } + else { + if ( LoadedImage* li = findImage(depImageNum) ) { + WrappedMachO foundWmo((MachOAnalyzer*)li->loadedAddress(), this, (void*)li->image()); + missingWeakDylib = false; + childWmo = foundWmo; + return true; + } + } + return false; +} + + +const char* Loader::wmo_path(const WrappedMachO* wmo) const +{ + const closure::Image* image = (closure::Image*)(wmo->_other); + return image->path(); +} + + + #if BUILDING_DYLD LoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum, Array& images) const { if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) ) return nullptr; - uint64_t cacheIndex = imageNum - _firstImageNum; + unsigned int cacheIndex = imageNum - _firstImageNum; uint32_t imagesIndex = _imageIndices[cacheIndex]; if ( imagesIndex == 0 ) return nullptr; @@ -847,16 +1129,17 @@ LoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum, return &images[imagesIndex - 1]; } -void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum, - uint64_t allImagesIndex) { +void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum, uint64_t allImagesIndex) const { if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) ) return; - uint64_t cacheIndex = imageNum - _firstImageNum; + unsigned int cacheIndex = imageNum - _firstImageNum; // Note the index is offset by 1 so that 0's are not yet set _imageIndices[cacheIndex] = (uint32_t)allImagesIndex + 1; } + #endif + void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop)) { @@ -880,7 +1163,7 @@ void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler) void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop)) { - int fd = dyld::my_open(path, O_RDONLY, 0); + int fd = dyld3::open(path, O_RDONLY, 0); if ( fd != -1 ) { struct stat statBuf; if ( fstat(fd, &statBuf) == 0 ) { @@ -894,14 +1177,13 @@ void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, b } } -#endif #if (BUILDING_LIBDYLD || BUILDING_DYLD) bool internalInstall() { #if TARGET_OS_SIMULATOR return false; -#elif __IPHONE_OS_VERSION_MIN_REQUIRED +#elif TARGET_OS_IPHONE uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM); return ( (devFlags & 1) == 1 ); #else diff --git a/dyld3/Loading.h b/dyld3/Loading.h index 5cb7cd1..8baece4 100644 --- a/dyld3/Loading.h +++ b/dyld3/Loading.h @@ -33,6 +33,7 @@ #include "Closure.h" #include "MachOLoaded.h" +#include "MachOAnalyzerSet.h" namespace objc_opt { struct objc_clsopt_t; @@ -41,6 +42,15 @@ struct objc_selopt_t; namespace dyld3 { +class RootsChecker; + +struct LaunchErrorInfo +{ + uintptr_t kind; + const char* clientOfDylibPath; + const char* targetDylibPath; + const char* symbol; +}; // // Tuple of info about a loaded image. Contains the loaded address, Image*, and state. @@ -89,20 +99,23 @@ private: // // Utility class to recursively load dependents // -class VIS_HIDDEN Loader { +class VIS_HIDDEN Loader : public MachOAnalyzerSet { public: typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2))); Loader(const Array& existingImages, Array& newImagesStorage, const void* cacheAddress, const Array& imagesArrays, const closure::ObjCSelectorOpt* selOpt, const Array& selImages, - LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs); + const RootsChecker& rootsChecker, dyld3::Platform platform, + LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs, + bool allowMissingLazies=false, dyld3::LaunchErrorInfo* launchErrorInfo=nullptr); void addImage(const LoadedImage&); void completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden); - void mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI=false); + void mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, bool* closureOutOfDate, bool* recoverable); uintptr_t resolveTarget(closure::Image::ResolvedSymbolTarget target); - LoadedImage* findImage(closure::ImageNum targetImageNum); + LoadedImage* findImage(closure::ImageNum targetImageNum) const; + void forEachImage(void (^handler)(const LoadedImage& li, bool& stop)) const; static void unmapImage(LoadedImage& info); static bool dtraceUserProbesEnabled(); @@ -126,7 +139,7 @@ private: struct LaunchImagesCache { LoadedImage* findImage(closure::ImageNum targetImageNum, Array& images) const; - void tryAddImage(closure::ImageNum targetImageNum, uint64_t allImagesIndex); + void tryAddImage(closure::ImageNum targetImageNum, uint64_t allImagesIndex) const; static const uint64_t _cacheSize = 128; static const closure::ImageNum _firstImageNum = closure::kFirstLaunchClosureImageNum; @@ -135,11 +148,11 @@ private: // Note, the cache stores "indices + 1" into the _allImages array. // 0 means we haven't cached an entry yet uint32_t _cacheStorage[_cacheSize] = { 0 }; - Array _imageIndices = { &_cacheStorage[0], _cacheSize, _cacheSize }; + mutable Array _imageIndices = { &_cacheStorage[0], _cacheSize, _cacheSize }; }; #endif - void mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI); + void mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI, bool* closureOutOfDate); void applyFixupsToImage(Diagnostics& diag, LoadedImage& info); void registerDOFs(const Array& dofs); void setSegmentProtects(const LoadedImage& info, bool write); @@ -147,6 +160,16 @@ private: bool sandboxBlockedOpen(const char* path); bool sandboxBlockedStat(const char* path); bool sandboxBlocked(const char* path, const char* kind); + void unmapAllImages(); + + // MachOAnalyzerSet support + void mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override; + void mas_mainExecutable(WrappedMachO& anImage) const override; + void* mas_dyldCache() const override; + bool wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override; + const char* wmo_path(const WrappedMachO* image) const override; + bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override; + const Array& _existingImages; Array& _newImages; @@ -155,13 +178,17 @@ private: const objc_opt::objc_selopt_t* _dyldCacheSelectorOpt; const closure::ObjCSelectorOpt* _closureSelectorOpt; const Array& _closureSelectorImages; + const RootsChecker& _rootsChecker; #if BUILDING_DYLD LaunchImagesCache _launchImagesCache; #endif + bool _allowMissingLazies; + dyld3::Platform _platform; LogFunc _logLoads; LogFunc _logSegments; LogFunc _logFixups; LogFunc _logDofs; + dyld3::LaunchErrorInfo* _launchErrorInfo; }; diff --git a/dyld3/MachOAnalyzer.cpp b/dyld3/MachOAnalyzer.cpp index a8183c0..3159968 100644 --- a/dyld3/MachOAnalyzer.cpp +++ b/dyld3/MachOAnalyzer.cpp @@ -22,6 +22,10 @@ */ #include +#include +#include +#include +#include #include #include #include @@ -29,6 +33,7 @@ #include #include #include +#include #include #include @@ -36,6 +41,8 @@ #include "CodeSigningTypes.h" #include "Array.h" +// FIXME: We should get this from cctools +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F namespace dyld3 { @@ -44,7 +51,7 @@ const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const const GradedArchs& archs, Platform platform) { const MachOAnalyzer* result = (const MachOAnalyzer*)mh; - if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform) ) + if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform, true) ) return nullptr; if ( !result->isDynamicExecutable() ) return nullptr; @@ -52,6 +59,62 @@ const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const return result; } +bool MachOAnalyzer::loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem, + const char* path, const GradedArchs& archs, Platform platform, + closure::LoadedFileInfo& info) +{ + // 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, archs, info.isOSBinary, sliceOffset, sliceLen, fatButMissingSlice) ) { + // 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 ( diag.hasError() ) { + // We must have generated an error in the fat file parsing so use that error + fileSystem.unloadFile(info); + return false; + } + else if ( fatButMissingSlice ) { + diag.error("missing compatible arch in %s", path); + fileSystem.unloadFile(info); + return false; + } + + const MachOAnalyzer* mh = (MachOAnalyzer*)info.fileContent; + + // validate is mach-o of requested arch and platform + if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, archs, platform, info.isOSBinary) ) { + fileSystem.unloadFile(info); + return false; + } + + // 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 false; + } + + // 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 false; + } + + return true; +} + closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* path, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN]) @@ -76,58 +139,63 @@ closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::Fi if (diag.hasError()) diag.clearError(); - // 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, archs, sliceOffset, sliceLen, fatButMissingSlice) ) { - // 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 ( diag.hasError() ) { - // We must have generated an error in the fat file parsing so use that error - fileSystem.unloadFile(info); - return closure::LoadedFileInfo(); - } - else if ( fatButMissingSlice ) { - diag.error("missing compatible arch in %s", 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, archs, platform) ) { - 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(); - } - + bool loaded = loadFromBuffer(diag, fileSystem, path, archs, platform, info); + if (!loaded) + return {}; return info; } +// for use with already mmap()ed file +bool MachOAnalyzer::isOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) const +{ +#ifdef F_GETSIGSINFO + if ( fd == -1 ) + return false; + + uint32_t sigOffset; + uint32_t sigSize; + if ( !this->hasCodeSignature(sigOffset, sigSize) ) + return false; + + // register code signature + fsignatures_t sigreg; + sigreg.fs_file_start = sliceOffset; // start of mach-o slice in fat file + sigreg.fs_blob_start = (void*)(long)sigOffset; // start of CD in mach-o file + sigreg.fs_blob_size = sigSize; // size of CD + if ( ::fcntl(fd, F_ADDFILESIGS_RETURN, &sigreg) == -1 ) + return false; + + // ask if code signature is for something in the OS + fgetsigsinfo siginfo = { (off_t)sliceOffset, GETSIGSINFO_PLATFORM_BINARY, 0 }; + if ( ::fcntl(fd, F_GETSIGSINFO, &siginfo) == -1 ) + return false; + + return (siginfo.fg_sig_is_platform); +#else + return false; +#endif +} + +// for use when just the fat_header has been read +bool MachOAnalyzer::sliceIsOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) +{ + if ( fd == -1 ) + return false; + + // need to mmap() slice so we can find the code signature + void* mappedSlice = ::mmap(nullptr, sliceSize, PROT_READ, MAP_PRIVATE, fd, sliceOffset); + if ( mappedSlice == MAP_FAILED ) + return false; + + const MachOAnalyzer* ma = (MachOAnalyzer*)mappedSlice; + bool result = ma->isOSBinary(fd, sliceOffset, sliceSize); + ::munmap(mappedSlice, sliceSize); + + return result; +} + + + #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 @@ -145,7 +213,7 @@ uint64_t MachOAnalyzer::mappedSize() const return vmSpace; } -bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform platform) const +bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform reqPlatform, bool isOSBinary) const { // must start with mach-o magic value if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) { @@ -153,7 +221,7 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice return false; } - if ( !archs.grade(this->cputype, this->cpusubtype) ) { + if ( !archs.grade(this->cputype, this->cpusubtype, isOSBinary) ) { diag.error("could not use '%s' because it is not a compatible arch", path); return false; } @@ -163,8 +231,14 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice case MH_EXECUTE: case MH_DYLIB: case MH_BUNDLE: - case MH_DYLINKER: break; +#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC + // Allow offline tools to analyze binaries dyld doesn't load + case MH_DYLINKER: + case MH_KEXT_BUNDLE: + case MH_FILESET: + break; +#endif default: diag.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path, this->filetype); return false; @@ -177,18 +251,55 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice // filter out static executables if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) { +#if !BUILDING_DYLDINFO && !BUILDING_APP_CACHE_UTIL + // dyldinfo should be able to inspect static executables such as the kernel diag.error("could not use '%s' because it is a static executable", path); return false; +#endif } - // must match requested platform (do this after load commands are validated) - if ( !this->supportsPlatform(platform) ) { - diag.error("could not use '%s' because it was built for a different platform", path); + // HACK: If we are asking for no platform, then make sure the binary doesn't have one +#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL + if ( isFileSet() ) { + // A statically linked kernel collection should contain a 0 platform + __block bool foundPlatform = false; + __block bool foundBadPlatform = false; + forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + foundPlatform = true; + if ( platform != Platform::unknown ) { + foundBadPlatform = true; + } + }); + if (!foundPlatform) { + diag.error("could not use '%s' because we expected it to have a platform", path); + return false; + } + if (foundBadPlatform) { + diag.error("could not use '%s' because is has the wrong platform", path); + return false; + } + } else if ( reqPlatform == Platform::unknown ) { + // Unfortunately the static kernel has a platform, but kext's don't, so we can't + // verify the platform of the kernel. + if ( !isStaticExecutable() ) { + __block bool foundPlatform = false; + forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + foundPlatform = true; + }); + if (foundPlatform) { + diag.error("could not use '%s' because we expected it to have no platform", path); + return false; + } + } + } else +#endif + if ( !this->loadableIntoProcess(reqPlatform, path) ) { + diag.error("could not use '%s' because it was not built for platform %s", path, MachOFile::platformName(reqPlatform)); return false; } // validate dylib loads - if ( !validEmbeddedPaths(diag, platform, path) ) + if ( !validEmbeddedPaths(diag, reqPlatform, path) ) return false; // validate segments @@ -212,10 +323,16 @@ bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const if ( !validLinkeditLayout(diag, path) ) return false; - if ( hasChainedFixups() ) { + if ( hasLoadCommand(LC_DYLD_CHAINED_FIXUPS) ) { if ( !validChainedFixupsInfo(diag, path) ) return false; } +#if SUPPORT_ARCH_arm64e + else if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) { + if ( !validChainedFixupsInfoOldArm64e(diag, path) ) + return false; + } +#endif else { // validate rebasing info if ( !validRebaseInfo(diag, path) ) @@ -283,11 +400,22 @@ const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const clo diag.error("vm_allocate failure"); return nullptr; } + // re-map each segment read-only, with runtime layout - uint64_t textSegVmAddr = preferredLoadAddress(); +#if BUILDING_APP_CACHE_UTIL + // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest + __block uint64_t baseAddress = ~0ULL; + forEachSegment(^(const SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + uint64_t textSegVMAddr = preferredLoadAddress(); +#else + uint64_t baseAddress = preferredLoadAddress(); +#endif + forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) { - 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 ( (segmentInfo.fileSize != 0) && (segmentInfo.vmSize != 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-baseAddress)); if ( r != KERN_SUCCESS ) { diag.error("vm_copy() failure"); stop = true; @@ -301,6 +429,30 @@ const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const clo // make the new mapping read-only ::vm_protect(mach_task_self(), newMappedAddr, (vm_size_t)vmSpaceRequired, false, VM_PROT_READ); +#if BUILDING_APP_CACHE_UTIL + if ( textSegVMAddr != baseAddress ) { + info.unload = [](const closure::LoadedFileInfo& info) { + // Unloading binaries where __DATA is first requires working out the real range of the binary + // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately. + const MachOAnalyzer* ma = (const MachOAnalyzer*)info.fileContent; + __block uint64_t baseAddress = ~0ULL; + ma->forEachSegment(^(const SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + uint64_t textSegVMAddr = ma->preferredLoadAddress(); + + uint64_t basePointerOffset = textSegVMAddr - baseAddress; + uint8_t* bufferStart = (uint8_t*)info.fileContent - basePointerOffset; + ::vm_deallocate(mach_task_self(), (vm_address_t)bufferStart, (size_t)info.fileContentLen); + }; + + // And update the file content to the new location + info.fileContent = (const void*)(newMappedAddr + textSegVMAddr - baseAddress); + info.fileContentLen = vmSpaceRequired; + return (const MachOAnalyzer*)info.fileContent; + } +#endif + // 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); @@ -332,6 +484,10 @@ void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) return; if ( segmentInfo.writable() && (segmentInfo.fileSize != segmentInfo.vmSize) ) writeExpansion = true; // zerofill at end of __DATA + if ( segmentInfo.vmSize == 0 ) { + // Always zero fill if we have zero-sized segments + writeExpansion = true; + } if ( segmentInfo.vmAddr < lowestVmAddr ) lowestVmAddr = segmentInfo.vmAddr; if ( segmentInfo.vmAddr+segmentInfo.vmSize > highestVmAddr ) @@ -344,12 +500,76 @@ void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) totalVmSpace = (totalVmSpace + (pageSize - 1)) & ~(pageSize - 1); bool hasHole = (totalVmSpace != sumVmSizes); // segments not contiguous + // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place + bool hasOutOfOrderSegments = false; +#if BUILDING_APP_CACHE_UTIL + uint64_t textSegVMAddr = preferredLoadAddress(); + hasOutOfOrderSegments = textSegVMAddr != lowestVmAddr; +#endif + vmSpace = totalVmSpace; - hasZeroFill = writeExpansion || hasHole; + hasZeroFill = writeExpansion || hasHole || hasOutOfOrderSegments; } bool MachOAnalyzer::enforceFormat(Malformed kind) const { +#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC + // HACK: If we are the kernel, we have a different format to enforce + if ( isFileSet() ) { + bool result = false; + switch (kind) { + case Malformed::linkeditOrder: + case Malformed::linkeditAlignment: + case Malformed::dyldInfoAndlocalRelocs: + result = true; + break; + case Malformed::segmentOrder: + // The aux KC has __DATA first + result = false; + break; + case Malformed::linkeditPermissions: + case Malformed::executableData: + case Malformed::writableData: + case Malformed::codeSigAlignment: + case Malformed::sectionsAddrRangeWithinSegment: + result = true; + break; + case Malformed::textPermissions: + // The kernel has its own __TEXT_EXEC for executable memory + result = false; + break; + } + return result; + } + + if ( isStaticExecutable() ) { + bool result = false; + switch (kind) { + case Malformed::linkeditOrder: + case Malformed::linkeditAlignment: + case Malformed::dyldInfoAndlocalRelocs: + result = true; + break; + case Malformed::segmentOrder: + result = false; + break; + case Malformed::linkeditPermissions: + case Malformed::executableData: + case Malformed::codeSigAlignment: + case Malformed::textPermissions: + case Malformed::sectionsAddrRangeWithinSegment: + result = true; + break; + case Malformed::writableData: + // The kernel has __DATA_CONST marked as r/o + result = false; + break; + } + return result; + } + +#endif + __block bool result = false; forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { switch (platform) { @@ -366,11 +586,17 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const case Malformed::linkeditPermissions: case Malformed::textPermissions: case Malformed::executableData: + case Malformed::writableData: case Malformed::codeSigAlignment: // enforce these checks on new binaries only if (sdk >= 0x000A0F00) // macOS 10.15 result = true; break; + case Malformed::sectionsAddrRangeWithinSegment: + // enforce these checks on new binaries only + if (sdk >= 0x000A1000) // macOS 10.16 + result = true; + break; } break; case Platform::iOS: @@ -379,6 +605,7 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const case Malformed::dyldInfoAndlocalRelocs: case Malformed::textPermissions: case Malformed::executableData: + case Malformed::writableData: result = true; break; case Malformed::linkeditAlignment: @@ -389,6 +616,11 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const if (sdk >= 0x000D0000) // iOS 13 result = true; break; + case Malformed::sectionsAddrRangeWithinSegment: + // enforce these checks on new binaries only + if (sdk >= 0x000E0000) // iOS 14 + result = true; + break; } break; default: @@ -486,7 +718,7 @@ bool MachOAnalyzer::validEmbeddedPaths(Diagnostics& diag, Platform platform, con } } - if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE) ) { + if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE) && isDynamicExecutable() ) { diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path); return false; } @@ -608,8 +840,8 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi if ( badPermissions || badSize ) return false; if ( !hasTEXT ) { - diag.error("in '%s' missing __TEXT segment", path); - return false; + diag.error("in '%s' missing __TEXT segment", path); + return false; } if ( !hasLINKEDIT ) { diag.error("in '%s' missing __LINKEDIT segment", path); @@ -640,8 +872,9 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi } if ( (info1.segIndex < info2.segIndex) && !stop1 ) { if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset != 0)) ){ - if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) ) { + if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) && !isStaticExecutable() ) { // dyld cache __DATA_* segments are moved around + // The static kernel also has segments with vmAddr's before __TEXT 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; @@ -663,16 +896,23 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi 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); + 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); + 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; + bool ignoreError = !enforceFormat(Malformed::sectionsAddrRangeWithinSegment); +#if BUILDING_APP_CACHE_UTIL + if ( (seg->vmsize == 0) && !strcmp(seg->segname, "__CTF") ) + ignoreError = true; +#endif + if ( !ignoreError ) { + 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; + } } } } @@ -703,10 +943,21 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const { + const char* executableTextSegmentName = "__TEXT"; +#if BUILDING_APP_CACHE_UTIL + // The kernel has __start in __TEXT_EXEC, or for x86_64 it's __HIB + if ( isStaticExecutable() ) { + if ( isArch("x86_64") || isArch("x86_64h") ) + executableTextSegmentName = "__HIB"; + else + executableTextSegmentName = "__TEXT_EXEC"; + } +#endif + __block uint64_t textSegStartAddr = 0; __block uint64_t textSegStartSize = 0; forEachSegment(^(const SegmentInfo& info, bool& stop) { - if ( strcmp(info.segName, "__TEXT") == 0 ) { + if ( strcmp(info.segName, executableTextSegmentName) == 0 ) { textSegStartAddr = info.vmAddr; textSegStartSize = info.vmSize; stop = true; @@ -746,8 +997,25 @@ bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const diag.error("LC_UNIXTHREAD not valid for arch %s", archName()); stop = true; } +#if BUILDING_DYLDINFO + else if ( isStaticExecutable() ) { + __block bool foundSegment = false; + forEachSegment(^(const SegmentInfo& info, bool& stopSegment) { + // Skip segments which don't contain this address + if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) ) + return; + foundSegment = true; + if ( (info.protections & VM_PROT_EXECUTE) == 0 ) + diag.error("LC_UNIXTHREAD points to non-executable segment"); + stopSegment = true; + }); + if (!foundSegment) + diag.error("LC_UNIXTHREAD entry is out of range"); + stop = true; + } +#endif else if ( (startAddress < textSegStartAddr) || (startAddress >= textSegStartAddr+textSegStartSize) ) { - diag.error("LC_UNIXTHREAD entry not in __TEXT segment"); + diag.error("LC_UNIXTHREAD entry not in %s segment", executableTextSegmentName); stop = true; } break; @@ -755,7 +1023,15 @@ bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const }); if ( diag.hasError() ) return false; - if ( diag.noError() && (mainCount+threadCount == 1) ) + + if ( this->builtForPlatform(Platform::driverKit) ) { + if ( mainCount + threadCount == 0 ) + return true; + diag.error("no LC_MAIN allowed for driverkit"); + return false; + } + + if ( mainCount+threadCount == 1 ) return true; if ( mainCount + threadCount == 0 ) @@ -812,6 +1088,10 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con if ( leInfo.exportsTrie->datasize != 0 ) *bp++ = {"exports trie", ptrSize, leInfo.exportsTrie->dataoff, leInfo.exportsTrie->datasize}; } + if ( leInfo.chainedFixups != nullptr ) { + if ( leInfo.chainedFixups->datasize != 0 ) + *bp++ = {"chained fixups", ptrSize, leInfo.chainedFixups->dataoff, leInfo.chainedFixups->datasize}; + } if ( leInfo.dynSymTab != nullptr ) { if ( leInfo.dynSymTab->nlocrel != 0 ) @@ -855,13 +1135,25 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con return false; } } - if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) { + + bool checkMissingDyldInfo = true; +#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL + checkMissingDyldInfo = !isFileSet() && !isStaticExecutable() && !isKextBundle(); +#endif + if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) && checkMissingDyldInfo ) { diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path); return false; } + + // FIXME: Remove this hack +#if BUILDING_APP_CACHE_UTIL + if ( isFileSet() ) + return true; +#endif + const unsigned long blobCount = bp - blobs; if ( blobCount == 0 ) { - diag.error("in '%s' malformed mach-o misssing LINKEDIT", path); + diag.error("in '%s' malformed mach-o missing LINKEDIT", path); return false; } @@ -939,7 +1231,7 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con 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 + bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) const { if ( !segIndexSet ) { diag.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName); @@ -953,9 +1245,10 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName 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() ) { + switch ( kind ) { + case Rebase::pointer32: + case Rebase::pointer64: + if ( !segments[segmentIndex].writable() && enforceFormat(Malformed::writableData) ) { diag.error("in '%s' %s pointer rebase is in non-writable segment", path, opcodeName); return true; } @@ -964,8 +1257,8 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName return true; } break; - case REBASE_TYPE_TEXT_ABSOLUTE32: - case REBASE_TYPE_TEXT_PCREL32: + case Rebase::textAbsolute32: + case Rebase::textPCrel32: if ( !segments[segmentIndex].textRelocs ) { diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName); return true; @@ -979,8 +1272,8 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName return true; } break; - default: - diag.error("in '%s' %s unknown rebase type %d", path, opcodeName, type); + case Rebase::unknown: + diag.error("in '%s' %s unknown rebase type", path, opcodeName); return true; } return false; @@ -998,8 +1291,8 @@ void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[ 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) ) + bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) { + if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, kind) ) stop = true; }); return diag.noError(); @@ -1011,8 +1304,8 @@ void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_ __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 ) + bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) { + if ( kind != Rebase::textAbsolute32 ) return; if ( !startVmAddrSet ) { for (int i=0; i <= segmentIndex; ++i) { @@ -1029,8 +1322,7 @@ void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_ }); } - -void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const +void MachOAnalyzer::forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop)) const { __block bool startVmAddrSet = false; __block uint64_t startVmAddr = 0; @@ -1038,22 +1330,29 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo __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; - } - }); - } + 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; + bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) { + switch ( kind ) { + case Rebase::unknown: + return; + case Rebase::pointer32: + case Rebase::pointer64: + // We only handle these kinds for now. + break; + case Rebase::textPCrel32: + case Rebase::textAbsolute32: + return; + } if ( !startVmAddrSet ) { for (int i=0; i < segmentIndex; ++i) { if ( strcmp(segments[i].segName, "__TEXT") == 0 ) { @@ -1064,7 +1363,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo } } uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset; - bool skipRebase = false; + bool isLazyPointerRebase = false; if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) { // rebase is in lazy pointer section uint64_t lpValue = 0; @@ -1079,20 +1378,45 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo bool isLazyStub = contentIsRegularStub(helperContent); // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub if ( isLazyStub ) - skipRebase = true; + isLazyPointerRebase = 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); - } + uint64_t runtimeOffset = rebaseVmAddr - startVmAddr; + callback(runtimeOffset, isLazyPointerRebase, stop); }); } + +void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const +{ + forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) { + if ( isLazyPointerRebase && ignoreLazyPointers ) + return; + handler(runtimeOffset, stop); + }); +} + +bool MachOAnalyzer::hasStompedLazyOpcodes() const +{ + // if first eight bytes of lazy opcodes are zeros, then the opcodes have been stomped + bool result = false; + uint32_t size; + if ( const uint8_t* p = (uint8_t*)getLazyBindOpcodes(size) ) { + if ( size > 8 ) { + uint64_t content; + memcpy(&content, p, 8); + if ( content == 0 ) + result = true; + } + } + + return result; +} + bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const { switch (this->cputype) { @@ -1109,8 +1433,8 @@ bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const return false; } -static int uint32Sorter(const void* l, const void* r) { - if ( *((uint32_t*)l) < *((uint32_t*)r) ) +static int relocSorter(const void* l, const void* r) { + if ( ((relocation_info*)l)->r_address < ((relocation_info*)r)->r_address ) return -1; else return 1; @@ -1120,24 +1444,26 @@ static int uint32Sorter(const void* l, const void* r) { 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 + Rebase kind, bool& stop)) const { LinkEditInfo leInfo; getLinkEditPointers(diag, leInfo); if ( diag.hasError() ) return; - BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1); + BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1); getAllSegmentsInfos(diag, segmentsInfo); if ( diag.hasError() ) return; + const Rebase pointerRebaseKind = is64() ? Rebase::pointer64 : Rebase::pointer32; + if ( leInfo.dyldInfo != nullptr ) { const uint8_t* const start = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off); const uint8_t* const end = start + leInfo.dyldInfo->rebase_size; const uint8_t* p = start; const uint32_t ptrSize = pointerSize(); - uint8_t type = 0; + Rebase kind = Rebase::unknown; int segIndex = 0; uint64_t segOffset = 0; uint64_t count; @@ -1155,7 +1481,20 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, stop = true; break; case REBASE_OPCODE_SET_TYPE_IMM: - type = immediate; + switch ( immediate ) { + case REBASE_TYPE_POINTER: + kind = pointerRebaseKind; + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + kind = Rebase::textAbsolute32; + break; + case REBASE_TYPE_TEXT_PCREL32: + kind = Rebase::textPCrel32; + break; + default: + kind = Rebase::unknown; + break; + } break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segIndex = immediate; @@ -1170,7 +1509,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, 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); + handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop); segOffset += ptrSize; if ( stop ) break; @@ -1179,14 +1518,14 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, 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); + handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, 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); + handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop); segOffset += read_uleb128(diag, p, end) + ptrSize; break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: @@ -1195,7 +1534,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, 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); + handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop); segOffset += skip + ptrSize; if ( stop ) break; @@ -1205,39 +1544,70 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, diag.error("unknown rebase opcode 0x%02X", opcode); } } + return; } - else { + + if ( leInfo.chainedFixups != nullptr ) { + // binary uses chained fixups, so do nothing + // The kernel collections need to support both chained and classic relocations + // If we are anything other than a kernel collection, then return here as we won't have + // anything else to do. + if ( !isFileSet() ) + return; + } + + if ( leInfo.dynSymTab != nullptr ) { // old binary, walk relocations - const uint64_t relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex); + const uint64_t relocsStartAddress = localRelocBaseAddress(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); + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info, relocs, 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; + bool shouldEmitError = true; +#if BUILDING_APP_CACHE_UTIL + if ( usesClassicRelocationsInKernelCollection() && (reloc->r_length == 2) && (relocSize == 3) ) + shouldEmitError = false; +#endif + if ( shouldEmitError ) { + 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); + relocs.push_back(*reloc); } - if ( !relocAddrs.empty() ) { - ::qsort(&relocAddrs[0], relocAddrs.count(), sizeof(uint32_t), &uint32Sorter); - for (uint32_t addrOff : relocAddrs) { + if ( !relocs.empty() ) { + ::qsort(&relocs[0], relocs.count(), sizeof(relocation_info), &relocSorter); + for (relocation_info reloc : relocs) { + uint32_t addrOff = reloc.r_address; uint32_t segIndex = 0; uint64_t segOffset = 0; - if ( segIndexAndOffsetForAddress(relocsStartAddress+addrOff, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) { - uint8_t type = REBASE_TYPE_POINTER; + uint64_t addr = 0; +#if BUILDING_APP_CACHE_UTIL + // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be + // negative + if ( isStaticExecutable() || isFileSet() ) { + addr = relocsStartAddress + (int32_t)addrOff; + } else { + addr = relocsStartAddress + addrOff; + } +#else + addr = relocsStartAddress + addrOff; +#endif + if ( segIndexAndOffsetForAddress(addr, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) { + Rebase kind = (reloc.r_length == 2) ? Rebase::pointer32 : Rebase::pointer64; if ( this->cputype == CPU_TYPE_I386 ) { if ( segmentsInfo[segIndex].executable() ) - type = REBASE_TYPE_TEXT_ABSOLUTE32; + kind = Rebase::textAbsolute32; } - handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, type , stop); + handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, kind, stop); } else { diag.error("local relocation has out of range r_address"); @@ -1253,7 +1623,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, 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); + handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, pointerRebaseKind, indStop); } else { diag.error("local relocation has out of range r_address"); @@ -1275,10 +1645,16 @@ bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo return false; } -uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const +uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const { - if ( is64() ) { - // x86_64 reloc base address is first writable segment + if ( isArch("x86_64") || isArch("x86_64h") ) { +#if BUILDING_APP_CACHE_UTIL + if ( isKextBundle() ) { + // for kext bundles the reloc base address starts at __TEXT segment + return segmentsInfos[0].vmAddr; + } +#endif + // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA) for (uint32_t i=0; i < segCount; ++i) { if ( segmentsInfos[i].writable() ) return segmentsInfos[i].vmAddr; @@ -1287,6 +1663,31 @@ uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint return segmentsInfos[0].vmAddr; } +uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const +{ + // Dyld caches are too large for a raw r_address, so everything is an offset from the base address + if ( inDyldCache() ) { + return preferredLoadAddress(); + } + +#if BUILDING_APP_CACHE_UTIL + if ( isKextBundle() ) { + // for kext bundles the reloc base address starts at __TEXT segment + return preferredLoadAddress(); + } +#endif + + if ( isArch("x86_64") || isArch("x86_64h") ) { + // for x86_64 reloc base address starts at first writable segment (usually __DATA) + for (uint32_t i=0; i < segCount; ++i) { + if ( segmentsInfos[i].writable() ) + return segmentsInfos[i].vmAddr; + } + } + // For everyone else we start at 0 + return 0; +} + void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint64_t pointerAddress, bool bind, int bindLibOrdinal, const char* bindSymbolName, @@ -1309,6 +1710,12 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui uint32_t symCount = leInfo.symTab->nsyms; uint32_t poolSize = leInfo.symTab->strsize; __block bool stop = false; + + // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess. + // In that case, skip the loop as there shouldn't be anything to process + if ( (indirectSymbolTableCount == 0) && isKextBundle() ) + return; + 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); @@ -1341,6 +1748,7 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui return; } uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc; + uint8_t n_type = is64Bit ? symbols64[symNum].n_type : symbols32[symNum].n_type; 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 ) { @@ -1351,6 +1759,9 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui const char* symbolName = stringPool + strOffset; bool weakImport = (n_desc & N_WEAK_REF); bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS); + // Handle defined weak def symbols which need to get a special ordinal + if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) ) + libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP; handler(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop); } sectionStop = stop; @@ -1390,7 +1801,7 @@ bool MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const stop = true; } }, ^(const char* symbolName) { - }, ^() { }); + }); return diag.noError(); } @@ -1438,8 +1849,14 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, } break; case BIND_TYPE_TEXT_ABSOLUTE32: - case BIND_TYPE_TEXT_PCREL32: - if ( !segments[segmentIndex].textRelocs ) { + case BIND_TYPE_TEXT_PCREL32: { + // Text relocations are permitted in x86_64 kexts + bool forceAllowTextRelocs = false; +#if BUILDING_APP_CACHE_UTIL + if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) ) + forceAllowTextRelocs = true; +#endif + if ( !forceAllowTextRelocs && !segments[segmentIndex].textRelocs ) { diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName); return true; } @@ -1452,6 +1869,7 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, return true; } break; + } default: diag.error("in '%s' %s unknown bind type %d", path, opcodeName, type); return true; @@ -1459,10 +1877,9 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, return false; } -void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, +void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop), - void (^strongHandler)(const char* symbolName), - void (^missingLazyBindHandler)()) const + void (^strongHandler)(const char* symbolName)) const { __block bool startVmAddrSet = false; __block uint64_t startVmAddr = 0; @@ -1481,21 +1898,28 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runt } uint64_t bindVmOffset = segments[segmentIndex].vmAddr + segmentOffset; uint64_t runtimeOffset = bindVmOffset - startVmAddr; - handler(runtimeOffset, libOrdinal, symbolName, weakImport, lazyBind, addend, stop); + handler(runtimeOffset, libOrdinal, type, symbolName, weakImport, lazyBind, addend, stop); }, ^(const char* symbolName) { strongHandler(symbolName); - }, ^() { - missingLazyBindHandler(); }); } +void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, + bool weakImport, bool lazyBind, uint64_t addend, bool& stop), + void (^strongHandler)(const char* symbolName)) const +{ + forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName, + bool weakImport, bool lazyBind, uint64_t addend, bool &stop) { + handler(runtimeOffset, libOrdinal, symbolName, weakImport, lazyBind, addend, stop); + }, strongHandler); +} + 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, bool lazyBind, uint64_t addend, bool& stop), - void (^strongHandler)(const char* symbolName), - void (^missingLazyBindHandler)()) const + void (^strongHandler)(const char* symbolName)) const { const uint32_t ptrSize = this->pointerSize(); bool stop = false; @@ -1505,7 +1929,7 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, if ( diag.hasError() ) return; - BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1); + BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1); getAllSegmentsInfos(diag, segmentsInfo); if ( diag.hasError() ) return; @@ -1684,9 +2108,9 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, break; } } - if ( lazyDoneCount > lazyBindCount+7 ) - missingLazyBindHandler(); - // diag.error("lazy bind opcodes missing binds"); + if ( lazyDoneCount > lazyBindCount+7 ) { + // diag.error("lazy bind opcodes missing binds"); + } } if ( diag.hasError() ) return; @@ -1774,9 +2198,12 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, } } } - else { + else if ( leInfo.chainedFixups != nullptr ) { + // binary uses chained fixups, so do nothing + } + else if ( leInfo.dynSymTab != nullptr ) { // old binary, process external relocations - const uint64_t relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex); + const uint64_t relocsStartAddress = externalRelocBaseAddress(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() ; @@ -1788,13 +2215,35 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, 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; + bool isBranch = false; +#if BUILDING_APP_CACHE_UTIL + if ( isKextBundle() ) { + // kext's may have other kinds of relocations, eg, branch relocs. Skip them + if ( isArch("x86_64") || isArch("x86_64h") ) { + if ( reloc->r_type == X86_64_RELOC_BRANCH ) { + if ( reloc->r_length != 2 ) { + diag.error("external relocation has wrong r_length"); + break; + } + if ( reloc->r_pcrel != true ) { + diag.error("external relocation should be pcrel"); + break; + } + isBranch = true; + } + } } - 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; +#endif + + if ( !isBranch ) { + 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; @@ -1807,6 +2256,7 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, 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; + uint8_t n_type = is64Bit ? symbols64[symbolIndex].n_type : symbols32[symbolIndex].n_type; 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); @@ -1816,9 +2266,13 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, 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); + uint64_t addend = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content); + // Handle defined weak def symbols which need to get a special ordinal + if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) ) + libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP; + uint8_t type = isBranch ? BIND_TYPE_TEXT_PCREL32 : BIND_TYPE_POINTER; handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal, - ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, symbolName, weakImport, false, addend, stop); + ptrSize, segIndex, segOffset, type, symbolName, weakImport, false, addend, stop); } } } @@ -1847,8 +2301,209 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, } - bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return false; + + BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1); + getAllSegmentsInfos(diag, segmentsInfo); + if ( diag.hasError() ) + return false; + + // validate dyld_chained_fixups_header + const dyld_chained_fixups_header* chainsHeader = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff); + if ( chainsHeader->fixups_version != 0 ) { + diag.error("chained fixups, unknown header version"); + return false; + } + if ( chainsHeader->starts_offset >= leInfo.chainedFixups->datasize ) { + diag.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size"); + return false; + } + if ( chainsHeader->imports_offset > leInfo.chainedFixups->datasize ) { + diag.error("chained fixups, imports_offset exceeds LC_DYLD_CHAINED_FIXUPS size"); + return false; + } + + uint32_t formatEntrySize; + switch ( chainsHeader->imports_format ) { + case DYLD_CHAINED_IMPORT: + formatEntrySize = sizeof(dyld_chained_import); + break; + case DYLD_CHAINED_IMPORT_ADDEND: + formatEntrySize = sizeof(dyld_chained_import_addend); + break; + case DYLD_CHAINED_IMPORT_ADDEND64: + formatEntrySize = sizeof(dyld_chained_import_addend64); + break; + default: + diag.error("chained fixups, unknown imports_format"); + return false; + } + if ( greaterThanAddOrOverflow(chainsHeader->imports_offset, (formatEntrySize * chainsHeader->imports_count), chainsHeader->symbols_offset) ) { + diag.error("chained fixups, imports array overlaps symbols"); + return false; + } + if ( chainsHeader->symbols_format != 0 ) { + diag.error("chained fixups, symbols_format unknown"); + return false; + } + + // validate dyld_chained_starts_in_image + const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainsHeader + chainsHeader->starts_offset); + if ( startsInfo->seg_count != leInfo.layout.linkeditSegIndex+1 ) { + // We can have fewer segments than the count, so long as those we are missing have no relocs + // This can happen because __CTF is inserted by ctf_insert after linking, and between __DATA and __LINKEDIT, but has no relocs + // ctf_insert updates the load commands to put __CTF between __DATA and __LINKEDIT, but doesn't update the chained fixups data structures + if ( startsInfo->seg_count > (leInfo.layout.linkeditSegIndex + 1) ) { + diag.error("chained fixups, seg_count exceeds number of segments"); + return false; + } + + // We can have fewer segments than the count, so long as those we are missing have no relocs + uint32_t numNoRelocSegments = 0; + uint32_t numExtraSegments = (leInfo.layout.lastSegIndex + 1) - startsInfo->seg_count; + for (unsigned i = 0; i != numExtraSegments; ++i) { + // Check each extra segment before linkedit + const SegmentInfo& segInfo = segmentsInfo[leInfo.layout.linkeditSegIndex - (i + 1)]; + if ( segInfo.vmSize == 0 ) + ++numNoRelocSegments; + } + + if ( numNoRelocSegments != numExtraSegments ) { + diag.error("chained fixups, seg_count does not match number of segments"); + return false; + } + } + const uint64_t baseAddress = preferredLoadAddress(); + uint32_t maxValidPointerSeen = 0; + uint16_t pointer_format_for_all = 0; + bool pointer_format_found = false; + const uint8_t* endOfStarts = (uint8_t*)chainsHeader + chainsHeader->imports_offset; + for (uint32_t i=0; i < startsInfo->seg_count; ++i) { + uint32_t segInfoOffset = startsInfo->seg_info_offset[i]; + // 0 offset means this segment has no fixups + if ( segInfoOffset == 0 ) + continue; + const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset); + if ( segInfo->size > (endOfStarts - (uint8_t*)segInfo) ) { + diag.error("chained fixups, dyld_chained_starts_in_segment for segment #%d overruns imports table", i); + return false; + } + + // validate dyld_chained_starts_in_segment + if ( (segInfo->page_size != 0x1000) && (segInfo->page_size != 0x4000) ) { + diag.error("chained fixups, page_size not 4KB or 16KB in segment #%d", i); + return false; + } + if ( segInfo->pointer_format > 12 ) { + diag.error("chained fixups, unknown pointer_format in segment #%d", i); + return false; + } + if ( !pointer_format_found ) { + pointer_format_for_all = segInfo->pointer_format; + pointer_format_found = true; + } + if ( segInfo->pointer_format != pointer_format_for_all) { + diag.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo->pointer_format, pointer_format_for_all); + return false; + } + if ( segInfo->segment_offset != (segmentsInfo[i].vmAddr - baseAddress) ) { + diag.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i); + return false; + } + if ( segInfo->max_valid_pointer != 0 ) { + if ( maxValidPointerSeen == 0 ) { + // record max_valid_pointer values seen + maxValidPointerSeen = segInfo->max_valid_pointer; + } + else if ( maxValidPointerSeen != segInfo->max_valid_pointer ) { + diag.error("chained fixups, different max_valid_pointer values seen in different segments"); + return false; + } + } + // validate starts table in segment + if ( offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]) > segInfo->size ) { + diag.error("chained fixups, page_start array overflows size"); + return false; + } + uint32_t maxOverflowIndex = (uint32_t)(segInfo->size - offsetof(dyld_chained_starts_in_segment, page_start[segInfo->page_count]))/sizeof(uint16_t); + for (int pageIndex=0; pageIndex < segInfo->page_count; ++pageIndex) { + uint16_t offsetInPage = segInfo->page_start[pageIndex]; + if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE ) + continue; + if ( (offsetInPage & DYLD_CHAINED_PTR_START_MULTI) == 0 ) { + // this is the offset into the page where the first fixup is + if ( offsetInPage > segInfo->page_size ) { + diag.error("chained fixups, in segment #%d page_start[%d]=0x%04X exceeds page size", i, pageIndex, offsetInPage); + } + } + else { + // this is actually an index into chain_starts[] + uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI; + // now verify all starts are within the page and in ascending order + uint16_t lastOffsetInPage = 0; + do { + if ( overflowIndex > maxOverflowIndex ) { + diag.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex, maxOverflowIndex, segmentName(i)); + return false; + } + offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST); + if ( offsetInPage > segInfo->page_size ) { + diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X exceeds page size", i, overflowIndex, offsetInPage); + return false; + } + if ( (offsetInPage <= lastOffsetInPage) && (lastOffsetInPage != 0) ) { + diag.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X is before previous at 0x%04X\n", i, overflowIndex, offsetInPage, lastOffsetInPage); + return false; + } + lastOffsetInPage = offsetInPage; + ++overflowIndex; + } while ( (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST) == 0 ); + } + } + + } + // validate import table size can fit + if ( chainsHeader->imports_count != 0 ) { + uint32_t maxBindOrdinal = 0; + switch (pointer_format_for_all) { + case DYLD_CHAINED_PTR_32: + maxBindOrdinal = 0x0FFFFF; // 20-bits + break; + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_OFFSET: + maxBindOrdinal = 0x00FFFF; // 16-bits + break; + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + maxBindOrdinal = 0xFFFFFF; // 24 bits + break; + } + if ( chainsHeader->imports_count >= maxBindOrdinal ) { + diag.error("chained fixups, imports_count (%d) exceeds max of %d", chainsHeader->imports_count, maxBindOrdinal); + return false; + } + } + + // validate max_valid_pointer is larger than last segment + if ( (maxValidPointerSeen != 0) && !inDyldCache() ) { + uint64_t lastSegmentLastVMAddr = segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmAddr + segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmSize; + if ( maxValidPointerSeen < lastSegmentLastVMAddr ) { + diag.error("chained fixups, max_valid_pointer too small for image"); + return false; + } + } + + return diag.noError(); +} + +bool MachOAnalyzer::validChainedFixupsInfoOldArm64e(Diagnostics& diag, const char* path) const { __block uint32_t maxTargetCount = 0; __block uint32_t currentTargetCount = 0; @@ -1916,7 +2571,7 @@ void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics& diag, void (^target if ( diag.hasError() ) return; - BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1); + BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1); getAllSegmentsInfos(diag, segmentsInfo); if ( diag.hasError() ) return; @@ -2022,7 +2677,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback if ( diag.hasError() ) return; - BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1); + BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1); getAllSegmentsInfos(diag, segmentsInfo); if ( diag.hasError() ) return; @@ -2049,7 +2704,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback switch (header->imports_format) { case DYLD_CHAINED_IMPORT: imports = (dyld_chained_import*)((uint8_t*)header + header->imports_offset); - for (uint32_t i=0; i < header->imports_count; ++i) { + for (uint32_t i=0; i < header->imports_count && !stop; ++i) { const char* symbolName = &symbolsPool[imports[i].name_offset]; if ( imports[i].name_offset > maxSymbolOffset ) { diag.error("malformed import table, string overflow"); @@ -2065,7 +2720,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback break; case DYLD_CHAINED_IMPORT_ADDEND: importsA32 = (dyld_chained_import_addend*)((uint8_t*)header + header->imports_offset); - for (uint32_t i=0; i < header->imports_count; ++i) { + for (uint32_t i=0; i < header->imports_count && !stop; ++i) { const char* symbolName = &symbolsPool[importsA32[i].name_offset]; if ( importsA32[i].name_offset > maxSymbolOffset ) { diag.error("malformed import table, string overflow"); @@ -2076,12 +2731,12 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback libOrdinal = (int8_t)libVal; else libOrdinal = libVal; - callback(libOrdinal, symbolName, importsA32[i].addend, importsA32[i].weak_import, stop); + callback(libOrdinal, symbolName, importsA32[i].addend, importsA32[i].weak_import, stop); } break; case DYLD_CHAINED_IMPORT_ADDEND64: importsA64 = (dyld_chained_import_addend64*)((uint8_t*)header + header->imports_offset); - for (uint32_t i=0; i < header->imports_count; ++i) { + for (uint32_t i=0; i < header->imports_count && !stop; ++i) { const char* symbolName = &symbolsPool[importsA64[i].name_offset]; if ( importsA64[i].name_offset > maxSymbolOffset ) { diag.error("malformed import table, string overflow"); @@ -2146,10 +2801,105 @@ bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const return true; } -bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const +bool MachOAnalyzer::hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset) const +{ + if ( this->filetype != MH_EXECUTE ) + return false; + + // macOS 10.8+ program uses LC_MAIN and ProgramVars are in libdyld.dylib + // macOS 10.6 -> 10.7 ProgramVars are in __program_vars section in main executable + // macOS 10.5 ProgramVars are in __dyld section in main executable and 7 pointers in size + // macOS 10.4 and earlier ProgramVars need to be looked up by name in nlist of main executable + + uint64_t offset; + bool usesCRT; + if ( getEntry(offset, usesCRT) && usesCRT ) { + // is pre-10.8 program + uint64_t sectionSize; + if ( const void* progVarsSection = findSectionContent("__DATA", "__program_vars", sectionSize) ) { + progVarsOffset = (uint32_t)((uint8_t*)progVarsSection - (uint8_t*)this); + return true; + } + else if ( const void* dyldSection = findSectionContent("__DATA", "__dyld", sectionSize) ) { + if ( sectionSize >= 7*pointerSize() ) { + progVarsOffset = (uint32_t)((uint8_t*)dyldSection - (uint8_t*)this) + 2*pointerSize(); + return true; + } + } + diag.error("pre-macOS 10.5 binaries not supported"); + return true; + } + return false; +} + +// Convert from a (possibly) live pointer to a vmAddr +uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value) const { + if ( contentRebased ) { + if ( value == 0 ) + return 0; + // The value may have been signed. Strip the signature if that is the case +#if __has_feature(ptrauth_calls) + value = (uint64_t)__builtin_ptrauth_strip((void*)value, ptrauth_key_asia); +#endif + value -= slide; + return value; + } + if ( chainedPointerFormat != 0 ) { + auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value; + uint64_t targetRuntimeOffset; + if ( chainedValue->isRebase(chainedPointerFormat, preferredLoadAddress, targetRuntimeOffset) ) { + value = preferredLoadAddress + targetRuntimeOffset; + } + return value; + } + +#if !(BUILDING_LIBDYLD || BUILDING_DYLD) + typedef MachOAnalyzer::VMAddrConverter VMAddrConverter; + if ( sharedCacheChainedPointerFormat != VMAddrConverter::SharedCacheFormat::none ) { + switch ( sharedCacheChainedPointerFormat ) { + case VMAddrConverter::SharedCacheFormat::none: + assert(false); + case VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi: { + const uint64_t deltaMask = 0x00FFFF0000000000; + const uint64_t valueMask = ~deltaMask; + const uint64_t valueAdd = preferredLoadAddress; + value = (value & valueMask); + if ( value != 0 ) { + value += valueAdd; + } + break; + } + case VMAddrConverter::SharedCacheFormat::v3: { + // Just use the chained pointer format for arm64e + auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value; + uint64_t targetRuntimeOffset; + if ( chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E, preferredLoadAddress, + targetRuntimeOffset) ) { + value = preferredLoadAddress + targetRuntimeOffset; + } + break; + } + } + return value; + } +#endif + + return value; +} + +MachOAnalyzer::VMAddrConverter MachOAnalyzer::makeVMAddrConverter(bool contentRebased) const { + MachOAnalyzer::VMAddrConverter vmAddrConverter; + vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); + vmAddrConverter.slide = getSlide(); + vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; + vmAddrConverter.contentRebased = contentRebased; + return vmAddrConverter; +} + +bool MachOAnalyzer::hasInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, const void* dyldCache) const { __block bool result = false; - forEachInitializer(diag, contentRebased, ^(uint32_t offset) { + forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) { result = true; }, dyldCache); return result; @@ -2206,7 +2956,7 @@ public: dyld3::OverflowSafeArray segments { localAlloc, sizeof(localAlloc) / sizeof(localAlloc[0]) }; }; -void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache) const +void MachOAnalyzer::forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache) const { __block SegmentRanges executableSegments; forEachSegment(^(const SegmentInfo& info, bool& stop) { @@ -2245,33 +2995,12 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v // next any function pointers in mod-init section const unsigned ptrSize = pointerSize(); - const bool useChainedFixups = hasChainedFixups(); - const uint16_t pointerFormat = useChainedFixups ? this->chainedPointerFormat() : 0; forEachInitializerPointerSection(diag, ^(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop) { if ( ptrSize == 8 ) { const uint64_t* initsStart = (uint64_t*)content; const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + sectionSize); for (const uint64_t* p=initsStart; p < initsEnd; ++p) { - uint64_t anInit = *p; - if ( contentRebased ) { - // The function pointer may have been signed. Strip the signature if that is the case -#if __has_feature(ptrauth_calls) - anInit = (uint64_t)__builtin_ptrauth_strip((void*)anInit, ptrauth_key_asia); -#endif - anInit -= slide; - } - else if ( useChainedFixups ) { - uint64_t initFuncRuntimeOffset; - ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p; - if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) { - anInit = loadAddress+initFuncRuntimeOffset; - } - else { - diag.error("initializer is not rebased"); - stop = true; - break; - } - } + uint64_t anInit = vmAddrConverter.convertToVMAddr(*p); if ( !executableSegments.contains(anInit) ) { diag.error("initializer 0x%0llX does not point within executable segment", anInit); stop = true; @@ -2284,22 +3013,7 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v const uint32_t* initsStart = (uint32_t*)content; const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + sectionSize); for (const uint32_t* p=initsStart; p < initsEnd; ++p) { - uint32_t anInit = *p; - if ( contentRebased ) { - anInit -= slide; - } - else if ( useChainedFixups ) { - uint64_t initFuncRuntimeOffset; - ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p; - if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) { - anInit = (uint32_t)(loadAddress+initFuncRuntimeOffset); - } - else { - diag.error("initializer is not rebased"); - stop = true; - break; - } - } + uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p); if ( !executableSegments.contains(anInit) ) { diag.error("initializer 0x%0X does not point within executable segment", anInit); stop = true; @@ -2338,8 +3052,8 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + info.sectSize); for (const uint32_t* p=initsStart; p < initsEnd; ++p) { uint32_t anInitOffset = *p; - if ( anInitOffset > executableSegments.segments[0].fileSize ) { - diag.error("initializer 0x%0X is not an offset within __TEXT segment", anInitOffset); + if ( !executableSegments.contains(loadAddress + anInitOffset) ) { + diag.error("initializer 0x%08X does not an offset to an executable segment", anInitOffset); stop = true; break; } @@ -2348,16 +3062,16 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v }); } -bool MachOAnalyzer::hasTerminators(Diagnostics& diag, bool contentRebased) const +bool MachOAnalyzer::hasTerminators(Diagnostics& diag, const VMAddrConverter& vmAddrConverter) const { __block bool result = false; - forEachTerminator(diag, contentRebased, ^(uint32_t offset) { + forEachTerminator(diag, vmAddrConverter, ^(uint32_t offset) { result = true; }); return result; } -void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset)) const +void MachOAnalyzer::forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const { __block SegmentRanges executableSegments; forEachSegment(^(const SegmentInfo& info, bool& stop) { @@ -2376,11 +3090,8 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo // next any function pointers in mod-term section const unsigned ptrSize = pointerSize(); - const bool useChainedFixups = hasChainedFixups(); forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) { if ( (info.sectFlags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ) { - uint64_t initFuncRuntimeOffset; - const uint16_t pointerFormat = useChainedFixups ? this->chainedPointerFormat() : 0; const uint8_t* content; content = (uint8_t*)(info.sectAddr + slide); if ( (info.sectSize % ptrSize) != 0 ) { @@ -2402,25 +3113,7 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo 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 ) { - // The function pointer may have been signed. Strip the signature if that is the case -#if __has_feature(ptrauth_calls) - anInit = (uint64_t)__builtin_ptrauth_strip((void*)anInit, ptrauth_key_asia); -#endif - anInit -= slide; - } - else if ( useChainedFixups ) { - ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p; - if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) { - anInit = loadAddress+initFuncRuntimeOffset; - } - else { - diag.error("terminator is not rebased"); - stop = true; - break; - } - } + uint64_t anInit = vmAddrConverter.convertToVMAddr(*p); if ( !executableSegments.contains(anInit) ) { diag.error("terminator 0x%0llX does not point within executable segment", anInit); stop = true; @@ -2433,21 +3126,7 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo 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; - } - else if ( useChainedFixups ) { - ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p; - if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) { - anInit = (uint32_t)(loadAddress+initFuncRuntimeOffset); - } - else { - diag.error("terminator is not rebased"); - stop = true; - break; - } - } + uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p); if ( !executableSegments.contains(anInit) ) { diag.error("terminator 0x%0X does not point within executable segment", anInit); stop = true; @@ -2491,10 +3170,26 @@ bool MachOAnalyzer::hasObjC() const return result; } +bool MachOAnalyzer::usesObjCGarbageCollection() 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) ) { + const uint64_t slide = (uint64_t)this - preferredLoadAddress(); + const uint32_t* flags = (uint32_t*)(info.sectAddr + slide); + if ( flags[1] & 4 ) + 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) ) { + if ( (this->cputype == CPU_TYPE_I386) && this->builtForPlatform(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) { @@ -2534,6 +3229,27 @@ bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const return result; } +bool MachOAnalyzer::isSwiftLibrary() const +{ + struct objc_image_info { + int32_t version; + uint32_t flags; + }; + + int64_t slide = getSlide(); + __block bool result = false; + forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( (strncmp(sectInfo.sectName, "__objc_imageinfo", 16) == 0) && (strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0) ) { + objc_image_info* info = (objc_image_info*)((uint8_t*)sectInfo.sectAddr + slide); + uint32_t swiftVersion = ((info->flags >> 8) & 0xFF); + if ( swiftVersion ) + result = true; + stop = true; + } + }); + return result; +} + const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const { Diagnostics diag; @@ -2582,6 +3298,30 @@ const void* MachOAnalyzer::getSplitSeg(uint32_t& size) const return getLinkEditContent(leInfo.layout, leInfo.splitSegInfo->dataoff); } +bool MachOAnalyzer::hasSplitSeg() const { + uint32_t splitSegSize = 0; + const void* splitSegStart = getSplitSeg(splitSegSize); + return splitSegStart != nullptr; +} + +bool MachOAnalyzer::isSplitSegV1() const { + uint32_t splitSegSize = 0; + const void* splitSegStart = getSplitSeg(splitSegSize); + if (!splitSegStart) + return false; + + return (*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT; +} + +bool MachOAnalyzer::isSplitSegV2() const { + uint32_t splitSegSize = 0; + const void* splitSegStart = getSplitSeg(splitSegSize); + if (!splitSegStart) + return false; + + return (*(const uint8_t*)splitSegStart) == DYLD_CACHE_ADJ_V2_FORMAT; +} + uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex, uint64_t targetSegOffset) const { @@ -2623,7 +3363,7 @@ uint64_t MachOAnalyzer::preferredLoadAddress() const } -bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const +bool MachOAnalyzer::getEntry(uint64_t& offset, bool& usesCRT) const { Diagnostics diag; offset = 0; @@ -2631,42 +3371,19 @@ bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const if ( cmd->cmd == LC_MAIN ) { entry_point_command* mainCmd = (entry_point_command*)cmd; usesCRT = false; - offset = (uint32_t)mainCmd->entryoff; + offset = 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()); + offset = 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; - 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; - } - return startAddress; -} - void MachOAnalyzer::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const { @@ -2750,44 +3467,20 @@ bool MachOAnalyzer::usesLibraryValidation() const bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const { + if (!MachOFile::canHavePrecomputedDlopenClosure(path, failureReason)) + return false; + + // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same + // at runtime as when the shared cache processed it. We must have a code signature to record this information + uint32_t codeSigFileOffset; + uint32_t codeSigSize; + if ( !hasCodeSignature(codeSigFileOffset, codeSigSize) ) { + failureReason("no code signature"); + return false; + } + __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) { @@ -2819,23 +3512,9 @@ bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^fai 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"); - } - - // Don't precompute iOSMac for now until dyld3 support is there. - if ( supportsPlatform(Platform::iOSMac) && !supportsPlatform(Platform::macOS) ) { - retval = false; - failureReason("UIKitForMac binary"); - } - return retval; } @@ -2866,8 +3545,6 @@ bool MachOAnalyzer::hasUnalignedPointerFixups() const } }, ^(const char* symbolName) { - }, - ^() { }); forEachRebase(diag, true, ^(uint64_t runtimeOffset, bool& stop) { if ( (runtimeOffset & 7) != 0 ) { @@ -2950,21 +3627,133 @@ void MachOAnalyzer::forEachExportedSymbol(Diagnostics& diag, ExportsCallback cal uint64_t trieSize; if ( const uint8_t* trieStart = getExportsTrie(leInfo, trieSize) ) { const uint8_t* trieEnd = trieStart + trieSize; + // We still emit empty export trie load commands just as a placeholder to show we have + // no exports. In that case, don't start recursing as we'll immediately think we ran + // of the end of the buffer + if ( trieSize == 0 ) + return; bool stop = false; STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString, 4096); recurseTrie(diag, trieStart, trieStart, trieEnd, cummulativeString, 0, stop, callback); } } +bool MachOAnalyzer::markNeverUnload(Diagnostics &diag) const { + bool neverUnload = false; + + if ( hasThreadLocalVariables() ) { + neverUnload = true; + } else if ( hasObjC() && isDylib() ) { + neverUnload = true; + } else { + // record if image has DOF sections + __block bool hasDOFs = false; + forEachDOFSection(diag, ^(uint32_t offset) { + hasDOFs = true; + }); + if ( hasDOFs ) + neverUnload = true; + } + return neverUnload; +} + + bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const { if (!MachOFile::canBePlacedInDyldCache(path, failureReason)) return false; + + // arm64e requires split seg v2 as the split seg code can't handle chained fixups for split seg v1 + if ( isArch("arm64e") ) { + uint32_t splitSegSize = 0; + const uint8_t* infoStart = (const uint8_t*)getSplitSeg(splitSegSize); + if ( *infoStart != DYLD_CACHE_ADJ_V2_FORMAT ) { + failureReason("chained fixups requires split seg v2"); + return false; + } + } + + // dyld_cache_patchable_location only supports addend in range 0..31 + const bool is64bit = is64(); + __block Diagnostics diag; + __block bool addendTooLarge = false; + if ( this->hasChainedFixups() ) { + // with chained fixups, addends can be in the import table or embedded in a bind pointer + forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { + if ( is64bit ) + addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI + if ( addend > 31 ) { + addendTooLarge = true; + stop = true; + } + }); + // check each pointer for embedded addend + withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { + forEachFixupInAllChains(diag, starts, false, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + switch (segInfo->pointer_format) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + if ( fixupLoc->arm64e.bind.bind && !fixupLoc->arm64e.authBind.auth ) { + if ( fixupLoc->arm64e.bind.addend > 31 ) { + addendTooLarge = true; + stop = true; + } + } + break; + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + if ( fixupLoc->generic64.rebase.bind ) { + if ( fixupLoc->generic64.bind.addend > 31 ) { + addendTooLarge = true; + stop = true; + } + } + break; + case DYLD_CHAINED_PTR_32: + if ( fixupLoc->generic32.bind.bind ) { + if ( fixupLoc->generic32.bind.addend > 31 ) { + addendTooLarge = true; + stop = true; + } + } + break; + } + }); + }); + } + else { + // scan bind opcodes for large addend + 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, bool lazyBind, uint64_t addend, bool& stop) { + if ( is64bit ) + addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI + if ( addend > 31 ) { + addendTooLarge = true; + stop = true; + } + }, + ^(const char* symbolName) { + }); + } + if ( addendTooLarge ) { + failureReason("bind addend too large"); + return false; + } + + // evict swift dylibs with split seg v1 info + if ( this->isSwiftLibrary() && this->isSplitSegV1() ) + return false; + + if ( hasChainedFixups() ) { + // Chained fixups assumes split seg v2. This is true for now as chained fixups is arm64e only + return this->isSplitSegV2(); + } + if ( !(isArch("x86_64") || isArch("x86_64h")) ) return true; __block bool rebasesOk = true; - Diagnostics diag; uint64_t startVMAddr = preferredLoadAddress(); uint64_t endVMAddr = startVMAddr + mappedSize(); forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) { @@ -2990,35 +3779,266 @@ bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReaso return rebasesOk; } +#if BUILDING_APP_CACHE_UTIL +bool MachOAnalyzer::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const +{ + if (!MachOFile::canBePlacedInKernelCollection(path, failureReason)) + return false; + + // App caches reguire that everything be built with split seg v2 + // This is because v1 can't move anything other than __TEXT and __DATA + // but kernels have __TEXT_EXEC and other segments + if ( isKextBundle() ) { + // x86_64 kext's might not have split seg + if ( !isArch("x86_64") && !isArch("x86_64h") ) { + if ( !isSplitSegV2() ) { + failureReason("Missing split seg v2"); + return false; + } + } + } else if ( isStaticExecutable() ) { + // The kernel must always have split seg V2 + if ( !isSplitSegV2() ) { + failureReason("Missing split seg v2"); + return false; + } + + // The kernel should have __TEXT and __TEXT_EXEC + __block bool foundText = false; + __block bool foundTextExec = false; + __block bool foundHIB = false; + __block uint64_t hibernateVMAddr = 0; + __block uint64_t hibernateVMSize = 0; + forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) { + if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) { + foundText = true; + } + if ( strcmp(segmentInfo.segName, "__TEXT_EXEC") == 0 ) { + foundTextExec = true; + } + if ( strcmp(segmentInfo.segName, "__HIB") == 0 ) { + foundHIB = true; + hibernateVMAddr = segmentInfo.vmAddr; + hibernateVMSize = segmentInfo.vmSize; + } + }); + if (!foundText) { + failureReason("Expected __TEXT segment"); + return false; + } + if ( foundTextExec && foundHIB ) { + failureReason("Expected __TEXT_EXEC or __HIB segment, but found both"); + return false; + } + if ( !foundTextExec && !foundHIB ) { + failureReason("Expected __TEXT_EXEC or __HIB segment, but found neither"); + return false; + } + + // The hibernate segment should be mapped before the base address + if ( foundHIB ) { + uint64_t baseAddress = preferredLoadAddress(); + if ( greaterThanAddOrOverflow(hibernateVMAddr, hibernateVMSize, baseAddress) ) { + failureReason("__HIB segment should be mapped before base address"); + return false; + } + } + } + + // Don't allow kext's to have load addresses + if ( isKextBundle() && (preferredLoadAddress() != 0) ) { + failureReason("Has load address"); + return false; + } + + if (hasChainedFixups()) { + if ( usesClassicRelocationsInKernelCollection() ) { + failureReason("Cannot use fixup chains with binary expecting classic relocations"); + return false; + } + + __block bool fixupsOk = true; + __block Diagnostics diag; + withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { + forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, + const dyld_chained_starts_in_segment* segInfo, bool& stop) { + // We only support inputs from a few pointer format types, so that we don't need to handle them all later + switch (segInfo->pointer_format) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + case DYLD_CHAINED_PTR_32_FIRMWARE: + failureReason("unsupported chained fixups pointer format"); + fixupsOk = false; + stop = true; + return; + case DYLD_CHAINED_PTR_64_OFFSET: + // arm64 kernel and kexts use this format + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + // arm64e kexts use this format + break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + failureReason("unsupported chained fixups pointer format"); + fixupsOk = false; + stop = true; + return; + default: + failureReason("unknown chained fixups pointer format"); + fixupsOk = false; + stop = true; + return; + } + + uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)this; + // Error if the fixup location is anything other than 4/8 byte aligned + if ( (vmOffset & 0x3) != 0 ) { + failureReason("fixup value is not 4-byte aligned"); + fixupsOk = false; + stop = true; + return; + } + + // We also must only need 30-bits for the chain format of the resulting cache + if ( vmOffset >= (1 << 30) ) { + failureReason("fixup value does not fit in 30-bits"); + fixupsOk = false; + stop = true; + return; + } + }); + }); + if (!fixupsOk) + return false; + } else { + // x86_64 xnu will have unaligned text/data fixups and fixups inside __HIB __text. + // We allow these as xnu is emitted with classic relocations + bool canHaveUnalignedFixups = usesClassicRelocationsInKernelCollection(); + canHaveUnalignedFixups |= ( isArch("x86_64") || isArch("x86_64h") ); + __block bool rebasesOk = true; + Diagnostics diag; + forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) { + // Error if the rebase location is anything other than 4/8 byte aligned + if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) { + failureReason("rebase value is not 4-byte aligned"); + rebasesOk = false; + stop = true; + return; + } + +#if BUILDING_APP_CACHE_UTIL + // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be + // negative. Adjust the fixups so that we don't think they are out of + // range of the number of bits we have + if ( isStaticExecutable() ) { + __block uint64_t baseAddress = ~0ULL; + forEachSegment(^(const SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + uint64_t textSegVMAddr = preferredLoadAddress(); + runtimeOffset = (textSegVMAddr + runtimeOffset) - baseAddress; + } +#endif + + // We also must only need 30-bits for the chain format of the resulting cache + if ( runtimeOffset >= (1 << 30) ) { + failureReason("rebase value does not fit in 30-bits"); + rebasesOk = false; + stop = true; + return; + } + }); + if (!rebasesOk) + return false; + + __block bool bindsOk = true; + forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char *symbolName, + bool weakImport, bool lazyBind, uint64_t addend, bool &stop) { + + // Don't validate branch fixups as we'll turn then in to direct jumps instead + if ( type == BIND_TYPE_TEXT_PCREL32 ) + return; + + // Error if the bind location is anything other than 4/8 byte aligned + if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) { + failureReason("bind value is not 4-byte aligned"); + bindsOk = false; + stop = true; + return; + } + + // We also must only need 30-bits for the chain format of the resulting cache + if ( runtimeOffset >= (1 << 30) ) { + failureReason("bind value does not fit in 30-bits"); + rebasesOk = false; + stop = true; + return; + } + }, ^(const char *symbolName) { + }); + if (!bindsOk) + return false; + } + + return true; +} + +#endif + +bool MachOAnalyzer::usesClassicRelocationsInKernelCollection() const { + // The xnu x86_64 static executable needs to do the i386->x86_64 transition + // so will be emitted with classic relocations + if ( isArch("x86_64") || isArch("x86_64h") ) { + return isStaticExecutable() || isFileSet(); + } + return false; +} + uint64_t MachOAnalyzer::chainStartsOffset() const +{ + const dyld_chained_fixups_header* header = chainedFixupsHeader(); + // old arm64e binary has no dyld_chained_fixups_header + if ( header == nullptr ) + return 0; + return header->starts_offset + ((uint8_t*)header - (uint8_t*)this); +} + +const dyld_chained_fixups_header* MachOAnalyzer::chainedFixupsHeader() const { Diagnostics diag; LinkEditInfo leInfo; getLinkEditPointers(diag, leInfo); if ( diag.hasError() || (leInfo.chainedFixups == nullptr) ) - return 0; + return nullptr; - const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff); - return header->starts_offset + ((uint8_t*)header - (uint8_t*)this); + return (dyld_chained_fixups_header*)getLinkEditContent(leInfo.layout, leInfo.chainedFixups->dataoff); +} + +uint16_t MachOAnalyzer::chainedPointerFormat(const dyld_chained_fixups_header* header) +{ + const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)header + header->starts_offset); + for (uint32_t i=0; i < startsInfo->seg_count; ++i) { + uint32_t segInfoOffset = startsInfo->seg_info_offset[i]; + // 0 offset means this segment has no fixups + if ( segInfoOffset == 0 ) + continue; + const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset); + if ( segInfo->page_count != 0 ) + return segInfo->pointer_format; + } + return 0; // no chains (perhaps no __DATA segment) } uint16_t MachOAnalyzer::chainedPointerFormat() const { - uint64_t infoOffset = chainStartsOffset(); - if ( infoOffset != 0 ) { + const dyld_chained_fixups_header* header = chainedFixupsHeader(); + if ( header != nullptr ) { // get pointer format from chain info struct in LINKEDIT - const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)this + infoOffset); - for (uint32_t i=0; i < startsInfo->seg_count; ++i) { - uint32_t segInfoOffset = startsInfo->seg_info_offset[i]; - // 0 offset means this segment has no fixups - if ( segInfoOffset == 0 ) - continue; - const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)startsInfo + segInfoOffset); - if ( segInfo->page_count != 0 ) - return segInfo->pointer_format; - } + return chainedPointerFormat(header); } - assert(this->cputype == CPU_TYPE_ARM64 && this->cpusubtype == CPU_SUBTYPE_ARM64E && "chainedPointerFormat() called on non-chained binary"); + assert(this->cputype == CPU_TYPE_ARM64 && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) && "chainedPointerFormat() called on non-chained binary"); return DYLD_CHAINED_PTR_ARM64E; } @@ -3050,7 +4070,7 @@ void MachOAnalyzer::withChainStarts(Diagnostics& diag, uint64_t startsStructOffs } #if SUPPORT_OLD_ARM64E_FORMAT // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up - else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) ) { + else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) { // old arm64e binary, create a dyld_chained_starts_in_image for caller uint64_t baseAddress = preferredLoadAddress(); BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer, leInfo.dyldInfo->bind_size + 512); @@ -3097,6 +4117,52 @@ void MachOAnalyzer::withChainStarts(Diagnostics& diag, uint64_t startsStructOffs } } +struct OldThreadsStartSection +{ + uint32_t padding : 31, + stride8 : 1; + uint32_t chain_starts[1]; +}; + +// ld64 can't sometimes determine the size of __thread_starts accurately, +// because these sections have to be given a size before everything is laid out, +// and you don't know the actual size of the chains until everything is +// laid out. In order to account for this, the linker puts trailing 0xFFFFFFFF at +// the end of the section, that must be ignored when walking the chains. This +// patch adjust the section size accordingly. +static uint32_t adjustStartsCount(uint32_t startsCount, const uint32_t* starts) { + for ( int i = startsCount; i > 0; --i ) + { + if ( starts[i - 1] == 0xFFFFFFFF ) + startsCount--; + else + break; + } + return startsCount; +} + +bool MachOAnalyzer::hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const +{ + if ( !this->isPreload() && !this->isStaticExecutable() ) + return false; + + uint64_t sectionSize; + if (const dyld_chained_starts_offsets* sect = (dyld_chained_starts_offsets*)this->findSectionContent("__TEXT", "__chain_starts", sectionSize) ) { + *pointerFormat = sect->pointer_format; + *startsCount = sect->starts_count; + *starts = §->chain_starts[0]; + return true; + } + if (const OldThreadsStartSection* sect = (OldThreadsStartSection*)this->findSectionContent("__TEXT", "__thread_starts", sectionSize) ) { + *pointerFormat = sect->stride8 ? DYLD_CHAINED_PTR_ARM64E : DYLD_CHAINED_PTR_ARM64E_FIRMWARE; + *startsCount = adjustStartsCount((uint32_t)(sectionSize/4) - 1, sect->chain_starts); + *starts = sect->chain_starts; + return true; + } + return false; +} + + MachOAnalyzer::ObjCInfo MachOAnalyzer::getObjCInfo() const { __block ObjCInfo result; @@ -3127,27 +4193,6 @@ MachOAnalyzer::ObjCInfo MachOAnalyzer::getObjCInfo() const return result; } -// Convert from a (possibly) live pointer to a vmAddr -static uint64_t convertToVMAddr(uint64_t value, MachOAnalyzer::VMAddrConverter vmAddrConverter) { - if ( vmAddrConverter.contentRebased ) { - // The value may have been signed. Strip the signature if that is the case -#if __has_feature(ptrauth_calls) - value = (uint64_t)__builtin_ptrauth_strip((void*)value, ptrauth_key_asia); -#endif - value -= vmAddrConverter.slide; - } - else if ( vmAddrConverter.chainedPointerFormat != 0 ) { - auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value; - uint64_t targetRuntimeOffset; - if ( chainedValue->isRebase(vmAddrConverter.chainedPointerFormat, vmAddrConverter.preferredLoadAddress, - targetRuntimeOffset) ) { - value = vmAddrConverter.preferredLoadAddress + targetRuntimeOffset; - } - } - - return value; -} - uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadOnlyDataField field, uint32_t pointerSize) const { if (pointerSize == 8) { typedef uint64_t PtrTy; @@ -3172,9 +4217,15 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide); switch (field) { case ObjCClassInfo::ReadOnlyDataField::name: - return convertToVMAddr(classData->nameVMAddr, vmAddrConverter); + return vmAddrConverter.convertToVMAddr(classData->nameVMAddr); + case ObjCClassInfo::ReadOnlyDataField::baseProtocols: + return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr); case ObjCClassInfo::ReadOnlyDataField::baseMethods: - return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter); + return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr); + case ObjCClassInfo::ReadOnlyDataField::baseProperties: + return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr); + case ObjCClassInfo::ReadOnlyDataField::flags: + return classData->flags; } } else { typedef uint32_t PtrTy; @@ -3199,9 +4250,15 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide); switch (field) { case ObjCClassInfo::ReadOnlyDataField::name: - return convertToVMAddr(classData->nameVMAddr, vmAddrConverter); + return vmAddrConverter.convertToVMAddr(classData->nameVMAddr); + case ObjCClassInfo::ReadOnlyDataField::baseProtocols: + return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr); case ObjCClassInfo::ReadOnlyDataField::baseMethods: - return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter); + return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr); + case ObjCClassInfo::ReadOnlyDataField::baseProperties: + return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr); + case ObjCClassInfo::ReadOnlyDataField::flags: + return classData->flags; } } } @@ -3257,8 +4314,8 @@ const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyz return; } - // We can't scan this section if its protected or not cstrings. - if ( sectInfo.segInfo.isProtected || ( (sectInfo.sectFlags & SECTION_TYPE) != S_CSTRING_LITERALS ) ) { + // We can't scan this section if its protected + if ( sectInfo.segInfo.isProtected ) { result = PrintableStringResult::ProtectedSection; stop = true; return; @@ -3292,6 +4349,15 @@ const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyz stop = true; }); +#if BUILDING_SHARED_CACHE_UTIL || BUILDING_DYLDINFO + // The shared cache coalesces strings in to their own section. + // Assume its a valid pointer + if (result == PrintableStringResult::UnknownSection) { + result = PrintableStringResult::CanPrint; + return (const char*)(stringVMAddr + getSlide()); + } +#endif + if (result == PrintableStringResult::CanPrint) return (const char*)(stringVMAddr + getSlide()); return nullptr; @@ -3370,20 +4436,14 @@ bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr, bool (^s return foundValidSection; } - -void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased, + +void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass, bool isMetaClass)) const { const uint64_t ptrSize = pointerSize(); intptr_t slide = getSlide(); - MachOAnalyzer::VMAddrConverter vmAddrConverter; - vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); - vmAddrConverter.slide = slide; - vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; - vmAddrConverter.contentRebased = contentRebased; - forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 ) return; @@ -3399,111 +4459,39 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased, if ( ptrSize == 8 ) { typedef uint64_t PtrTy; - struct objc_class_t { - uint64_t isaVMAddr; - uint64_t superclassVMAddr; - uint64_t methodCacheBuckets; - uint64_t methodCacheProperties; - uint64_t dataVMAddrAndFastFlags; - }; - // This matches "struct TargetClassMetadata" from Metadata.h in Swift - struct swift_class_metadata_t : objc_class_t { - uint32_t swiftClassFlags; - }; - enum : uint64_t { - FAST_DATA_MASK = 0x00007ffffffffff8ULL - }; + for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) { - uint64_t classVMAddr = convertToVMAddr(*(PtrTy*)(classList + i), vmAddrConverter); - uint64_t classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr); - uint64_t classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); - - // First call the handler on the class - const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide); - const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr; - ObjCClassInfo objcClass; - objcClass.isaVMAddr = convertToVMAddr(classPtr->isaVMAddr, vmAddrConverter); - objcClass.superclassVMAddr = convertToVMAddr(classPtr->superclassVMAddr, vmAddrConverter); - objcClass.dataVMAddr = convertToVMAddr(classPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK; - objcClass.vmAddrConverter = vmAddrConverter; - objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; - objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; - // The Swift class flags are only present if the class is swift - objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0; - handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false); - if (diag.hasError()) - return; - - // Then call it on the metaclass - const objc_class_t* metaClassPtr = (const objc_class_t*)(objcClass.isaVMAddr + slide); - const swift_class_metadata_t* swiftMetaClassPtr = (const swift_class_metadata_t*)metaClassPtr; - ObjCClassInfo objcMetaClass; - objcMetaClass.isaVMAddr = convertToVMAddr(metaClassPtr->isaVMAddr, vmAddrConverter); - objcMetaClass.superclassVMAddr = convertToVMAddr(metaClassPtr->superclassVMAddr, vmAddrConverter); - objcMetaClass.dataVMAddr = convertToVMAddr(metaClassPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK; - objcMetaClass.vmAddrConverter = vmAddrConverter; - objcMetaClass.isSwiftLegacy = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; - objcMetaClass.isSwiftStable = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; - // The Swift class flags are only present if the class is swift - objcMetaClass.swiftClassFlags = (objcMetaClass.isSwiftLegacy || objcMetaClass.isSwiftStable) ? swiftMetaClassPtr->swiftClassFlags : 0; - classSuperclassVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, superclassVMAddr); - classDataVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); - handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true); + uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i)); + parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) { + handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false); + if (classDiag.hasError()) + return; + + // Then parse and call for the metaclass + uint64_t isaVMAddr = objcClass.isaVMAddr; + parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) { + handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true); + }); + }); if (diag.hasError()) return; } } else { typedef uint32_t PtrTy; - struct objc_class_t { - uint32_t isaVMAddr; - uint32_t superclassVMAddr; - uint32_t methodCacheBuckets; - uint32_t methodCacheProperties; - uint32_t dataVMAddrAndFastFlags; - }; - // This matches "struct TargetClassMetadata" from Metadata.h in Swift - struct swift_class_metadata_t : objc_class_t { - uint32_t swiftClassFlags; - }; - enum : uint32_t { - FAST_DATA_MASK = 0xfffffffcUL - }; + for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) { - uint64_t classVMAddr = convertToVMAddr(*(PtrTy*)(classList + i), vmAddrConverter); - uint64_t classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr); - uint64_t classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); + uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i)); + parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) { + handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false); + if (classDiag.hasError()) + return; - // First call the handler on the class - const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide); - const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr; - ObjCClassInfo objcClass; - objcClass.isaVMAddr = convertToVMAddr(classPtr->isaVMAddr, vmAddrConverter); - objcClass.superclassVMAddr = convertToVMAddr(classPtr->superclassVMAddr, vmAddrConverter); - objcClass.dataVMAddr = convertToVMAddr(classPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK; - objcClass.vmAddrConverter = vmAddrConverter; - objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; - objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; - // The Swift class flags are only present if the class is swift - objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0; - handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false); - if (diag.hasError()) - return; - - // Then call it on the metaclass - const objc_class_t* metaClassPtr = (const objc_class_t*)(objcClass.isaVMAddr + slide); - const swift_class_metadata_t* swiftMetaClassPtr = (const swift_class_metadata_t*)metaClassPtr; - ObjCClassInfo objcMetaClass; - objcMetaClass.isaVMAddr = convertToVMAddr(metaClassPtr->isaVMAddr, vmAddrConverter); - objcMetaClass.superclassVMAddr = convertToVMAddr(metaClassPtr->superclassVMAddr, vmAddrConverter); - objcMetaClass.dataVMAddr = convertToVMAddr(metaClassPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK; - objcMetaClass.vmAddrConverter = vmAddrConverter; - objcMetaClass.isSwiftLegacy = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; - objcMetaClass.isSwiftStable = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; - // The Swift class flags are only present if the class is swift - objcMetaClass.swiftClassFlags = (objcMetaClass.isSwiftLegacy || objcMetaClass.isSwiftStable) ? swiftMetaClassPtr->swiftClassFlags : 0; - classSuperclassVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, superclassVMAddr); - classDataVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); - handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true); + // Then parse and call for the metaclass + uint64_t isaVMAddr = objcClass.isaVMAddr; + parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) { + handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true); + }); + }); if (diag.hasError()) return; } @@ -3511,18 +4499,90 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased, }); } -void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased, +void MachOAnalyzer::parseObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, + uint64_t classVMAddr, + void (^handler)(Diagnostics& diag, + uint64_t classSuperclassVMAddr, + uint64_t classDataVMAddr, + const ObjCClassInfo& objcClass)) const { + const uint64_t ptrSize = pointerSize(); + intptr_t slide = getSlide(); + + uint64_t classSuperclassVMAddr = 0; + uint64_t classDataVMAddr = 0; + ObjCClassInfo objcClass; + + if ( ptrSize == 8 ) { + struct objc_class_t { + uint64_t isaVMAddr; + uint64_t superclassVMAddr; + uint64_t methodCacheBuckets; + uint64_t methodCacheProperties; + uint64_t dataVMAddrAndFastFlags; + }; + // This matches "struct TargetClassMetadata" from Metadata.h in Swift + struct swift_class_metadata_t : objc_class_t { + uint32_t swiftClassFlags; + }; + enum : uint64_t { + FAST_DATA_MASK = 0x00007ffffffffff8ULL + }; + classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr); + classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); + + // First call the handler on the class + const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide); + const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr; + objcClass.isaVMAddr = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr); + objcClass.superclassVMAddr = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr); + objcClass.methodCacheVMAddr = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties); + objcClass.dataVMAddr = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK; + objcClass.vmAddrConverter = vmAddrConverter; + objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; + objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; + // The Swift class flags are only present if the class is swift + objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0; + } else { + struct objc_class_t { + uint32_t isaVMAddr; + uint32_t superclassVMAddr; + uint32_t methodCacheBuckets; + uint32_t methodCacheProperties; + uint32_t dataVMAddrAndFastFlags; + }; + // This matches "struct TargetClassMetadata" from Metadata.h in Swift + struct swift_class_metadata_t : objc_class_t { + uint32_t swiftClassFlags; + }; + enum : uint32_t { + FAST_DATA_MASK = 0xfffffffcUL + }; + classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr); + classDataVMAddr = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags); + + // First call the handler on the class + const objc_class_t* classPtr = (const objc_class_t*)(classVMAddr + slide); + const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr; + objcClass.isaVMAddr = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr); + objcClass.superclassVMAddr = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr); + objcClass.methodCacheVMAddr = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties); + objcClass.dataVMAddr = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK; + objcClass.vmAddrConverter = vmAddrConverter; + objcClass.isSwiftLegacy = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY; + objcClass.isSwiftStable = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE; + // The Swift class flags are only present if the class is swift + objcClass.swiftClassFlags = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0; + } + + handler(diag, classSuperclassVMAddr, classDataVMAddr, objcClass); +} + +void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const { const uint64_t ptrSize = pointerSize(); intptr_t slide = getSlide(); - MachOAnalyzer::VMAddrConverter vmAddrConverter; - vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); - vmAddrConverter.slide = slide; - vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; - vmAddrConverter.contentRebased = contentRebased; - forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 ) return; @@ -3547,16 +4607,16 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased, PtrTy instancePropertiesVMAddr; }; for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) { - uint64_t categoryVMAddr = convertToVMAddr(*(PtrTy*)(categoryList + i), vmAddrConverter); + uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i)); const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide); ObjCCategory objCCategory; - objCCategory.nameVMAddr = convertToVMAddr(categoryPtr->nameVMAddr, vmAddrConverter); - objCCategory.clsVMAddr = convertToVMAddr(categoryPtr->clsVMAddr, vmAddrConverter); - objCCategory.instanceMethodsVMAddr = convertToVMAddr(categoryPtr->instanceMethodsVMAddr, vmAddrConverter); - objCCategory.classMethodsVMAddr = convertToVMAddr(categoryPtr->classMethodsVMAddr, vmAddrConverter); - objCCategory.protocolsVMAddr = convertToVMAddr(categoryPtr->protocolsVMAddr, vmAddrConverter); - objCCategory.instancePropertiesVMAddr = convertToVMAddr(categoryPtr->instancePropertiesVMAddr, vmAddrConverter); + objCCategory.nameVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr); + objCCategory.clsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr); + objCCategory.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr); + objCCategory.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr); + objCCategory.protocolsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr); + objCCategory.instancePropertiesVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr); handler(diag, categoryVMAddr, objCCategory); if (diag.hasError()) return; @@ -3572,16 +4632,16 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased, PtrTy instancePropertiesVMAddr; }; for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) { - uint64_t categoryVMAddr = convertToVMAddr(*(PtrTy*)(categoryList + i), vmAddrConverter); + uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i)); const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide); ObjCCategory objCCategory; - objCCategory.nameVMAddr = convertToVMAddr(categoryPtr->nameVMAddr, vmAddrConverter); - objCCategory.clsVMAddr = convertToVMAddr(categoryPtr->clsVMAddr, vmAddrConverter); - objCCategory.instanceMethodsVMAddr = convertToVMAddr(categoryPtr->instanceMethodsVMAddr, vmAddrConverter); - objCCategory.classMethodsVMAddr = convertToVMAddr(categoryPtr->classMethodsVMAddr, vmAddrConverter); - objCCategory.protocolsVMAddr = convertToVMAddr(categoryPtr->protocolsVMAddr, vmAddrConverter); - objCCategory.instancePropertiesVMAddr = convertToVMAddr(categoryPtr->instancePropertiesVMAddr, vmAddrConverter); + objCCategory.nameVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr); + objCCategory.clsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr); + objCCategory.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr); + objCCategory.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr); + objCCategory.protocolsVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr); + objCCategory.instancePropertiesVMAddr = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr); handler(diag, categoryVMAddr, objCCategory); if (diag.hasError()) return; @@ -3590,18 +4650,12 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased, }); } -void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased, +void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const { const uint64_t ptrSize = pointerSize(); intptr_t slide = getSlide(); - MachOAnalyzer::VMAddrConverter vmAddrConverter; - vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); - vmAddrConverter.slide = slide; - vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; - vmAddrConverter.contentRebased = contentRebased; - forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 ) return; @@ -3634,19 +4688,17 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased, PtrTy classPropertiesVMAddr; }; for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) { - uint64_t protocolVMAddr = convertToVMAddr(*(PtrTy*)(protocolList + i), vmAddrConverter); + uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i)); const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide); ObjCProtocol objCProtocol; - objCProtocol.isaVMAddr = convertToVMAddr(protocolPtr->isaVMAddr, vmAddrConverter); - objCProtocol.nameVMAddr = convertToVMAddr(protocolPtr->nameVMAddr, vmAddrConverter); - objCProtocol.instanceMethodsVMAddr = convertToVMAddr(protocolPtr->instanceMethodsVMAddr, vmAddrConverter); - objCProtocol.classMethodsVMAddr = convertToVMAddr(protocolPtr->classMethodsVMAddr, vmAddrConverter); - objCProtocol.optionalInstanceMethodsVMAddr = convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr, vmAddrConverter); - objCProtocol.optionalClassMethodsVMAddr = convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr, vmAddrConverter); - - // Track if this protocol needs a reallocation in objc - objCProtocol.requiresObjCReallocation = protocolPtr->size < sizeof(protocol_t); + objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr); + objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr); + objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr); + objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr); + objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr); + objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr); + objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr); handler(diag, protocolVMAddr, objCProtocol); if (diag.hasError()) @@ -3671,19 +4723,17 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased, PtrTy classPropertiesVMAddr; }; for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) { - uint64_t protocolVMAddr = convertToVMAddr(*(PtrTy*)(protocolList + i), vmAddrConverter); + uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i)); const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide); ObjCProtocol objCProtocol; - objCProtocol.isaVMAddr = convertToVMAddr(protocolPtr->isaVMAddr, vmAddrConverter); - objCProtocol.nameVMAddr = convertToVMAddr(protocolPtr->nameVMAddr, vmAddrConverter); - objCProtocol.instanceMethodsVMAddr = convertToVMAddr(protocolPtr->instanceMethodsVMAddr, vmAddrConverter); - objCProtocol.classMethodsVMAddr = convertToVMAddr(protocolPtr->classMethodsVMAddr, vmAddrConverter); - objCProtocol.optionalInstanceMethodsVMAddr = convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr, vmAddrConverter); - objCProtocol.optionalClassMethodsVMAddr = convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr, vmAddrConverter); - - // Track if this protocol needs a reallocation in objc - objCProtocol.requiresObjCReallocation = protocolPtr->size < sizeof(protocol_t); + objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr); + objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr); + objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr); + objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr); + objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr); + objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr); + objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr); handler(diag, protocolVMAddr, objCProtocol); if (diag.hasError()) @@ -3693,20 +4743,15 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased, }); } -void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased, - void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const { +void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method), + bool* isRelativeMethodList) const { if ( methodListVMAddr == 0 ) return; const uint64_t ptrSize = pointerSize(); intptr_t slide = getSlide(); - MachOAnalyzer::VMAddrConverter vmAddrConverter; - vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); - vmAddrConverter.slide = slide; - vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; - vmAddrConverter.contentRebased = contentRebased; - if ( ptrSize == 8 ) { typedef uint64_t PtrTy; struct method_list_t { @@ -3715,7 +4760,15 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb PtrTy methodArrayBase; // Note this is the start the array method_t[0] uint32_t getEntsize() const { - return (entsize) & ~(uint32_t)3; + return entsize & ObjCMethodList::methodListSizeMask; + } + + bool usesDirectOffsetsToSelectors() const { + return (entsize & 0x40000000) != 0; + } + + bool usesRelativeOffsets() const { + return (entsize & 0x80000000) != 0; } }; @@ -3725,19 +4778,44 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb PtrTy impVMAddr; // IMP }; + struct relative_method_t { + int32_t nameOffset; // SEL* + int32_t typesOffset; // const char * + int32_t impOffset; // IMP + }; + const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide); + if ( methodList == nullptr ) + return; + bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors(); uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase); for (unsigned i = 0; i != methodList->count; ++i) { uint64_t methodEntryOffset = i * methodList->getEntsize(); uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset; - const method_t* methodPtr = (const method_t*)(methodVMAddr + slide); ObjCMethod method; - method.nameVMAddr = convertToVMAddr(methodPtr->nameVMAddr, vmAddrConverter); - method.typesVMAddr = convertToVMAddr(methodPtr->typesVMAddr, vmAddrConverter); - method.impVMAddr = convertToVMAddr(methodPtr->impVMAddr, vmAddrConverter); - method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr); + if ( methodList->usesRelativeOffsets() ) { + const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide); + if ( relativeMethodListsAreOffsetsToSelectors ) { + method.nameVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset; + } else { + PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset); + method.nameVMAddr = vmAddrConverter.convertToVMAddr(*nameLocation); + } + method.typesVMAddr = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset; + method.impVMAddr = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset; + method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset; + } else { + const method_t* methodPtr = (const method_t*)(methodVMAddr + slide); + method.nameVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr); + method.typesVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr); + method.impVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr); + method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr); + } handler(methodVMAddr, method); } + + if ( isRelativeMethodList != nullptr ) + *isRelativeMethodList = methodList->usesRelativeOffsets(); } else { typedef uint32_t PtrTy; struct method_list_t { @@ -3746,7 +4824,15 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb PtrTy methodArrayBase; // Note this is the start the array method_t[0] uint32_t getEntsize() const { - return (entsize) & ~(uint32_t)3; + return entsize & ObjCMethodList::methodListSizeMask; + } + + bool usesDirectOffsetsToSelectors() const { + return (entsize & 0x40000000) != 0; + } + + bool usesRelativeOffsets() const { + return (entsize & 0x80000000) != 0; } }; @@ -3756,34 +4842,216 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb PtrTy impVMAddr; // IMP }; + struct relative_method_t { + int32_t nameOffset; // SEL* + int32_t typesOffset; // const char * + int32_t impOffset; // IMP + }; + const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide); + if ( methodList == nullptr ) + return; + bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors(); uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase); for (unsigned i = 0; i != methodList->count; ++i) { uint64_t methodEntryOffset = i * methodList->getEntsize(); uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset; - const method_t* methodPtr = (const method_t*)(methodVMAddr + slide); ObjCMethod method; - method.nameVMAddr = convertToVMAddr(methodPtr->nameVMAddr, vmAddrConverter); - method.typesVMAddr = convertToVMAddr(methodPtr->typesVMAddr, vmAddrConverter); - method.impVMAddr = convertToVMAddr(methodPtr->impVMAddr, vmAddrConverter); - method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr); + if ( methodList->usesRelativeOffsets() ) { + const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide); + if ( relativeMethodListsAreOffsetsToSelectors ) { + method.nameVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset; + } else { + PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset); + method.nameVMAddr = vmAddrConverter.convertToVMAddr(*nameLocation); + } + method.typesVMAddr = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset; + method.impVMAddr = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset; + method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset; + } else { + const method_t* methodPtr = (const method_t*)(methodVMAddr + slide); + method.nameVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr); + method.typesVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr); + method.impVMAddr = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr); + method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr); + } handler(methodVMAddr, method); } + + if ( isRelativeMethodList != nullptr ) + *isRelativeMethodList = methodList->usesRelativeOffsets(); + } +} + +void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const { + if ( propertyListVMAddr == 0 ) + return; + + const uint64_t ptrSize = pointerSize(); + intptr_t slide = getSlide(); + + if ( ptrSize == 8 ) { + typedef uint64_t PtrTy; + struct property_list_t { + uint32_t entsize; + uint32_t count; + PtrTy propertyArrayBase; // Note this is the start the array property_t[0] + + uint32_t getEntsize() const { + return (entsize) & ~(uint32_t)3; + } + }; + + struct property_t { + PtrTy nameVMAddr; // SEL + PtrTy attributesVMAddr; // const char * + }; + + const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide); + uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase); + for (unsigned i = 0; i != propertyList->count; ++i) { + uint64_t propertyEntryOffset = i * propertyList->getEntsize(); + uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset; + const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide); + ObjCProperty property; + property.nameVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr); + property.attributesVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr); + handler(propertyVMAddr, property); + } + } else { + typedef uint32_t PtrTy; + struct property_list_t { + uint32_t entsize; + uint32_t count; + PtrTy propertyArrayBase; // Note this is the start the array property_t[0] + + uint32_t getEntsize() const { + return (entsize) & ~(uint32_t)3; + } + }; + + struct property_t { + PtrTy nameVMAddr; // SEL + PtrTy attributesVMAddr; // const char * + }; + + const property_list_t* propertyList = (const property_list_t*)(propertyListVMAddr + slide); + uint64_t propertyListArrayBaseVMAddr = propertyListVMAddr + offsetof(property_list_t, propertyArrayBase); + for (unsigned i = 0; i != propertyList->count; ++i) { + uint64_t propertyEntryOffset = i * propertyList->getEntsize(); + uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset; + const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide); + ObjCProperty property; + property.nameVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr); + property.attributesVMAddr = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr); + handler(propertyVMAddr, property); + } + } +} + +void MachOAnalyzer::forEachObjCProtocol(uint64_t protocolListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t protocolRefVMAddr, const ObjCProtocol&)) const +{ + if ( protocolListVMAddr == 0 ) + return; + + auto ptrSize = pointerSize(); + intptr_t slide = getSlide(); + + if ( ptrSize == 8 ) { + typedef uint64_t PtrTy; + struct protocol_ref_t { + PtrTy refVMAddr; + }; + struct protocol_list_t { + PtrTy count; + protocol_ref_t array[]; + }; + struct protocol_t { + PtrTy isaVMAddr; + PtrTy nameVMAddr; + PtrTy protocolsVMAddr; + PtrTy instanceMethodsVMAddr; + PtrTy classMethodsVMAddr; + PtrTy optionalInstanceMethodsVMAddr; + PtrTy optionalClassMethodsVMAddr; + PtrTy instancePropertiesVMAddr; + uint32_t size; + uint32_t flags; + // Fields below this point are not always present on disk. + PtrTy extendedMethodTypesVMAddr; + PtrTy demangledNameVMAddr; + PtrTy classPropertiesVMAddr; + }; + + const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide); + for (PtrTy i = 0; i != protoList->count; ++i) { + uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr); + + const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide); + ObjCProtocol objCProtocol; + objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr); + objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr); + objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr); + objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr); + objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr); + objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr); + objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr); + + handler(protocolVMAddr, objCProtocol); + } + } else { + typedef uint32_t PtrTy; + struct protocol_ref_t { + PtrTy refVMAddr; + }; + struct protocol_list_t { + PtrTy count; + protocol_ref_t array[]; + }; + struct protocol_t { + PtrTy isaVMAddr; + PtrTy nameVMAddr; + PtrTy protocolsVMAddr; + PtrTy instanceMethodsVMAddr; + PtrTy classMethodsVMAddr; + PtrTy optionalInstanceMethodsVMAddr; + PtrTy optionalClassMethodsVMAddr; + PtrTy instancePropertiesVMAddr; + uint32_t size; + uint32_t flags; + // Fields below this point are not always present on disk. + PtrTy extendedMethodTypesVMAddr; + PtrTy demangledNameVMAddr; + PtrTy classPropertiesVMAddr; + }; + + const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide); + for (PtrTy i = 0; i != protoList->count; ++i) { + uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr); + + const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide); + ObjCProtocol objCProtocol; + objCProtocol.isaVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr); + objCProtocol.nameVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr); + objCProtocol.protocolsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr); + objCProtocol.instanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr); + objCProtocol.classMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr); + objCProtocol.optionalInstanceMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr); + objCProtocol.optionalClassMethodsVMAddr = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr); + + handler(protocolVMAddr, objCProtocol); + } } } -void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased, +void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const { const uint64_t ptrSize = pointerSize(); intptr_t slide = getSlide(); - MachOAnalyzer::VMAddrConverter vmAddrConverter; - vmAddrConverter.preferredLoadAddress = preferredLoadAddress(); - vmAddrConverter.slide = slide; - vmAddrConverter.chainedPointerFormat = hasChainedFixups() ? chainedPointerFormat() : 0; - vmAddrConverter.contentRebased = contentRebased; - forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 ) return; @@ -3802,7 +5070,7 @@ void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool content typedef uint64_t PtrTy; for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) { uint64_t selRefVMAddr = selRefSectionVMAddr + i; - uint64_t selRefTargetVMAddr = convertToVMAddr(*(PtrTy*)(selRefs + i), vmAddrConverter); + uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i)); handler(selRefVMAddr, selRefTargetVMAddr); if (diag.hasError()) { stop = true; @@ -3813,7 +5081,7 @@ void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool content typedef uint32_t PtrTy; for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) { uint64_t selRefVMAddr = selRefSectionVMAddr + i; - uint64_t selRefTargetVMAddr = convertToVMAddr(*(PtrTy*)(selRefs + i), vmAddrConverter); + uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i)); handler(selRefVMAddr, selRefTargetVMAddr); if (diag.hasError()) { stop = true; @@ -3914,7 +5182,7 @@ uint32_t MachOAnalyzer::loadCommandsFreeSpace() const } void MachOAnalyzer::forEachWeakDef(Diagnostics& diag, - void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const { + void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const { uint64_t baseAddress = preferredLoadAddress(); forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { if ( (n_desc & N_WEAK_DEF) != 0 ) { @@ -3933,6 +5201,7 @@ void MachOAnalyzer::forEachWeakDef(Diagnostics& diag, }); } + } // dyld3 diff --git a/dyld3/MachOAnalyzer.h b/dyld3/MachOAnalyzer.h index 757ab6e..e503fc9 100644 --- a/dyld3/MachOAnalyzer.h +++ b/dyld3/MachOAnalyzer.h @@ -44,7 +44,23 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded using MachOLoaded::getDylibInstallName; using MachOLoaded::FoundSymbol; using MachOLoaded::findExportedSymbol; + using MachOLoaded::forEachGlobalSymbol; + using MachOLoaded::forEachLocalSymbol; + using MachOFile::canBePlacedInDyldCache; + using MachOFile::forEachLoadCommand; + using MachOFile::removeLoadCommand; + enum class Rebase { + unknown, + pointer32, + pointer64, + textPCrel32, + textAbsolute32, + }; + + static bool loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem, + const char* path, const GradedArchs& archs, Platform platform, + closure::LoadedFileInfo& info); static closure::LoadedFileInfo load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* logicalPath, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN]); static const MachOAnalyzer* validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength, @@ -52,26 +68,47 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded typedef void (^ExportsCallback)(const char* symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char* importName, bool& stop); - bool validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const GradedArchs& archs, Platform platform) const; + bool validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const GradedArchs& archs, Platform platform, bool isOSBinary) const; + + // Caches data useful for converting from raw data to VM addresses + struct VMAddrConverter { + uint64_t preferredLoadAddress = 0; + intptr_t slide = 0; + uint16_t chainedPointerFormat = 0; + bool contentRebased = false; +#if !(BUILDING_LIBDYLD || BUILDING_DYLD) + enum class SharedCacheFormat : uint8_t { + none = 0, + v2_x86_64_tbi = 1, + v3 = 2 + }; + SharedCacheFormat sharedCacheChainedPointerFormat = SharedCacheFormat::none; +#endif + + uint64_t convertToVMAddr(uint64_t v) const; + }; + + VMAddrConverter makeVMAddrConverter(bool contentRebased) const; + uint64_t mappedSize() const; bool hasObjC() const; bool hasPlusLoadMethod(Diagnostics& diag) const; + bool usesObjCGarbageCollection() const; + bool isSwiftLibrary() 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 hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset) const; void forEachCDHash(void (^handler)(const 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 getEntry(uint64_t& offset, bool& usesCRT) const; bool isSlideable() const; - bool hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache=nullptr) const; + bool hasInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, const void* dyldCache=nullptr) const; void forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop)) const; - void forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) const; - bool hasTerminators(Diagnostics& diag, bool contentRebased) const; - void forEachTerminator(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset)) const; + void forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) const; + bool hasTerminators(Diagnostics& diag, const VMAddrConverter& vmAddrConverter) const; + void forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const; void forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const; uint32_t segmentCount() const; void forEachExportedSymbol(Diagnostics& diag, ExportsCallback callback) const; @@ -88,26 +125,36 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded const void* getBindOpcodes(uint32_t& size) const; const void* getLazyBindOpcodes(uint32_t& size) const; const void* getSplitSeg(uint32_t& size) const; + bool hasSplitSeg() const; + bool isSplitSegV1() const; + bool isSplitSegV2() 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 forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool isLazyPointerRebase, 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, bool lazyBind, uint64_t addend, bool& stop), - void (^strongHandler)(const char* symbolName), - void (^missingLazyBindHandler)()) const; + void (^strongHandler)(const char* symbolName)) const; + void forEachBind(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName, + bool weakImport, bool lazyBind, 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; - bool canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) 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 segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop)) 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, bool lazyBind, uint64_t addend, bool& stop), - void (^strongHandler)(const char* symbolName), - void (^missingLazyBindHandler)()) const; + void (^strongHandler)(const char* symbolName)) const; bool canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const; + bool canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const; +#if BUILDING_APP_CACHE_UTIL + bool canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const; +#endif + bool usesClassicRelocationsInKernelCollection() const; uint32_t loadCommandsFreeSpace() const; + bool hasStompedLazyOpcodes() const; #if DEBUG void validateDyldCacheDylib(Diagnostics& diag, const char* path) const; @@ -115,9 +162,16 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded void withChainStarts(Diagnostics& diag, uint64_t startsStructOffsetHint, void (^callback)(const dyld_chained_starts_in_image*)) const; uint64_t chainStartsOffset() const; uint16_t chainedPointerFormat() const; + static uint16_t chainedPointerFormat(const dyld_chained_fixups_header* chainHeader); bool hasUnalignedPointerFixups() const; + const dyld_chained_fixups_header* chainedFixupsHeader() const; + bool hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const; + bool isOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) const; // checks if binary is codesigned to be part of the OS + static bool sliceIsOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize); const MachOAnalyzer* remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const; + + bool markNeverUnload(Diagnostics &diag) const; struct ObjCInfo { uint32_t selRefCount; @@ -140,20 +194,12 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded dyld3::OverflowSafeArray sectionInfos = { buffer, sizeof(buffer) / sizeof(buffer[0]) }; }; - // Caches data useful for converting from raw data to VM addresses - struct VMAddrConverter { - uint64_t preferredLoadAddress = 0; - intptr_t slide = 0; - uint16_t chainedPointerFormat = 0; - bool contentRebased = false; - }; - struct ObjCClassInfo { // These fields are all present on the objc_class_t struct uint64_t isaVMAddr = 0; uint64_t superclassVMAddr = 0; //uint64_t methodCacheBuckets; - //uint64_t methodCacheProperties; + uint64_t methodCacheVMAddr = 0; uint64_t dataVMAddr = 0; // This field is only present if this is a Swift object, ie, has the Swift @@ -170,16 +216,28 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded // These are from the class_ro_t which data points to enum class ReadOnlyDataField { name, - baseMethods + baseProtocols, + baseMethods, + baseProperties, + flags }; uint64_t getReadOnlyDataField(ReadOnlyDataField field, uint32_t pointerSize) const; uint64_t nameVMAddr(uint32_t pointerSize) const { return getReadOnlyDataField(ReadOnlyDataField::name, pointerSize); } + uint64_t baseProtocolsVMAddr(uint32_t pointerSize) const { + return getReadOnlyDataField(ReadOnlyDataField::baseProtocols, pointerSize); + } uint64_t baseMethodsVMAddr(uint32_t pointerSize) const { return getReadOnlyDataField(ReadOnlyDataField::baseMethods, pointerSize); } + uint64_t basePropertiesVMAddr(uint32_t pointerSize) const { + return getReadOnlyDataField(ReadOnlyDataField::baseProperties, pointerSize); + } + uint64_t flags(uint32_t pointerSize) const { + return getReadOnlyDataField(ReadOnlyDataField::flags, pointerSize); + } // These are embedded in the Mach-O itself by the compiler enum FastDataBits { @@ -205,6 +263,19 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded } }; + struct ObjCMethodList { + // This matches the bits in the objc runtime + enum : uint32_t { + methodListIsUniqued = 0x1, + methodListIsSorted = 0x2, + + // The size is bits 2 through 16 of the entsize field + // The low 2 bits are uniqued/sorted as above. The upper 16-bits + // are reserved for other flags + methodListSizeMask = 0x0000FFFC + }; + }; + struct ObjCImageInfo { uint32_t version; uint32_t flags; @@ -225,6 +296,11 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded uint64_t nameLocationVMAddr; }; + struct ObjCProperty { + uint64_t nameVMAddr; // & const char * + uint64_t attributesVMAddr; // & const char * + }; + struct ObjCCategory { uint64_t nameVMAddr; uint64_t clsVMAddr; @@ -237,7 +313,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded struct ObjCProtocol { uint64_t isaVMAddr; uint64_t nameVMAddr; - //uint64_t protocolsVMAddr; + uint64_t protocolsVMAddr; uint64_t instanceMethodsVMAddr; uint64_t classMethodsVMAddr; uint64_t optionalInstanceMethodsVMAddr; @@ -249,10 +325,6 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded //uint64_t extendedMethodTypesVMAddr; //uint64_t demangledNameVMAddr; //uint64_t classPropertiesVMAddr; - - // Note this isn't in a protocol, but we use it in dyld to track if the protocol - // is large enough to avoid a reallocation in objc. - bool requiresObjCReallocation; }; enum class PrintableStringResult { @@ -265,26 +337,42 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded const char* getPrintableString(uint64_t stringVMAddr, PrintableStringResult& result, SectionCache* sectionCache = nullptr, bool (^sectionHandler)(const SectionInfo& sectionInfo) = nullptr) const; - - void forEachObjCClass(Diagnostics& diag, bool contentRebased, + + void parseObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, + uint64_t classVMAddr, + void (^handler)(Diagnostics& diag, + uint64_t classSuperclassVMAddr, + uint64_t classDataVMAddr, + const ObjCClassInfo& objcClass)) const; + + void forEachObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass, bool isMetaClass)) const; - void forEachObjCCategory(Diagnostics& diag, bool contentRebased, + void forEachObjCCategory(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const; - void forEachObjCProtocol(Diagnostics& diag, bool contentRebased, + // lists all Protocols defined in the image + void forEachObjCProtocol(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(Diagnostics& diag, uint64_t protocolVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const; // Walk a method list starting from its vmAddr. // Note, classes, categories, protocols, etc, all share the same method list struture so can all use this. - void forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased, - void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const; + void forEachObjCMethod(uint64_t methodListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method), + bool* isRelativeMethodList = nullptr) const; - void forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased, + void forEachObjCProperty(uint64_t propertyListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const; + + // lists all Protocols on a protocol_list_t + void forEachObjCProtocol(uint64_t protocolListVMAddr, const VMAddrConverter& vmAddrConverter, + void (^handler)(uint64_t protocolRefVMAddr, const ObjCProtocol& protocol)) const; + + void forEachObjCSelectorReference(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const; void forEachObjCMethodName(void (^handler)(const char* methodName)) const; @@ -293,7 +381,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded const ObjCImageInfo* objcImageInfo() const; - void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const; + void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const; private: @@ -307,7 +395,8 @@ private: segSize : 61; }; - enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder, textPermissions, executableData, codeSigAlignment }; + enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder, + textPermissions, executableData, writableData, codeSigAlignment, sectionsAddrRangeWithinSegment }; bool enforceFormat(Malformed) const; const uint8_t* getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const; @@ -320,9 +409,10 @@ private: 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 validChainedFixupsInfoOldArm64e(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 segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) 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; @@ -335,19 +425,20 @@ private: void getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const; bool segmentHasTextRelocs(uint32_t segIndex) const; - uint64_t relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const; + uint64_t localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const; + uint64_t externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const; bool segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const; void parseOrgArm64eChainedFixups(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, uint16_t format, bool& stop)) const; bool contentIsRegularStub(const uint8_t* helperContent) const; - uint64_t entryAddrFromThreadCmd(const thread_command* cmd) const; void recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end, OverflowSafeArray& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const; void analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const; }; + } // namespace dyld3 #endif /* MachOAnalyzer_h */ diff --git a/dyld3/MachOAnalyzerSet.cpp b/dyld3/MachOAnalyzerSet.cpp new file mode 100644 index 0000000..3ca24c7 --- /dev/null +++ b/dyld3/MachOAnalyzerSet.cpp @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOAnalyzerSet.h" +#include "DyldSharedCache.h" + +#if BUILDING_DYLD + namespace dyld { void log(const char*, ...); } +#endif + +namespace dyld3 { + +static bool hasHigh8(uint64_t addend) +{ + // distinguish negative addend from TBI + if ( (addend >> 56) == 0 ) + return false; + return ( (addend >> 48) != 0xFFFF ); +} + +void MachOAnalyzerSet::WrappedMachO::forEachBind(Diagnostics& diag, FixUpHandler fixUpHandler, CachePatchHandler patchHandler) const +{ + const bool is64 = _mh->is64(); + __block int lastLibOrdinal = 256; + __block const char* lastSymbolName = nullptr; + __block uint64_t lastAddend = 0; + __block FixupTarget target; + __block PointerMetaData pmd; + _mh->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) { + if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) { + // same symbol lookup as last location + fixUpHandler(runtimeOffset, pmd, target, stop); + } + else if ( this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, lazyBind, addend, patchHandler, target) ) { + pmd.high8 = 0; + if ( is64 && (target.addend != 0) ) { + if ( hasHigh8(target.addend) ) { + pmd.high8 = (target.addend >> 56); + target.offsetInImage &= 0x00FFFFFFFFFFFFFFULL; + target.addend &= 0x00FFFFFFFFFFFFFFULL; + } + } + if ( !target.skippableWeakDef ) { + fixUpHandler(runtimeOffset, pmd, target, stop); + lastSymbolName = symbolName; + lastLibOrdinal = libOrdinal; + lastAddend = addend; + } + } + else { + // call handler with missing symbol before stopping + if ( target.kind == FixupTarget::Kind::bindMissingSymbol ) + fixUpHandler(runtimeOffset, pmd, target, stop); + stop = true; + } + }, ^(const char* symbolName) { + }); +} + +MachOAnalyzerSet::PointerMetaData::PointerMetaData() +{ + this->diversity = 0; + this->high8 = 0; + this->authenticated = 0; + this->key = 0; + this->usesAddrDiversity = 0; +} + +MachOAnalyzerSet::PointerMetaData::PointerMetaData(const MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format) +{ + this->diversity = 0; + this->high8 = 0; + this->authenticated = 0; + this->key = 0; + this->usesAddrDiversity = 0; + switch ( pointer_format ) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + this->authenticated = fixupLoc->arm64e.authRebase.auth; + if ( this->authenticated ) { + this->key = fixupLoc->arm64e.authRebase.key; + this->usesAddrDiversity = fixupLoc->arm64e.authRebase.addrDiv; + this->diversity = fixupLoc->arm64e.authRebase.diversity; + } + else if ( fixupLoc->arm64e.bind.bind == 0 ) { + this->high8 = fixupLoc->arm64e.rebase.high8; + } + break; + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + if ( fixupLoc->generic64.bind.bind == 0 ) + this->high8 = fixupLoc->generic64.rebase.high8; + break; + } +} + + +void MachOAnalyzerSet::WrappedMachO::forEachFixup(Diagnostics& diag, FixUpHandler fixup, CachePatchHandler patcher) const +{ + uint16_t fmPointerFormat; + uint32_t fmStartsCount; + const uint32_t* fmStarts; + const MachOAnalyzer* ma = _mh; + const uint64_t prefLoadAddr = ma->preferredLoadAddress(); + if ( ma->hasChainedFixups() ) { + // build targets table + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(FixupTarget, targets, 512); + ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { + targets.default_constuct_back(); + FixupTarget& foundTarget = targets.back(); + if ( !this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, false, addend, patcher, foundTarget) ) { + // call handler with missing symbol before stopping + if ( foundTarget.kind == FixupTarget::Kind::bindMissingSymbol ) + fixup(0, PointerMetaData(), foundTarget, stop); + stop = true; + } + }); + if ( diag.hasError() ) + return; + + // walk all chains + ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { + ma->forEachFixupInAllChains(diag, startsInfo, false, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, + const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) { + uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + uint64_t targetOffset; + uint32_t bindOrdinal; + int64_t embeddedAddend; + PointerMetaData pmd(fixupLoc, segInfo->pointer_format); + if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, embeddedAddend) ) { + if ( bindOrdinal < targets.count() ) { + if ( embeddedAddend == 0 ) { + if ( hasHigh8(targets[bindOrdinal].addend) ) { + FixupTarget targetWithoutHigh8 = targets[bindOrdinal]; + pmd.high8 = (targetWithoutHigh8.addend >> 56); + targetWithoutHigh8.offsetInImage &= 0x00FFFFFFFFFFFFFFULL; + targetWithoutHigh8.addend &= 0x00FFFFFFFFFFFFFFULL; + fixup(fixupOffset, pmd, targetWithoutHigh8, fixupsStop); + } + else { + fixup(fixupOffset, pmd, targets[bindOrdinal], fixupsStop); + } + } + else { + // pointer on disk encodes extra addend, make pseudo target for that + FixupTarget targetWithAddend = targets[bindOrdinal]; + targetWithAddend.addend += embeddedAddend; + targetWithAddend.offsetInImage += embeddedAddend; + fixup(fixupOffset, pmd, targetWithAddend, fixupsStop); + } + } + else { + diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, targets.count()); + fixupsStop = true; + } + } + else if ( fixupLoc->isRebase(segInfo->pointer_format, prefLoadAddr, targetOffset) ) { + FixupTarget rebaseTarget; + rebaseTarget.kind = FixupTarget::Kind::rebase; + rebaseTarget.foundInImage = *this; + rebaseTarget.offsetInImage = targetOffset & 0x00FFFFFFFFFFFFFFULL; + rebaseTarget.isLazyBindRebase = false; // FIXME + fixup(fixupOffset, pmd, rebaseTarget, fixupsStop); + } + }); + }); + } + else if ( ma->hasFirmwareChainStarts(&fmPointerFormat, &fmStartsCount, &fmStarts) ) { + // This is firmware which only has rebases, the chain starts info is in a section (not LINKEDIT) + ma->forEachFixupInAllChains(diag, fmPointerFormat, fmStartsCount, fmStarts, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, bool& stop) { + uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + PointerMetaData pmd(fixupLoc, fmPointerFormat); + uint64_t targetOffset; + fixupLoc->isRebase(fmPointerFormat, prefLoadAddr, targetOffset); + FixupTarget rebaseTarget; + rebaseTarget.kind = FixupTarget::Kind::rebase; + rebaseTarget.foundInImage = *this; + rebaseTarget.offsetInImage = targetOffset & 0x00FFFFFFFFFFFFFFULL; + rebaseTarget.isLazyBindRebase = false; + fixup(fixupOffset, pmd, rebaseTarget, stop); + }); + } + else { + // process all rebase opcodes + const bool is64 = ma->is64(); + ma->forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) { + uint64_t* loc = (uint64_t*)((uint8_t*)ma + runtimeOffset); + uint64_t locValue = is64 ? *loc : *((uint32_t*)loc); + FixupTarget rebaseTarget; + PointerMetaData pmd; + if ( is64 ) + pmd.high8 = (locValue >> 56); + rebaseTarget.kind = FixupTarget::Kind::rebase; + rebaseTarget.foundInImage = *this; + rebaseTarget.offsetInImage = (locValue & 0x00FFFFFFFFFFFFFFULL) - prefLoadAddr; + rebaseTarget.isLazyBindRebase = isLazyPointerRebase; + fixup(runtimeOffset, pmd, rebaseTarget, stop); + }); + if ( diag.hasError() ) + return; + + // process all bind opcodes + this->forEachBind(diag, fixup, patcher); + } + if ( diag.hasError() ) + return; + + // main executable may define operator new/delete symbols that overrides weak-defs but have no fixups + if ( ma->isMainExecutable() && ma->hasWeakDefs() ) { + _set->wmo_findExtraSymbolFrom(this, patcher); + } +} + + +bool MachOAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, + bool lazyBind, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const +{ + target.libOrdinal = libOrdinal; + if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) { + __block bool found = false; + this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) { + // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself + if ( hidden && (fromWmo->_mh != anImage._mh) ) + return; + if ( anImage.findSymbolIn(diag, symbolName, addend, target) ) { + stop = true; + found = true; + } + }); + if ( found ) + return true; + // see if missing symbol resolver can find something + if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) ) + return true; + // fill out target info about missing symbol + target.kind = FixupTarget::Kind::bindMissingSymbol; + target.requestedSymbolName = symbolName; + target.foundSymbolName = nullptr; + target.foundInImage = WrappedMachO(); // no image it should be in + + diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromWmo->path()); + return false; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { + if ( this->mas_fromImageWeakDefLookup(*fromWmo, symbolName, addend, patcher, target) ) { + target.weakCoalesced = true; + return true; + } + + if ( !fromWmo->_mh->hasChainedFixups() ) { + // support old binaries where symbols have been stripped and have weak_bind to itself + target.skippableWeakDef = true; + return true; + } + // see if missing symbol resolver can find something + if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) ) + return true; + // fill out target info about missing symbol + target.kind = FixupTarget::Kind::bindMissingSymbol; + target.requestedSymbolName = symbolName; + target.foundSymbolName = nullptr; + target.foundInImage = WrappedMachO(); // no image it should be in + + diag.error("symbol '%s' not found, expected to be weak-def coalesced in '%s'", symbolName, fromWmo->path()); + return false; + } + else { + int depIndex = libOrdinal - 1; + bool missingWeakDylib = false; + WrappedMachO depHelper; + const WrappedMachO* targetImage = nullptr; + if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + targetImage = fromWmo; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + this->mas_mainExecutable(depHelper); + targetImage = &depHelper; + } + else if ( fromWmo->dependent(depIndex, depHelper, missingWeakDylib) ) { + targetImage = &depHelper; + } + else { + diag.error("unknown library ordinal %d in %s", libOrdinal, fromWmo->path()); + return false; + } + // use two-level namespace target image + if ( !missingWeakDylib && targetImage->findSymbolIn(diag, symbolName, addend, target) ) + return true; + + // see if missing symbol resolver can find something + const char* expectedInPath = missingWeakDylib ? "missing dylib" : targetImage->path(); + if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, expectedInPath, fromWmo->path(), target) ) + return true; + + // fill out target info about missing symbol + target.kind = FixupTarget::Kind::bindMissingSymbol; + target.requestedSymbolName = symbolName; + target.foundSymbolName = nullptr; + target.foundInImage = *targetImage; // no image it is expected to be in + + // symbol not found and not weak or lazy so error out + diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromWmo->path()); + return false; + } + return false; +} + +// 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* const 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 MachOAnalyzerSet::wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler patcher) const +{ + for (const char* weakSymbolName : sTreatAsWeak) { + Diagnostics exportDiag; + FixupTarget dummyTarget; + // pretend main executable does have a use of this operator new/delete and look up the impl + // this has the side effect of adding a cache patch if there is an impl outside the cache + wmo_findSymbolFrom(fromWmo, exportDiag, -3, weakSymbolName, true, false, 0, patcher, dummyTarget); + } + +} + +bool MachOAnalyzerSet::WrappedMachO::findSymbolIn(Diagnostics& diag, const char* symbolName, uint64_t addend, FixupTarget& target) const +{ + const MachOAnalyzer* ma = _mh; + + // if exports trie location not computed yet, do it now + ExportsTrie exportsTrie = this->getExportsTrie(); + + target.foundSymbolName = nullptr; + if ( exportsTrie.start ) { + if ( const uint8_t* node = this->_mh->trieWalk(diag, exportsTrie.start, exportsTrie.end, symbolName)) { + const uint8_t* p = node; + const uint64_t flags = this->_mh->read_uleb128(diag, p, exportsTrie.end); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uint64_t libOrdinal = ma->read_uleb128(diag, p, exportsTrie.end); + const char* importedName = (char*)p; + if ( importedName[0] == '\0' ) + importedName = symbolName; + const int depIndex = (int)(libOrdinal - 1); + bool missingWeakDylib; + WrappedMachO depHelper; + if ( this->dependent(depIndex, depHelper, missingWeakDylib) && !missingWeakDylib ) { + if ( depHelper.findSymbolIn(diag, importedName, addend, target) ) { + target.requestedSymbolName = symbolName; + return true; + } + } + if ( !missingWeakDylib ) + diag.error("re-export ordinal %lld out of range for %s", libOrdinal, symbolName); + return false; + } + target.kind = FixupTarget::Kind::bindToImage; + target.requestedSymbolName = symbolName; + target.foundSymbolName = symbolName; + target.foundInImage = *this; + target.isWeakDef = false; + target.addend = addend; + uint64_t trieValue = ma->read_uleb128(diag, p, exportsTrie.end); + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + target.offsetInImage = trieValue + addend; + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // for now, just return address of resolver helper stub + // FIXME handle running resolver + (void)this->_mh->read_uleb128(diag, p, exportsTrie.end); + } + if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ) + target.isWeakDef = true; + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + // no type checking that client expected TLV yet + target.offsetInImage = trieValue; + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + target.kind = FixupTarget::Kind::bindAbsolute; + target.offsetInImage = trieValue + addend; + break; + default: + diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-exportsTrie.start)); + return false; + } + return true; + } + } + else { + ma->forEachGlobalSymbol(diag, ^(const char* n_name, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( strcmp(n_name, symbolName) == 0 ) { + target.kind = FixupTarget::Kind::bindToImage; + target.foundSymbolName = symbolName; + target.requestedSymbolName = symbolName; + target.foundInImage = *this; + target.offsetInImage = n_value - ma->preferredLoadAddress() + addend; + target.addend = addend; + stop = true; + } + }); + if ( target.foundSymbolName ) + return true; + } + + // symbol not exported from this image + // if this is a dylib and has re-exported dylibs, search those too + if ( (ma->filetype == MH_DYLIB) && ((ma->flags & MH_NO_REEXPORTED_DYLIBS) == 0) ) { + __block unsigned depIndex = 0; + ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( isReExport ) { + bool missingWeakDylib; + WrappedMachO child; + if ( this->dependent(depIndex, child, missingWeakDylib) && !missingWeakDylib ) { + if ( child.findSymbolIn(diag, symbolName, addend, target) ) + stop = true; + } + } + ++depIndex; + }); + } + + return (target.foundSymbolName != nullptr); +} + + +MachOAnalyzerSet::ExportsTrie MachOAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* wmo) const +{ + const uint8_t* start = nullptr; + const uint8_t* end = nullptr; + uint32_t runtimeOffset; + uint32_t size; + if ( wmo->_mh->hasExportTrie(runtimeOffset, size) ) { + start = (uint8_t*)wmo->_mh + runtimeOffset; + end = start + size; + } + return { start, end }; +} + + +// scan all weak-def images in load order +// return first non-weak defintion found +// otherwise first weak definition found +bool MachOAnalyzerSet::mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const +{ + // walk all images in load order, looking only at ones with weak-defs + const DyldSharedCache* dyldCache = (DyldSharedCache*)mas_dyldCache(); + __block bool foundImpl = false; + this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) { + if ( !anImage._mh->hasWeakDefs() ) + return; + // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself + if ( hidden && (fromWmo._mh != anImage._mh) ) + return; + FixupTarget tempTarget; + Diagnostics diag; + if ( anImage.findSymbolIn(diag, symbolName, addend, tempTarget) ) { + // ignore symbol re-exports, we will find the real definition later in forEachImage() + if ( anImage._mh != tempTarget.foundInImage._mh ) + return; + if ( foundImpl && anImage._mh->inDyldCache() && (anImage._mh != target.foundInImage._mh) ) { + // we have already found the target, but now we see something in the dyld cache + // that also implements this symbol, so we need to change all caches uses of that + // to use the found one instead + uint32_t cachedDylibIndex = 0; + if ( dyldCache->findMachHeaderImageIndex(anImage._mh, cachedDylibIndex) ) { + uintptr_t exportCacheOffset = (uint8_t*)tempTarget.foundInImage._mh + tempTarget.offsetInImage - (uint8_t*)dyldCache; + patcher(cachedDylibIndex, (uint32_t)exportCacheOffset, target); + } + } + if ( !foundImpl ) { + // this is first found, so copy this to result + target = tempTarget; + foundImpl = true; + } + else if ( target.isWeakDef && !tempTarget.isWeakDef ) { + // we found a non-weak impl later on, switch to it + target = tempTarget; + } + } + }); + return foundImpl; +} + + +} // dyld3 + + diff --git a/dyld3/MachOAnalyzerSet.h b/dyld3/MachOAnalyzerSet.h new file mode 100644 index 0000000..27f4c3d --- /dev/null +++ b/dyld3/MachOAnalyzerSet.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef MachOAnalyzerSet_h +#define MachOAnalyzerSet_h + + +#include "MachOAnalyzer.h" +#include "Array.h" + + +namespace dyld3 { + +// +// MachOAnalyzerSet is an abstraction to deal with sets of mach-o files. For instance, +// if a mach-o file A binds to a symbol in mach-o file B, the MachOAnalyzerSet lets you +// evaulate the bind such that you know where in B, the bind pointer in A needs to point. +// +// The goal of MachOAnalyzerSet is to be the one place for code that handles mach-o +// file interactions, such as two-level namespace binding, weak-def coalescing, and +// dyld cache patching. +// +// Conceptually, MachOAnalyzerSet is an ordered list of mach-o files. Each file is modeled +// as an WrappedMachO object. This is a lightweight POD struct of three pointers that +// can be copied around. A WrappedMachO consists of the MachOAnalyzer* that is represents, +// a pointer to the MachOAnalyzerSet it is in, and an abstract "other" pointer that the +// concrete implementation of MachOAnalyzerSet defines. All uses of mach-o files in +// the MachOAnalyzerSet method uses WrappedMachO types. +// +// // This is the key method on WrappedMachO. It is called during closure building to +// // compile down the fixups, as well as at runtime in dyld3 minimal closure mode +// // to parse LINKEDIT and rebase/bind pointers. +// void forEachFixup(Diagnostics& diag, FixUpHandler, CachePatchHandler) const; +// +// +// It would have been nice to have virtual methods on WrappedMachO, but C++ won't allow +// objects with vtables to be copied. So instead the methods on WrappedMachO simply +// forward to virtual methods in the owning MachOAnalyzerSet object. Therefore, there +// are two kinds of methods on MachOAnalyzerSet: methods for a WrappedMachO start with wmo_, +// whereas methods for the whole set start with mas_. +// +// // Walk all images in the set in order. "hidden" means the image was loaded with RTLD_LOCAL +// void mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const = 0; +// +// // fills in mainWmo with the WrappedMachO for the main executable in the set +// void mas_mainExecutable(WrappedMachO& mainWmo) const = 0; +// +// // returns a pointer to the start of the dyld cache used the mach-o files in the set +// void* mas_dyldCache() const = 0; +// +// // For weak-def coalescing. The file fromWmo needs to bind to symbolName. All files with weak-defs should be searched. +// // As a side effect of doing this binding, it may find that the symbol is bound overrides something in the dyld cache. +// // In that case, the CachePatchHandler function is called with info about how to patch the dyld cache. +// // This function has a default implementation. Only the dyld cache builder overrides this, because the set is all the +// // dylibs in the dyld cache, and coalescing should only look at files actually linked. +// bool mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, +// CachePatchHandler patcher, FixupTarget& target) const; +// +// +// // For a given WrappedMachO (fromWmo), find the nth dependent dylib. If depIndex is out of range, return false. +// // If child is weak-linked dylib that could not be loaded, set missingWeakDylib to true and return true. +// // Otherwise fill in childWmo and return true +// bool wmo_dependent(const WrappedMachO* fromWmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const = 0; +// +// // Returns the path to the specified WrappedMachO +// const char* wmo_path(const WrappedMachO* wmo) const = 0; +// +// // Called if a symbol cannot be found. If false is returned, then the symbol binding code will return an error. +// // If true is returned, then "target" must be set. It may be set to "NULL" as absolute/0. +// bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, +// const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const = 0; +// +// // Returns the exports trie for the given binary. There is a default implementation which walks the load commands. +// // This should only be overriden if the MachOAnalyzerSet caches the export trie location. +// ExportsTrie wmo_getExportsTrie(const WrappedMachO* wmo) const; +// +// // This handles special symbols like C++ operator new which can exist in the main executable as non-weak but +// // coalesce with other weak implementations. It does not need to be overridden. +// void wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler ph) const; +// +// // This is core logic for two-level namespace symbol look ups. It does not need to be overridden. +// bool wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, +// bool weakImport, bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const; +// +// + +struct VIS_HIDDEN MachOAnalyzerSet +{ +public: + struct FixupTarget; + + struct ExportsTrie { const uint8_t* start; const uint8_t* end; } ; + + // Extra info needed when setting an actual pointer value at runtime + struct PointerMetaData + { + PointerMetaData(); + PointerMetaData(const MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format); + + uint32_t diversity : 16, + high8 : 8, + authenticated : 1, + key : 2, + usesAddrDiversity : 1; + }; + + typedef void (^FixUpHandler)(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const FixupTarget& target, bool& stop); + typedef void (^CachePatchHandler)(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target); + + struct WrappedMachO + { + const MachOAnalyzer* _mh; + const MachOAnalyzerSet* _set; + void* _other; + + WrappedMachO() : _mh(nullptr), _set((nullptr)), _other((nullptr)) { } + WrappedMachO(const MachOAnalyzer* ma, const MachOAnalyzerSet* mas, void* o) : _mh(ma), _set(mas), _other(o) { } + ~WrappedMachO() {} + + // Used by: dyld cache building, dyld3s fixup applying, app closure building traditional format, dyldinfo tool + void forEachFixup(Diagnostics& diag, FixUpHandler, CachePatchHandler) const; + + // convenience functions + bool dependent(uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const { return _set->wmo_dependent(this, depIndex, childObj, missingWeakDylib); } + const char* path() const { return (_set ? _set->wmo_path(this) : nullptr); } + ExportsTrie getExportsTrie() const { return _set->wmo_getExportsTrie(this); } + bool findSymbolFrom(Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const + { return _set->wmo_findSymbolFrom(this, diag, libOrdinal, symbolName, weakImport, lazyBind, addend, ph, target); } + bool missingSymbolResolver(bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const + { return _set->wmo_missingSymbolResolver(this, weakImport, lazyBind, symbolName, expectedInDylibPath, clientPath, target); } + + bool findSymbolIn(Diagnostics& diag, const char* symbolName, uint64_t addend, FixupTarget& target) const; + + private: + void forEachBind(Diagnostics& diag, FixUpHandler, CachePatchHandler) const; + }; + + + struct FixupTarget + { + enum class Kind { rebase, bindToImage, bindAbsolute, bindMissingSymbol }; + WrappedMachO foundInImage; + uint64_t offsetInImage = 0; // includes addend + const char* requestedSymbolName = nullptr; + const char* foundSymbolName = nullptr; + uint64_t addend = 0; // already added into offsetInImage + int libOrdinal = 0; + Kind kind = Kind::rebase; + bool isLazyBindRebase = false; // target is stub helper in same image + bool isWeakDef = false; // target symbol is a weak-def + bool weakCoalesced = false; // target found searching all images + bool weakBoundSameImage = false; // first weak-def was in same image as use + bool skippableWeakDef = false; // old binary that stripped symbol, so def will never be found + }; + + virtual void mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const = 0; + virtual bool mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const; + virtual void mas_mainExecutable(WrappedMachO& mainWmo) const = 0; + virtual void* mas_dyldCache() const = 0; + + +protected: + friend WrappedMachO; + + virtual bool wmo_dependent(const WrappedMachO* fromWmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const = 0; + virtual const char* wmo_path(const WrappedMachO* wmo) const = 0; + virtual ExportsTrie wmo_getExportsTrie(const WrappedMachO* wmo) const; + virtual bool wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, + bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const; + virtual bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, + const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const = 0; + virtual void wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler ph) const; +}; + + + +} // namespace dyld3 + +#endif /* MachOAnalyzerSet_h */ diff --git a/dyld3/MachOAppCache.cpp b/dyld3/MachOAppCache.cpp new file mode 100644 index 0000000..b8130ad --- /dev/null +++ b/dyld3/MachOAppCache.cpp @@ -0,0 +1,163 @@ +/* +* 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 "MachOAppCache.h" + +#include + +#include +#include +#include + +#ifndef LC_FILESET_ENTRY +#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */ +struct fileset_entry_command { + uint32_t cmd; /* LC_FILESET_ENTRY */ + uint32_t cmdsize; /* includes id string */ + uint64_t vmaddr; /* memory address of the dylib */ + uint64_t fileoff; /* file offset of the dylib */ + union lc_str entry_id; /* contained entry id */ + uint32_t reserved; /* entry_id is 32-bits long, so this is the reserved padding */ +}; +#endif + +namespace dyld3 { + +void MachOAppCache::forEachDylib(Diagnostics& diag, void (^callback)(const MachOAnalyzer* ma, const char* name, bool& stop)) const { + const intptr_t slide = getSlide(); + forEachLoadCommand(diag, ^(const load_command *cmd, bool &stop) { + if (cmd->cmd == LC_FILESET_ENTRY) { + const fileset_entry_command* app_cache_cmd = (const fileset_entry_command*)cmd; + const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset; + callback((const MachOAnalyzer*)(app_cache_cmd->vmaddr + slide), name, stop); + return; + } + }); +} + +void MachOAppCache::forEachPrelinkInfoLibrary(Diagnostics& diags, + void (^callback)(const char* bundleName, const char* relativePath, + const std::vector& deps)) const { + + __block std::list nonASCIIStrings; + auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) { + const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8); + if ( symbolName != nullptr ) + return symbolName; + + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); + char buffer[len + 1]; + if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { + diags.error("Could not convert string to ASCII"); + return (const char*)nullptr; + } + buffer[len] = '\0'; + nonASCIIStrings.push_back(buffer); + return nonASCIIStrings.back().c_str(); + }; + + const uint8_t* prelinkInfoBuffer = nullptr; + uint64_t prelinkInfoBufferSize = 0; + prelinkInfoBuffer = (const uint8_t*)findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize); + if ( prelinkInfoBuffer == nullptr ) + return; + + CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull); + if ( !CFReadStreamOpen(readStreamRef) ) { + fprintf(stderr, "Could not open plist stream\n"); + exit(1); + } + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef); + if ( errorRef != nullptr ) { + CFStringRef stringRef = CFErrorCopyFailureReason(errorRef); + fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII)); + CFRelease(stringRef); + exit(1); + } + assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID()); + + // Get the "_PrelinkInfoDictionary" array + CFArrayRef prelinkInfoDictionaryArrayRef = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, CFSTR("_PrelinkInfoDictionary")); + assert(CFGetTypeID(prelinkInfoDictionaryArrayRef) == CFArrayGetTypeID()); + + for (CFIndex i = 0; i != CFArrayGetCount(prelinkInfoDictionaryArrayRef); ++i) { + CFDictionaryRef kextInfoDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(prelinkInfoDictionaryArrayRef, i); + assert(CFGetTypeID(kextInfoDictionary) == CFDictionaryGetTypeID()); + + CFStringRef bundleIdentifierStringRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("CFBundleIdentifier")); + assert(CFGetTypeID(bundleIdentifierStringRef) == CFStringGetTypeID()); + + const char* bundleID = getString(diags, bundleIdentifierStringRef); + if ( bundleID == nullptr ) + return; + + const char* relativePath = nullptr; + CFStringRef relativePathStringRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("_PrelinkExecutableRelativePath")); + if ( relativePathStringRef != nullptr ) { + assert(CFGetTypeID(relativePathStringRef) == CFStringGetTypeID()); + relativePath = getString(diags, relativePathStringRef); + if ( relativePath == nullptr ) + return; + } + + std::vector dependencies; + + CFDictionaryRef bundleLibrariesDictionaryRef = (CFDictionaryRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("OSBundleLibraries")); + if (bundleLibrariesDictionaryRef != nullptr) { + // Add the libraries to the dependencies + // If we didn't have bundle libraries then a placeholder was added + assert(CFGetTypeID(bundleLibrariesDictionaryRef) == CFDictionaryGetTypeID()); + + struct ApplyContext { + Diagnostics* diagnostics; + std::vector* dependencies = nullptr; + const char* (^getString)(Diagnostics& diags, CFStringRef symbolNameRef) = nullptr; + }; + + CFDictionaryApplierFunction callback = [](const void *key, const void *value, void *context) { + CFStringRef keyStringRef = (CFStringRef)key; + assert(CFGetTypeID(keyStringRef) == CFStringGetTypeID()); + + ApplyContext* applyContext = (ApplyContext*)context; + const char* depString = applyContext->getString(*applyContext->diagnostics, keyStringRef); + if ( !depString ) + return; + + applyContext->dependencies->push_back(depString); + }; + + ApplyContext applyContext = { &diags, &dependencies, getString }; + CFDictionaryApplyFunction(bundleLibrariesDictionaryRef, callback, &applyContext); + + if ( diags.hasError() ) + return; + } + callback(bundleID, relativePath, dependencies); + } + + CFRelease(plistRef); + CFRelease(readStreamRef); +} + +} // namespace dyld3 diff --git a/dyld3/MachOAppCache.h b/dyld3/MachOAppCache.h new file mode 100644 index 0000000..9d0f2ce --- /dev/null +++ b/dyld3/MachOAppCache.h @@ -0,0 +1,63 @@ +/* + * 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 MachOAppCache_h +#define MachOAppCache_h + +#include "MachOAnalyzer.h" + +namespace dyld3 { + +struct MachOAppCache : public MachOAnalyzer { + // Taken from kmod.h + enum { + kmodMaxName = 64 + }; + #pragma pack(push, 4) + struct KModInfo64_v1 { + uint64_t next_addr; + int32_t info_version; + uint32_t id; + uint8_t name[kmodMaxName]; + uint8_t version[kmodMaxName]; + int32_t reference_count; + uint64_t reference_list_addr; + uint64_t address; + uint64_t size; + uint64_t hdr_size; + uint64_t start_addr; + uint64_t stop_addr; + }; + #pragma pack(pop) + + void forEachDylib(Diagnostics& diag, void (^callback)(const MachOAnalyzer* ma, const char* name, bool& stop)) const; + + // Walk the __PRELINK_INFO dictionary and return each bundle and its libraries + void forEachPrelinkInfoLibrary(Diagnostics& diags, + void (^callback)(const char* bundleName, const char* relativePath, + const std::vector& deps)) const; +}; + +} // namespace dyld3 + +#endif /* MachOAppCache_h */ diff --git a/dyld3/MachOFile.cpp b/dyld3/MachOFile.cpp index ce8b8f6..1b8006c 100644 --- a/dyld3/MachOFile.cpp +++ b/dyld3/MachOFile.cpp @@ -25,6 +25,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #include @@ -34,9 +39,50 @@ #include "MachOFile.h" #include "SupportedArchs.h" +#if BUILDING_DYLD || BUILDING_LIBDYLD + // define away restrict until rdar://60166935 is fixed + #define restrict + #include +#endif namespace dyld3 { +//////////////////////////// posix wrappers //////////////////////////////////////// + +// wrap calls to stat() with check for EAGAIN +int stat(const char* path, struct stat* buf) +{ + int result; + do { +#if BUILDING_DYLD || BUILDING_LIBDYLD + result = ::stat_with_subsystem(path, buf); +#else + result = ::stat(path, buf); +#endif + } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR))); + + return result; +} + +// dyld should retry open() if it gets an EGAIN +int open(const char* path, int flag, int other) +{ + int result; + do { +#if BUILDING_DYLD || BUILDING_LIBDYLD + if (flag & O_CREAT) + result = ::open(path, flag, other); + else + result = ::open_with_subsystem(path, flag); +#else + result = ::open(path, flag, other); +#endif + } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR))); + + return result; +} + + //////////////////////////// FatFile //////////////////////////////////////// const FatFile* FatFile::isFatFile(const void* fileStart) @@ -130,7 +176,8 @@ void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback) } } -bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const +bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool isOSBinary, + uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const { missingSlice = false; if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) ) @@ -138,7 +185,7 @@ bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const Grad __block int bestGrade = 0; forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) { - if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType)) { + if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) { if ( sliceGrade > bestGrade ) { sliceOffset = (char*)sliceStart - (char*)this; sliceLen = sliceSize; @@ -158,26 +205,47 @@ bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const Grad //////////////////////////// GradedArchs //////////////////////////////////////// -const GradedArchs GradedArchs::i386 = { {{CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, 1}} }; -const GradedArchs GradedArchs::x86_64 = { {{CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, 1}} }; -const GradedArchs GradedArchs::x86_64h = { {{CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, 2}, {CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, 1}}, }; -const GradedArchs GradedArchs::arm64 = { {{CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, 1}} }; + +#define GRADE_i386 CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL, false +#define GRADE_x86_64 CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, false +#define GRADE_x86_64h CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H, false +#define GRADE_armv7 CPU_TYPE_ARM, CPU_SUBTYPE_ARM64_ALL, false +#define GRADE_armv7s CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, false +#define GRADE_armv7k CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K, false +#define GRADE_arm64 CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, false +#define GRADE_arm64e CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, false +#define GRADE_arm64e_pb CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, true +#define GRADE_arm64_32 CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, false + +const GradedArchs GradedArchs::i386 = { {{GRADE_i386, 1}} }; +const GradedArchs GradedArchs::x86_64 = { {{GRADE_x86_64, 1}} }; +const GradedArchs GradedArchs::x86_64h = { {{GRADE_x86_64h, 2}, {GRADE_x86_64, 1}} }; +const GradedArchs GradedArchs::arm64 = { {{GRADE_arm64, 1}} }; #if SUPPORT_ARCH_arm64e -const GradedArchs GradedArchs::arm64e_compat = { {{CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, 2}, {CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL, 1}} }; -const GradedArchs GradedArchs::arm64e = { {{CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E, 1}} }; +const GradedArchs GradedArchs::arm64e_keysoff = { {{GRADE_arm64e, 2}, {GRADE_arm64, 1}} }; +const GradedArchs GradedArchs::arm64e_keysoff_pb = { {{GRADE_arm64e_pb, 2}, {GRADE_arm64, 1}} }; +const GradedArchs GradedArchs::arm64e = { {{GRADE_arm64e, 1}} }; +const GradedArchs GradedArchs::arm64e_pb = { {{GRADE_arm64e_pb, 1}} }; #endif -const GradedArchs GradedArchs::armv7k = { {{CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K, 1}} }; -const GradedArchs GradedArchs::armv7 = { {{CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, 1}} }; -const GradedArchs GradedArchs::armv7s = { {{CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S, 2}, {CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7, 1}} }; +const GradedArchs GradedArchs::armv7 = { {{GRADE_armv7, 1}} }; +const GradedArchs GradedArchs::armv7s = { {{GRADE_armv7s, 2}, {GRADE_armv7, 1}} }; +const GradedArchs GradedArchs::armv7k = { {{GRADE_armv7k, 1}} }; #if SUPPORT_ARCH_arm64_32 -const GradedArchs GradedArchs::arm64_32 = { {{CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, 1}} }; +const GradedArchs GradedArchs::arm64_32 = { {{GRADE_arm64_32, 1}} }; #endif -int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype) const +int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const { for (const CpuGrade* p = _orderedCpuTypes; p->type != 0; ++p) { - if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) - return p->grade; + if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) { + if ( p->osBinary ) { + if ( isOSBinary ) + return p->grade; + } + else { + return p->grade; + } + } } return 0; } @@ -206,13 +274,13 @@ static bool isHaswell() } #endif -const GradedArchs& GradedArchs::forCurrentOS(const MachOFile* mainExecutable) +const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly) { #if __arm64e__ - if ( mainExecutable->cpusubtype < CPU_SUBTYPE_ARM64E ) - return arm64e_compat; + if ( osBinariesOnly ) + return (keysOff ? arm64e_keysoff_pb : arm64e_pb); else - return arm64e; + return (keysOff ? arm64e_keysoff : arm64e); #elif __ARM64_ARCH_8_32__ return arm64_32; #elif __arm64__ @@ -232,7 +300,7 @@ const GradedArchs& GradedArchs::forCurrentOS(const MachOFile* mainExecutable) #endif } -const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecutable) +const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff) { if (strcmp(archName, "x86_64h") == 0 ) return x86_64h; @@ -240,7 +308,7 @@ const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecut return x86_64; #if SUPPORT_ARCH_arm64e else if (strcmp(archName, "arm64e") == 0 ) - return forMainExecutable ? arm64e_compat : arm64e; + return keysOff ? arm64e_keysoff : arm64e; #endif else if (strcmp(archName, "arm64") == 0 ) return arm64; @@ -260,6 +328,7 @@ const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecut } + //////////////////////////// MachOFile //////////////////////////////////////// @@ -285,7 +354,7 @@ const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = { { "tvOS", Platform::tvOS, LC_VERSION_MIN_TVOS }, { "watchOS", Platform::watchOS, LC_VERSION_MIN_WATCHOS }, { "bridgeOS", Platform::bridgeOS, LC_BUILD_VERSION }, - { "UIKitForMac", Platform::iOSMac, LC_BUILD_VERSION }, + { "MacCatalyst", 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 }, @@ -303,6 +372,10 @@ size_t MachOFile::machHeaderSize() const return is64() ? sizeof(mach_header_64) : sizeof(mach_header); } +uint32_t MachOFile::maskedCpuSubtype() const +{ + return (this->cpusubtype & ~CPU_SUBTYPE_MASK); +} uint32_t MachOFile::pointerSize() const { @@ -404,32 +477,83 @@ void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString *s++ = '\0'; } -bool MachOFile::supportsPlatform(Platform reqPlatform) const +bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const { __block bool foundRequestedPlatform = false; - __block bool foundOtherPlatform = false; + __block bool foundOtherPlatform = false; forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { if ( platform == reqPlatform ) foundRequestedPlatform = true; else foundOtherPlatform = true; }); + // if checking that this binary is built for exactly one platform, fail if more + if ( foundOtherPlatform && onlyOnePlatform ) + return false; 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 ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) { if ( this->cputype == CPU_TYPE_X86_64 ) return true; if ( this->cputype == CPU_TYPE_I386 ) return true; } +#if BUILDING_DYLDINFO + // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms + if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) ) + return true; +#endif + + return false; +} + +bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path) const +{ + if ( this->builtForPlatform(processPlatform) ) + return true; + + // Some host macOS dylibs can be loaded into simulator processes + if ( MachOFile::isSimulatorPlatform(processPlatform) && this->builtForPlatform(Platform::macOS)) { + static const char* macOSHost[] = { + "/usr/lib/system/libsystem_kernel.dylib", + "/usr/lib/system/libsystem_platform.dylib", + "/usr/lib/system/libsystem_pthread.dylib", + "/usr/lib/system/libsystem_platform_debug.dylib", + "/usr/lib/system/libsystem_pthread_debug.dylib", + "/usr/lib/system/host/liblaunch_sim.dylib", + }; + for (const char* libPath : macOSHost) { + if (strcmp(libPath, path) == 0) + return true; + } + } + + // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable + if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOSMac, true) ) + return true; +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) ) + return true; +#endif + + bool iOSonMac = (processPlatform == Platform::iOSMac); +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + // allow iOS binaries in iOSApp + if ( processPlatform == Platform::iOS ) { + // can load Catalyst binaries into iOS process + if ( this->builtForPlatform(Platform::iOSMac) ) + return true; + iOSonMac = true; + } +#endif + // macOS dylibs can be loaded into iOSMac processes + if ( (iOSonMac) && this->builtForPlatform(Platform::macOS, true) ) + return true; + return false; } @@ -469,8 +593,10 @@ Platform MachOFile::currentPlatform() return Platform::tvOS; #elif TARGET_OS_IOS return Platform::iOS; -#elif TARGET_OS_MAC +#elif TARGET_OS_OSX return Platform::macOS; +#elif TARGET_OS_DRIVERKIT + return Platform::driverKit; #else #error unknown platform #endif @@ -542,6 +668,16 @@ bool MachOFile::isStaticExecutable() const return !hasLoadCommand(LC_LOAD_DYLINKER); } +bool MachOFile::isKextBundle() const +{ + return (this->filetype == MH_KEXT_BUNDLE); +} + +bool MachOFile::isFileSet() const +{ + return (this->filetype == MH_FILESET); +} + bool MachOFile::isPIE() const { return (this->flags & MH_PIE); @@ -659,6 +795,52 @@ void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const loa } } +void MachOFile::removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop)) +{ + 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 if ( hasMachOBigEndianMagic() ) + return; // can't process big endian mach-o + else { + const uint32_t* h = (uint32_t*)this; + diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return; // not a mach-o file + } + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds); + auto cmd = (load_command*)startCmds; + const uint32_t origNcmds = this->ncmds; + unsigned bytesRemaining = this->sizeofcmds; + for (uint32_t i = 0; i < origNcmds; ++i) { + bool remove = false; + auto nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return; + } + callback(cmd, remove, stop); + if ( remove ) { + this->sizeofcmds -= cmd->cmdsize; + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + this->ncmds--; + } else { + bytesRemaining -= cmd->cmdsize; + cmd = nextCmd; + } + if ( stop ) + break; + } + if ( cmd ) + ::bzero(cmd, bytesRemaining); +} + const char* MachOFile::installName() const { const char* name; @@ -782,6 +964,48 @@ bool MachOFile::enforceCompatVersion() const return result; } +const thread_command* MachOFile::unixThreadLoadCommand() const { + Diagnostics diag; + __block const thread_command* command = nullptr; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_UNIXTHREAD ) { + command = (const thread_command*)cmd; + stop = true; + } + }); + return command; +} + + +uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const +{ + switch ( this->cputype ) { + case CPU_TYPE_I386: + return 10; // i386_thread_state_t.eip + case CPU_TYPE_X86_64: + return 16; // x86_thread_state64_t.rip + case CPU_TYPE_ARM: + return 15; // arm_thread_state_t.pc + case CPU_TYPE_ARM64: + return 32; // arm_thread_state64_t.__pc + } + return ~0U; +} + + +uint64_t MachOFile::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); + + uint32_t index = entryAddrRegisterIndexForThreadCmd(); + if (index == ~0U) + return 0; + + return is64() ? regs64[index] : regs32[index]; +} + void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const { @@ -981,8 +1205,94 @@ bool MachOFile::isSharedCacheEligiblePath(const char* dylibName) { || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0) ); } +static bool startsWith(const char* buffer, const char* valueToFind) { + return strncmp(buffer, valueToFind, strlen(valueToFind)) == 0; +} + +static bool platformExcludesSharedCache_macOS(const char* installName) { + // Note: This function basically matches dontCache() from update dyld shared cache + + if ( startsWith(installName, "/usr/lib/system/introspection/") ) + return true; + if ( startsWith(installName, "/System/Library/QuickTime/") ) + return true; + if ( startsWith(installName, "/System/Library/Tcl/") ) + return true; + if ( startsWith(installName, "/System/Library/Perl/") ) + return true; + if ( startsWith(installName, "/System/Library/MonitorPanels/") ) + return true; + if ( startsWith(installName, "/System/Library/Accessibility/") ) + return true; + if ( startsWith(installName, "/usr/local/") ) + return true; + if ( startsWith(installName, "/usr/lib/pam/") ) + return true; + // We no longer support ROSP, so skip all paths which start with the special prefix + if ( startsWith(installName, "/System/Library/Templates/Data/") ) + return true; + + // anything inside a .app bundle is specific to app, so should not be in shared cache + if ( strstr(installName, ".app/") != NULL ) + return true; + + return false; +} + +static bool platformExcludesSharedCache_iOS(const char* installName) { + if ( strcmp(installName, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 ) + return true; + if ( strcmp(installName, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 ) + return true; + return false; +} + +static bool platformExcludesSharedCache_tvOS(const char* installName) { + return platformExcludesSharedCache_iOS(installName); +} + +static bool platformExcludesSharedCache_watchOS(const char* installName) { + return platformExcludesSharedCache_iOS(installName); +} + +static bool platformExcludesSharedCache_bridgeOS(const char* installName) { + return platformExcludesSharedCache_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. +static bool platformExcludesSharedCache(Platform platform, const char* installName) { + switch (platform) { + case dyld3::Platform::unknown: + return false; + case dyld3::Platform::macOS: + return platformExcludesSharedCache_macOS(installName); + case dyld3::Platform::iOS: + return platformExcludesSharedCache_iOS(installName); + case dyld3::Platform::tvOS: + return platformExcludesSharedCache_tvOS(installName); + case dyld3::Platform::watchOS: + return platformExcludesSharedCache_watchOS(installName); + case dyld3::Platform::bridgeOS: + return platformExcludesSharedCache_bridgeOS(installName); + case dyld3::Platform::iOSMac: + return platformExcludesSharedCache_macOS(installName); + case dyld3::Platform::iOS_simulator: + return false; + case dyld3::Platform::tvOS_simulator: + return false; + case dyld3::Platform::watchOS_simulator: + return false; + case dyld3::Platform::driverKit: + return false; + } +} + + + bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const { + if ( !isSharedCacheEligiblePath(path) ) { // Dont spam the user with an error about paths when we know these are never eligible. return false; @@ -1006,6 +1316,28 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c failureReason("install path does not match install name"); return false; } + else if ( strstr(dylibName, "//") != 0 ) { + failureReason("install name should not include //"); + return false; + } + else if ( strstr(dylibName, "./") != 0 ) { + failureReason("install name should not include ./"); + return false; + } + + __block bool platformExcludedFile = false; + forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + if ( platformExcludedFile ) + return; + if ( platformExcludesSharedCache(platform, dylibName) ) { + platformExcludedFile = true; + return; + } + }); + if ( platformExcludedFile ) { + failureReason("install name is not shared cache eligible on platform"); + return false; + } bool retval = true; @@ -1025,6 +1357,7 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c __block bool hasExtraInfo = false; __block bool hasDyldInfo = false; __block bool hasExportTrie = false; + __block bool hasLazyLoad = false; Diagnostics diag; forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO ) @@ -1033,6 +1366,8 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c hasDyldInfo = true; if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE ) hasExportTrie = true; + if ( cmd->cmd == LC_LAZY_LOAD_DYLIB ) + hasLazyLoad = true; }); if ( !hasExtraInfo ) { retval = false; @@ -1042,6 +1377,10 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c retval = false; failureReason("Old binary, missing dyld info or export trie"); } + if ( hasLazyLoad ) { + retval = false; + failureReason("Has lazy load"); + } // dylib can only depend on other dylibs in the shared cache __block bool allDepPathsAreGood = true; @@ -1057,18 +1396,13 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c } // 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 ) { + if ( hasInterposingTuples() ) { retval = false; failureReason("Has interposing tuples"); } - // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg - if ( supportsPlatform(Platform::watchOS_simulator) && isArch("i386") ) { + // Temporarily kick out swift binaries out of dyld cache on watchOS simulators as they have missing split seg + if ( (this->cputype == CPU_TYPE_I386) && builtForPlatform(Platform::watchOS_simulator) ) { if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) { retval = false; failureReason("i386 swift binary"); @@ -1078,6 +1412,243 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c return retval; } +#if BUILDING_APP_CACHE_UTIL +bool MachOFile::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const +{ + // only dylibs and the kernel itself can go in cache + if ( this->filetype == MH_EXECUTE ) { + // xnu + } else if ( this->isKextBundle() ) { + // kext's + } else { + failureReason("Not MH_KEXT_BUNDLE"); + return false; + } + + if ( this->filetype == MH_EXECUTE ) { + // xnu + + // two-level namespace binaries cannot go in cache + if ( (this->flags & MH_TWOLEVEL) != 0 ) { + failureReason("Built with two level namespaces"); + return false; + } + + // xnu kernel cannot have a page zero + __block bool foundPageZero = false; + forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) { + if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) { + foundPageZero = true; + stop = true; + } + }); + if (foundPageZero) { + failureReason("Has __PAGEZERO"); + return false; + } + + // xnu must have an LC_UNIXTHREAD to point to the entry point + __block bool foundMainLC = false; + __block bool foundUnixThreadLC = false; + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_MAIN ) { + foundMainLC = true; + stop = true; + } + else if ( cmd->cmd == LC_UNIXTHREAD ) { + foundUnixThreadLC = true; + } + }); + if (foundMainLC) { + failureReason("Found LC_MAIN"); + return false; + } + if (!foundUnixThreadLC) { + failureReason("Expected LC_UNIXTHREAD"); + return false; + } + + if (diag.hasError()) { + failureReason("Error parsing load commands"); + return false; + } + + // The kernel should be a static executable, not a dynamic one + if ( !isStaticExecutable() ) { + failureReason("Expected static executable"); + return false; + } + + // The kernel must be built with -pie + if ( !isPIE() ) { + failureReason("Expected pie"); + return false; + } + } + + if ( isArch("arm64e") && isKextBundle() && !hasChainedFixups() ) { + failureReason("Missing fixup information"); + return false; + } + + // 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 ) { + failureReason("Has interposing tuples"); + return false; + } + + // Only x86_64 is allowed to have RWX segments + if ( !isArch("x86_64") && !isArch("x86_64h") ) { + __block bool foundBadSegment = false; + forEachSegment(^(const SegmentInfo &info, bool &stop) { + if ( (info.protections & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) ) { + failureReason("Segments are not allowed to be both writable and executable"); + foundBadSegment = true; + stop = true; + } + }); + if ( foundBadSegment ) + return false; + } + + return true; +} +#endif + +static bool platformExcludesPrebuiltClosure_macOS(const char* path) { + // We no longer support ROSP, so skip all paths which start with the special prefix + if ( startsWith(path, "/System/Library/Templates/Data/") ) + return true; + + // anything inside a .app bundle is specific to app, so should not get a prebuilt closure + if ( strstr(path, ".app/") != NULL ) + return true; + + return false; +} + +static bool platformExcludesPrebuiltClosure_iOS(const char* path) { + if ( strcmp(path, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 ) + return true; + if ( strcmp(path, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 ) + return true; + return false; +} + +static bool platformExcludesPrebuiltClosure_tvOS(const char* path) { + return platformExcludesPrebuiltClosure_iOS(path); +} + +static bool platformExcludesPrebuiltClosure_watchOS(const char* path) { + return platformExcludesPrebuiltClosure_iOS(path); +} + +static bool platformExcludesPrebuiltClosure_bridgeOS(const char* path) { + return platformExcludesPrebuiltClosure_iOS(path); +} + +// 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. +static bool platformExcludesPrebuiltClosure(Platform platform, const char* path) { + switch (platform) { + case dyld3::Platform::unknown: + return false; + case dyld3::Platform::macOS: + return platformExcludesPrebuiltClosure_macOS(path); + case dyld3::Platform::iOS: + return platformExcludesPrebuiltClosure_iOS(path); + case dyld3::Platform::tvOS: + return platformExcludesPrebuiltClosure_tvOS(path); + case dyld3::Platform::watchOS: + return platformExcludesPrebuiltClosure_watchOS(path); + case dyld3::Platform::bridgeOS: + return platformExcludesPrebuiltClosure_bridgeOS(path); + case dyld3::Platform::iOSMac: + return platformExcludesPrebuiltClosure_macOS(path); + case dyld3::Platform::iOS_simulator: + return false; + case dyld3::Platform::tvOS_simulator: + return false; + case dyld3::Platform::watchOS_simulator: + return false; + case dyld3::Platform::driverKit: + return false; + } +} + +bool MachOFile::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"); + } + + __block bool platformExcludedFile = false; + forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) { + if ( platformExcludedFile ) + return; + if ( platformExcludesPrebuiltClosure(platform, path) ) { + platformExcludedFile = true; + return; + } + }); + if ( platformExcludedFile ) { + failureReason("file cannot get a prebuilt closure on this platform"); + return false; + } + + // dylibs with interposing info cannot have dlopen closure pre-computed + if ( hasInterposingTuples() ) { + retval = false; + failureReason("has interposing tuples"); + } + + // 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 MachOFile::hasInterposingTuples() const +{ + __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; + }); + return hasInterposing; +} bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const { @@ -1145,12 +1716,19 @@ bool MachOFile::hasChainedFixups() const { #if SUPPORT_ARCH_arm64e // arm64e always uses chained fixups - if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) ) - return true; + if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) { + // Not all binaries have fixups at all so check for the load commands + return hasLoadCommand(LC_DYLD_INFO_ONLY) || hasLoadCommand(LC_DYLD_CHAINED_FIXUPS); + } #endif return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS); } +bool MachOFile::hasChainedFixupsLoadCommand() const +{ + return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS); +} + uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end) { uint64_t result = 0; @@ -1191,7 +1769,7 @@ int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint bit += 7; } while (byte & 0x80); // sign extend negative numbers - if ( (byte & 0x40) != 0 ) + if ( ((byte & 0x40) != 0) && (bit < 64) ) result |= (~0ULL) << bit; return result; } diff --git a/dyld3/MachOFile.h b/dyld3/MachOFile.h index 6a78714..b9099e6 100644 --- a/dyld3/MachOFile.h +++ b/dyld3/MachOFile.h @@ -74,8 +74,15 @@ #define S_INIT_FUNC_OFFSETS 0x16 #endif +#ifndef MH_FILESET + #define MH_FILESET 0xc /* set of mach-o's */ +#endif + namespace dyld3 { +// replacements for posix that handle EINTR +int stat(const char* path, struct stat* buf) VIS_HIDDEN; +int open(const char* path, int flag, int other) VIS_HIDDEN; /// Returns true if (addLHS + addRHS) > b, or if the add overflowed @@ -99,7 +106,7 @@ enum class Platform { tvOS = 3, // PLATFORM_TVOS watchOS = 4, // PLATFORM_WATCHOS bridgeOS = 5, // PLATFORM_BRIDGEOS - iOSMac = 6, // PLATFORM_IOSMAC + iOSMac = 6, // PLATFORM_MACCATALYST iOS_simulator = 7, // PLATFORM_IOSSIMULATOR tvOS_simulator = 8, // PLATFORM_TVOSSIMULATOR watchOS_simulator = 9, // PLATFORM_WATCHOSSIMULATOR @@ -115,10 +122,11 @@ public: GradedArchs() = delete; GradedArchs(const GradedArchs&) = delete; - static const GradedArchs& forCurrentOS(const MachOFile* mainExecutable); - static const GradedArchs& forName(const char* archName, bool forMainExecutable = false); + static const GradedArchs& forCurrentOS(bool keysOff, bool platformBinariesOnly); + static const GradedArchs& forName(const char* archName, bool keysOff = false); - int grade(uint32_t cputype, uint32_t cpusubtype) const; + + int grade(uint32_t cputype, uint32_t cpusubtype, bool platformBinariesOnly) const; const char* name() const; // pre-built lists for existing hardware @@ -127,8 +135,10 @@ public: static const GradedArchs x86_64h; // haswell Mac static const GradedArchs arm64; // A11 or earlier iPhone or iPad #if SUPPORT_ARCH_arm64e - static const GradedArchs arm64e; // A12 or later iPhone or iPad - static const GradedArchs arm64e_compat; // A12 running arm64 main executable + static const GradedArchs arm64e; // A12 or later iPhone or iPad + static const GradedArchs arm64e_keysoff; // A12 running with signing keys disabled + static const GradedArchs arm64e_pb; // macOS Apple Silicon running platform binary + static const GradedArchs arm64e_keysoff_pb; // macOS Apple Silicon running with signing keys disabled #endif static const GradedArchs armv7k; // watch thru series 3 static const GradedArchs armv7s; // deprecated @@ -139,7 +149,7 @@ public: // private: // should be private, but compiler won't statically initialize static members above - struct CpuGrade { uint32_t type; uint32_t subtype; uint32_t grade; }; + struct CpuGrade { uint32_t type; uint32_t subtype; bool osBinary; uint16_t grade; }; const CpuGrade _orderedCpuTypes[3]; // zero terminated }; @@ -149,7 +159,7 @@ 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 GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const; + bool isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool osBinary, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const; private: bool isValidSlice(Diagnostics& diag, uint64_t fileLen, uint32_t sliceIndex, @@ -180,18 +190,21 @@ struct VIS_HIDDEN MachOFile : mach_header bool isMainExecutable() const; bool isDynamicExecutable() const; bool isStaticExecutable() const; + bool isKextBundle() const; + bool isFileSet() const; bool isPreload() const; bool isPIE() const; bool isArch(const char* archName) const; const char* archName() const; bool is64() const; + uint32_t maskedCpuSubtype() const; size_t machHeaderSize() const; uint32_t pointerSize() const; bool uses16KPages() const; - bool supportsPlatform(Platform) const; + bool builtForPlatform(Platform, bool onlyOnePlatform=false) const; + bool loadableIntoProcess(Platform processPlatform, const char* path) const; bool isZippered() const; bool inDyldCache() const; - bool isSimulatorBinary() const; bool getUuid(uuid_t uuid) const; bool hasWeakDefs() const; bool hasThreadLocalVariables() const; @@ -200,12 +213,22 @@ struct VIS_HIDDEN MachOFile : mach_header 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; +#if BUILDING_APP_CACHE_UTIL + bool canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const; +#endif + bool canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const; bool canBeFairPlayEncrypted() const; bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const; bool allowsAlternatePlatform() const; bool hasChainedFixups() const; + bool hasChainedFixupsLoadCommand() const; void forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const; bool enforceCompatVersion() const; + bool hasInterposingTuples() const; + + const thread_command* unixThreadLoadCommand() const; + uint32_t entryAddrRegisterIndexForThreadCmd() const; + uint64_t entryAddrFromThreadCmd(const thread_command* cmd) const; struct SegmentInfo { @@ -246,6 +269,7 @@ struct VIS_HIDDEN MachOFile : mach_header protected: bool hasMachOBigEndianMagic() const; void forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const; + void removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop)); bool hasLoadCommand(uint32_t) const; const encryption_info_command* findFairPlayEncryptionLoadCommand() const; diff --git a/dyld3/MachOLoaded.cpp b/dyld3/MachOLoaded.cpp index 9ce21a6..13bfca7 100644 --- a/dyld3/MachOLoaded.cpp +++ b/dyld3/MachOLoaded.cpp @@ -211,6 +211,7 @@ void MachOLoaded::getLayoutInfo(LayoutInfo& result) const result.linkeditFileSize = (uint32_t)info.fileSize; result.linkeditSegIndex = info.segIndex; } + result.lastSegIndex = info.segIndex; }); } @@ -583,6 +584,15 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u return false; uint64_t targetUnslidAddress = address - leInfo.layout.slide; + // find section index the address is in to validate n_sect + __block uint32_t sectionIndexForTargetAddress = 0; + forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + ++sectionIndexForTargetAddress; + if ( (sectInfo.sectAddr <= targetUnslidAddress) && (targetUnslidAddress < sectInfo.sectAddr+sectInfo.sectSize) ) { + stop = true; + } + }); + 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)); @@ -595,10 +605,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u 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 ) + if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) ) bestSymbol = s; } - else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) { + else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) { bestSymbol = s; } } @@ -609,10 +619,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u 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 ) + if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) ) bestSymbol = s; } - else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) { + else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) { bestSymbol = s; } } @@ -632,10 +642,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u 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 ) + if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) ) bestSymbol = s; } - else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) { + else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) { bestSymbol = s; } } @@ -646,10 +656,10 @@ bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, u 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 ) + if ( (s->n_value <= targetUnslidAddress) && (s->n_sect == sectionIndexForTargetAddress) ) bestSymbol = s; } - else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) { + else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) && (s->n_sect == sectionIndexForTargetAddress) ) { bestSymbol = s; } } @@ -698,9 +708,8 @@ bool MachOLoaded::intersectsRange(uintptr_t start, uintptr_t length) const 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; + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, visitedNodeOffsets, 128); + visitedNodeOffsets.push_back(0); const uint8_t* p = start; while ( p < end ) { uint64_t terminalSize = *p++; @@ -769,17 +778,14 @@ const uint8_t* MachOLoaded::trieWalk(Diagnostics& diag, const uint8_t* start, co 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 ) { + // check for cycles + for (uint32_t aVisitedNodeOffset : visitedNodeOffsets) { + if ( aVisitedNodeOffset == 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; - } + visitedNodeOffsets.push_back((uint32_t)nodeOffset); p = &start[nodeOffset]; } else @@ -791,7 +797,6 @@ const uint8_t* MachOLoaded::trieWalk(Diagnostics& diag, const uint8_t* start, co void MachOLoaded::forEachCDHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, void (^callback)(const uint8_t cdHash[20])) const { -#ifndef DARLING forEachCodeDirectoryBlob(codeSigStart, codeSignLen, ^(const void *cdBuffer) { const CS_CodeDirectory* cd = (const CS_CodeDirectory*)cdBuffer; uint32_t cdLength = htonl(cd->length); @@ -834,7 +839,6 @@ void MachOLoaded::forEachCDHashOfCodeSignature(const void* codeSigStart, size_t return; } }); -#endif } @@ -907,7 +911,7 @@ void MachOLoaded::forEachCodeDirectoryBlob(const void* codeSigStart, size_t code // Note: The kernel sometimes chooses sha1 on watchOS, and sometimes sha256. // Embed all of them so that we just need to match any of them - const bool isWatchOS = this->supportsPlatform(Platform::watchOS); + const bool isWatchOS = this->builtForPlatform(Platform::watchOS); const bool isMainExecutable = this->isMainExecutable(); auto hashRankFn = isWatchOS ? &hash_rank_watchOS_dylibs : &hash_rank; @@ -960,43 +964,58 @@ uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signExtendedAddend() co { assert(this->authBind.bind == 1); assert(this->authBind.auth == 0); - uint64_t addend19 = this->bind.addend; + uint64_t addend19 = this->bind.addend; if ( addend19 & 0x40000 ) return addend19 | 0xFFFFFFFFFFFC0000ULL; else return addend19; } -const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName() const +const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName(uint8_t keyBits) { static const char* names[] = { "IA", "IB", "DA", "DB" }; - assert(this->authBind.auth == 1); - uint8_t keyBits = this->authBind.key; assert(keyBits < 4); return names[keyBits]; } +const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName() const +{ + assert(this->authBind.auth == 1); + return keyName(this->authBind.key); +} + +uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(uint64_t unsignedAddr, void* loc, bool addrDiv, uint16_t diversity, uint8_t key) +{ + // don't sign NULL + if ( unsignedAddr == 0 ) + return 0; + +#if __has_feature(ptrauth_calls) + uint64_t extendedDiscriminator = diversity; + if ( addrDiv ) + extendedDiscriminator = __builtin_ptrauth_blend_discriminator(loc, extendedDiscriminator); + switch ( key ) { + case 0: // IA + return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 0, extendedDiscriminator); + case 1: // IB + return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 1, extendedDiscriminator); + case 2: // DA + return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 2, extendedDiscriminator); + case 3: // DB + return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 3, extendedDiscriminator); + } + assert(0 && "invalid signing key"); +#else + assert(0 && "arm64e signing only arm64e"); +#endif +} + uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(void* loc, uint64_t target) const { assert(this->authBind.auth == 1); -#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; + return signPointer(target, loc, authBind.addrDiv, authBind.diversity, authBind.key); } uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Generic64::unpackedTarget() const @@ -1013,10 +1032,25 @@ uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Generic64::signExtendedAddend() return newValue; } +const char* MachOLoaded::ChainedFixupPointerOnDisk::Kernel64::keyName() const +{ + static const char* names[] = { + "IA", "IB", "DA", "DB" + }; + assert(this->isAuth == 1); + uint8_t keyBits = this->key; + assert(keyBits < 4); + return names[keyBits]; +} + bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const { switch (pointerFormat) { case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: if ( this->arm64e.bind.bind ) return false; if ( this->arm64e.authRebase.auth ) { @@ -1024,14 +1058,25 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, ui return true; } else { - targetRuntimeOffset = this->arm64e.unpackTarget() - preferedLoadAddress; + targetRuntimeOffset = this->arm64e.unpackTarget(); + if ( (pointerFormat == DYLD_CHAINED_PTR_ARM64E) || (pointerFormat == DYLD_CHAINED_PTR_ARM64E_FIRMWARE) ) { + targetRuntimeOffset -= preferedLoadAddress; + } return true; } break; case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: if ( this->generic64.bind.bind ) return false; - targetRuntimeOffset = this->generic64.unpackedTarget() - preferedLoadAddress; + targetRuntimeOffset = this->generic64.unpackedTarget(); + if ( pointerFormat == DYLD_CHAINED_PTR_64 ) + targetRuntimeOffset -= preferedLoadAddress; + return true; + break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + targetRuntimeOffset = this->kernel64.target; return true; break; case DYLD_CHAINED_PTR_32: @@ -1040,45 +1085,89 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, ui targetRuntimeOffset = this->generic32.rebase.target - preferedLoadAddress; return true; break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + targetRuntimeOffset = this->firmware32.target - preferedLoadAddress; + return true; + break; default: break; } assert(0 && "unsupported pointer chain format"); } -bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint32_t& bindOrdinal) const +bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint32_t& bindOrdinal, int64_t& addend) const { + addend = 0; switch (pointerFormat) { case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: if ( !this->arm64e.authBind.bind ) return false; if ( this->arm64e.authBind.auth ) { - bindOrdinal = this->arm64e.authBind.ordinal; + if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 ) + bindOrdinal = this->arm64e.authBind24.ordinal; + else + bindOrdinal = this->arm64e.authBind.ordinal; return true; } else { - bindOrdinal = this->arm64e.bind.ordinal; + if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 ) + bindOrdinal = this->arm64e.bind24.ordinal; + else + bindOrdinal = this->arm64e.bind.ordinal; + addend = this->arm64e.signExtendedAddend(); return true; } break; case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: if ( !this->generic64.bind.bind ) return false; bindOrdinal = this->generic64.bind.ordinal; + addend = this->generic64.bind.addend; return true; break; case DYLD_CHAINED_PTR_32: if ( !this->generic32.bind.bind ) return false; bindOrdinal = this->generic32.bind.ordinal; + addend = this->generic32.bind.addend; return true; break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + return false; default: break; } assert(0 && "unsupported pointer chain format"); } +unsigned MachOLoaded::ChainedFixupPointerOnDisk::strideSize(uint16_t pointerFormat) +{ + switch (pointerFormat) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + return 8; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + case DYLD_CHAINED_PTR_32_FIRMWARE: + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + return 4; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + return 1; + } + assert(0 && "unsupported pointer chain format"); +} + #if BUILDING_DYLD || BUILDING_LIBDYLD void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_starts_in_image* starts, uintptr_t slide, Array bindTargets, void (^logFixup)(void* loc, void* newValue)) const @@ -1089,16 +1178,20 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st #if __LP64__ #if __has_feature(ptrauth_calls) case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: if ( fixupLoc->arm64e.authRebase.auth ) { if ( fixupLoc->arm64e.authBind.bind ) { - if ( fixupLoc->arm64e.authBind.ordinal >= bindTargets.count() ) { - diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.authBind.ordinal, bindTargets.count()); + uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.authBind24.ordinal : fixupLoc->arm64e.authBind.ordinal; + if ( bindOrdinal >= bindTargets.count() ) { + diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.count()); stop = true; break; } else { // authenticated bind - newValue = (void*)(bindTargets[fixupLoc->arm64e.bind.ordinal]); + newValue = (void*)(bindTargets[bindOrdinal]); if (newValue != 0) // Don't sign missing weak imports newValue = (void*)fixupLoc->arm64e.signPointer(fixupLoc, (uintptr_t)newValue); } @@ -1110,19 +1203,23 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st } else { if ( fixupLoc->arm64e.bind.bind ) { - if ( fixupLoc->arm64e.bind.ordinal >= bindTargets.count() ) { - diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.bind.ordinal, bindTargets.count()); + uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.bind24.ordinal : fixupLoc->arm64e.bind.ordinal; + if ( bindOrdinal >= bindTargets.count() ) { + diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.count()); stop = true; break; } else { // plain bind - newValue = (void*)((long)bindTargets[fixupLoc->arm64e.bind.ordinal] + fixupLoc->arm64e.signExtendedAddend()); + newValue = (void*)((long)bindTargets[bindOrdinal] + fixupLoc->arm64e.signExtendedAddend()); } } else { - // plain rebase - newValue = (void*)(fixupLoc->arm64e.unpackTarget()+slide); + // plain rebase (old format target is vmaddr, new format target is offset) + if ( segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E ) + newValue = (void*)(fixupLoc->arm64e.unpackTarget()+slide); + else + newValue = (void*)((uintptr_t)this + fixupLoc->arm64e.unpackTarget()); } } if ( logFixup ) @@ -1131,6 +1228,7 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st break; #endif case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: if ( fixupLoc->generic64.bind.bind ) { if ( fixupLoc->generic64.bind.ordinal >= bindTargets.count() ) { diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->generic64.bind.ordinal, bindTargets.count()); @@ -1142,7 +1240,11 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st } } else { - newValue = (void*)(fixupLoc->generic64.unpackedTarget()+slide); + // plain rebase (old format target is vmaddr, new format target is offset) + if ( segInfo->pointer_format == DYLD_CHAINED_PTR_64 ) + newValue = (void*)(fixupLoc->generic64.unpackedTarget()+slide); + else + newValue = (void*)((uintptr_t)this + fixupLoc->generic64.unpackedTarget()); } if ( logFixup ) logFixup(fixupLoc, newValue); @@ -1184,26 +1286,31 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st } #endif -bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t pageIndex, uint16_t offsetInPage, - bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const + +bool MachOLoaded::walkChain(Diagnostics& diag, ChainedFixupPointerOnDisk* chain, uint16_t pointer_format, bool notifyNonPointers, uint32_t max_valid_pointer, + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const { - bool stop = false; - uint8_t* pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size); - ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage); - bool chainEnd = false; + const unsigned stride = ChainedFixupPointerOnDisk::strideSize(pointer_format); + bool stop = false; + bool chainEnd = false; while (!stop && !chainEnd) { // copy chain content, in case handler modifies location to final value ChainedFixupPointerOnDisk chainContent = *chain; - handler(chain, segInfo, stop); + handler(chain, stop); if ( !stop ) { - switch (segInfo->pointer_format) { + switch (pointer_format) { case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: if ( chainContent.arm64e.rebase.next == 0 ) chainEnd = true; else - chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*8); + chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*stride); break; case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: if ( chainContent.generic64.rebase.next == 0 ) chainEnd = true; else @@ -1215,15 +1322,28 @@ bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segm else { chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.generic32.rebase.next*4); if ( !notifyNonPointers ) { - while ( (chain->generic32.rebase.bind == 0) && (chain->generic32.rebase.target > segInfo->max_valid_pointer) ) { + while ( (chain->generic32.rebase.bind == 0) && (chain->generic32.rebase.target > max_valid_pointer) ) { // not a real pointer, but a non-pointer co-opted into chain chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chain->generic32.rebase.next*4); } } } break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + if ( chainContent.kernel64.next == 0 ) + chainEnd = true; + else + chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.kernel64.next*stride); + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + if ( chainContent.firmware32.next == 0 ) + chainEnd = true; + else + chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.firmware32.next*4); + break; default: - diag.error("unknown pointer format 0x%04X", segInfo->pointer_format); + diag.error("unknown pointer format 0x%04X", pointer_format); stop = true; } } @@ -1231,6 +1351,51 @@ bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segm return stop; } +void MachOLoaded::forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts, + void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop)) const +{ + bool stopped = false; + for (uint32_t segIndex=0; segIndex < starts->seg_count && !stopped; ++segIndex) { + if ( starts->seg_info_offset[segIndex] == 0 ) + continue; + const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]); + handler(segInfo, segIndex, stopped); + } +} + +void MachOLoaded::forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, bool notifyNonPointers, + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const +{ + auto adaptor = ^(ChainedFixupPointerOnDisk* fixupLocation, bool& stop) { + handler(fixupLocation, segInfo, stop); + }; + bool stopped = false; + for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) { + uint16_t offsetInPage = segInfo->page_start[pageIndex]; + if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE ) + continue; + if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) { + // 32-bit chains which may need multiple starts per page + uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI; + bool chainEnd = false; + while (!stopped && !chainEnd) { + chainEnd = (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST); + offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST); + uint8_t* pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size); + ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage); + stopped = walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor); + ++overflowIndex; + } + } + else { + // one chain per page + uint8_t* pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size); + ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage); + stopped = walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor); + } + } +} + void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const { @@ -1239,27 +1404,17 @@ void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_ if ( starts->seg_info_offset[segIndex] == 0 ) continue; const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]); - for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) { - uint16_t offsetInPage = segInfo->page_start[pageIndex]; - if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE ) - continue; - if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) { - // 32-bit chains which may need multiple starts per page - uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI; - bool chainEnd = false; - while (!stopped && !chainEnd) { - chainEnd = (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST); - offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST); - if ( walkChain(diag, segInfo, pageIndex, offsetInPage, notifyNonPointers, handler) ) - stopped = true; - ++overflowIndex; - } - } - else { - // one chain per page - walkChain(diag, segInfo, pageIndex, offsetInPage, notifyNonPointers, handler); - } - } + forEachFixupInSegmentChains(diag, segInfo, notifyNonPointers, handler); + } +} + +void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, uint16_t pointer_format, uint32_t starts_count, const uint32_t chain_starts[], + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const +{ + for (uint32_t i=0; i < starts_count; ++i) { + ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)((uint8_t*)this + chain_starts[i]); + if ( walkChain(diag, chain, pointer_format, false, 0, handler) ) + break; } } diff --git a/dyld3/MachOLoaded.h b/dyld3/MachOLoaded.h index 45770aa..f53c22c 100644 --- a/dyld3/MachOLoaded.h +++ b/dyld3/MachOLoaded.h @@ -30,7 +30,7 @@ #include "MachOFile.h" -class CacheBuilder; +class SharedCacheBuilder; namespace dyld3 { @@ -79,6 +79,7 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile Array bindTargets, void (^fixupLogger)(void* loc, void* newValue)) const; #endif + 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; // For use with new rebase/bind scheme were each fixup location on disk contains info on what @@ -90,11 +91,15 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile dyld_chained_ptr_arm64e_auth_bind authBind; dyld_chained_ptr_arm64e_rebase rebase; dyld_chained_ptr_arm64e_bind bind; + dyld_chained_ptr_arm64e_bind24 bind24; + dyld_chained_ptr_arm64e_auth_bind24 authBind24; uint64_t signExtendedAddend() const; uint64_t unpackTarget() const; const char* keyName() const; uint64_t signPointer(void* loc, uint64_t target) const; + static uint64_t signPointer(uint64_t unsignedPtr, void* loc, bool addrDiv, uint16_t diversity, uint8_t key); + static const char* keyName(uint8_t keyBits); }; union Generic64 { @@ -112,19 +117,27 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile uint64_t signExtendedAddend() const; }; + struct Kernel64 : dyld_chained_ptr_64_kernel_cache_rebase { + const char* keyName() const; + }; + + struct Firm32 : dyld_chained_ptr_32_firmware_rebase { }; + typedef dyld_chained_ptr_32_cache_rebase Cache32; uint64_t raw64; Arm64e arm64e; Generic64 generic64; + Kernel64 kernel64; uint32_t raw32; Generic32 generic32; Cache32 cache32; - + Firm32 firmware32; bool isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const; - bool isBind(uint16_t pointerFormat, uint32_t& bindOrdinal) const; + bool isBind(uint16_t pointerFormat, uint32_t& bindOrdinal, int64_t& addend) const; + static unsigned strideSize(uint16_t pointerFormat); }; @@ -135,6 +148,7 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile uint32_t linkeditFileOffset; uint32_t linkeditFileSize; uint32_t linkeditSegIndex; + uint32_t lastSegIndex; }; struct LinkEditInfo @@ -152,11 +166,21 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile }; void getLinkEditPointers(Diagnostics& diag, LinkEditInfo&) const; - // use by dyldinfo + void forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts, + void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop)) const; + + void forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, bool notifyNonPointers, + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const; + + // for dyld loaded images void forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers, void (^callback)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const; + // for preload images + void forEachFixupInAllChains(Diagnostics& diag, uint16_t pointer_format, uint32_t starts_count, const uint32_t chain_starts[], + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const; + protected: - friend CacheBuilder; + friend SharedCacheBuilder; struct FoundSymbol { enum class Kind { headerOffset, absolute, resolverOffset }; @@ -169,18 +193,14 @@ protected: const char* foundSymbolName; }; - -protected: - friend CacheBuilder; - bool findExportedSymbol(Diagnostics& diag, const char* symbolName, bool weakImport, FoundSymbol& foundInfo, DependentToMachOLoaded finder) const; void getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const; void getLayoutInfo(LayoutInfo&) const; const uint8_t* getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const; const uint8_t* getExportsTrie(const LinkEditInfo& info, uint64_t& trieSize) 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; @@ -190,9 +210,8 @@ protected: // On all other platforms this always returns a single best cd hash (ranked to match the kernel). // Note the callback parameter is really a CS_CodeDirectory. void forEachCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen, void (^callback)(const void* cd)) const; - bool walkChain(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t pageIndex, uint16_t offsetInPage, - bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const; - + bool walkChain(Diagnostics& diag, ChainedFixupPointerOnDisk* start, uint16_t pointer_format, bool notifyNonPointers, uint32_t max_valid_pointer, + void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const; }; diff --git a/dyld3/PathOverrides.cpp b/dyld3/PathOverrides.cpp index 0c21970..4949b63 100644 --- a/dyld3/PathOverrides.cpp +++ b/dyld3/PathOverrides.cpp @@ -33,13 +33,13 @@ #include #include #include +#include #include #include "PathOverrides.h" - namespace dyld3 { namespace closure { @@ -94,7 +94,7 @@ PathOverrides::~PathOverrides() void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const { - if ( _insertedDylibs != nullptr ) { + if ( _insertedDylibs != nullptr && _insertedDylibs[0] != '\0' ) { forEachInColonList(_insertedDylibs, nullptr, ^(const char* path, bool &stop) { handler(path, stop); }); @@ -401,6 +401,25 @@ void PathOverrides::forEachPathVariant(const char* initialPath, bool pathIsInDyl return; } + // try rootpath + bool searchiOSSupport = (platform == Platform::iOSMac); +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + if ( platform == Platform::iOS ) { + searchiOSSupport = true; + // some old Almond apps reference old WebKit location + if ( strcmp(initialPath, "/System/Library/PrivateFrameworks/WebKit.framework/WebKit") == 0 ) + initialPath = "/System/Library/Frameworks/WebKit.framework/WebKit"; + } +#endif + if ( searchiOSSupport ) { + char rtpath[strlen("/System/iOSSupport")+strlen(initialPath)+8]; + strcpy(rtpath, "/System/iOSSupport"); + strcat(rtpath, initialPath); + forEachImageSuffix(rtpath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler); + if ( stop ) + return; + } + // try original path forEachImageSuffix(initialPath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler); if ( stop ) @@ -533,7 +552,7 @@ const char* PathPool::add(const char* path) return _next->add(path); } -void PathPool::forEachPath(void (^handler)(const char* path)) +void PathPool::forEachPath(void (^handler)(const char* path)) const { for (const char* s = _buffer; s < _current; ++s) { handler(s); @@ -544,6 +563,20 @@ void PathPool::forEachPath(void (^handler)(const char* path)) _next->forEachPath(handler); } +bool PathPool::contains(const char* path) const +{ + for (const char* s = _buffer; s < _current; ++s) { + if ( strcmp(s, path) == 0 ) + return true; + s += strlen(s); + } + + if ( _next != nullptr ) + return _next->contains(path); + + return false; +} + } // namespace closure diff --git a/dyld3/PathOverrides.h b/dyld3/PathOverrides.h index 1478f36..81251ab 100644 --- a/dyld3/PathOverrides.h +++ b/dyld3/PathOverrides.h @@ -42,7 +42,8 @@ public: static PathPool* allocate(); static void deallocate(PathPool* pool); const char* add(const char* path); - void forEachPath(void (^handler)(const char* path)); + void forEachPath(void (^handler)(const char* path)) const; + bool contains(const char* path) const; private: enum { kAllocationSize = 32*1024 }; diff --git a/dyld3/PointerAuth.h b/dyld3/PointerAuth.h new file mode 100644 index 0000000..998006a --- /dev/null +++ b/dyld3/PointerAuth.h @@ -0,0 +1,90 @@ +/* + * 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_POINTER_AUTH_H__ +#define __DYLD_POINTER_AUTH_H__ + +#include + +#if __has_feature(ptrauth_calls) +#define __ptrauth_dyld_address_auth __ptrauth(ptrauth_key_process_dependent_data, 1, 0) +#define __ptrauth_dyld_function_ptr __ptrauth(ptrauth_key_process_dependent_code, 1, 0) +#else +#define __ptrauth_dyld_address_auth +#define __ptrauth_dyld_function_ptr +#endif + +namespace dyld3 { + +// On arm64e, signs the given pointer with the address of where it is stored. +// Other archs just have a regular pointer +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wptrauth-null-pointers" +template +struct AuthenticatedValue { + static_assert(sizeof(T) <= sizeof(uintptr_t)); + + AuthenticatedValue() { + this->value = ptrauth_sign_unauthenticated(nullptr, ptrauth_key_process_dependent_data, this); + } + ~AuthenticatedValue() = default; + AuthenticatedValue(const AuthenticatedValue& other) { + this->value = ptrauth_auth_and_resign(other.value, + ptrauth_key_process_dependent_data, &other, + ptrauth_key_process_dependent_data, this); + } + AuthenticatedValue(AuthenticatedValue&& other) { + this->value = ptrauth_auth_and_resign(other.value, + ptrauth_key_process_dependent_data, &other, + ptrauth_key_process_dependent_data, this); + other.value = ptrauth_sign_unauthenticated(nullptr, ptrauth_key_process_dependent_data, &other); + } + + AuthenticatedValue& operator=(const AuthenticatedValue&) = delete; + AuthenticatedValue& operator=(AuthenticatedValue&&) = delete; + + // Add a few convenience methods for interoperating with values of the given type + AuthenticatedValue& operator=(const T& other) { + this->value = ptrauth_sign_unauthenticated(other, ptrauth_key_process_dependent_data, this); + return *this; + } + bool operator==(const T& other) const { + return ptrauth_auth_data(this->value, ptrauth_key_process_dependent_data, this) == other; + } + bool operator!=(const T& other) const { + return ptrauth_auth_data(this->value, ptrauth_key_process_dependent_data, this) != other; + } + +private: + const void* value; +}; + + +#pragma clang diagnostic pop + +} // namespace dyld3 + +#endif // __DYLD_POINTER_AUTH_H__ + diff --git a/dyld3/RootsChecker.cpp b/dyld3/RootsChecker.cpp new file mode 100644 index 0000000..765b45d --- /dev/null +++ b/dyld3/RootsChecker.cpp @@ -0,0 +1,140 @@ +/* +* Copyright (c) 2020 Apple Inc. All rights reserved. +* +* @APPLE_LICENSE_HEADER_START@ +* +* This file contains Original Code and/or Modifications of Original Code +* as defined in and that are subject to the Apple Public Source License +* Version 2.0 (the 'License'). You may not use this file except in +* compliance with the License. Please obtain a copy of the License at +* http://www.opensource.apple.com/apsl/ and read it before using this +* file. +* +* The Original Code and all software distributed under the License are +* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +* Please see the License for the specific language governing rights and +* limitations under the License. +* +* @APPLE_LICENSE_HEADER_END@ +*/ + +#include + +#include "ClosureFileSystemPhysical.h" +#include "DyldSharedCache.h" +#include "MachOAnalyzer.h" +#include "RootsChecker.h" + +#if DYLD_SIMULATOR_ROOTS_SUPPORT +#include "SharedCacheRuntime.h" +#endif + +namespace dyld3 { + +#if DYLD_SIMULATOR_ROOTS_SUPPORT +static bool imageUUIDMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem, + const DyldSharedCache* cache, const closure::Image* image) { + // We can only use the cache if the file UUID matches the shared cache + bool uuidMatchesCache = false; + + // The simulator can't pass in a file system as its not hooked up to the vector. + // They can just pass in nullptr where needed and we'll assume its the physical file system + closure::FileSystemPhysical fileSystemPhysical; + if ( fileSystem == nullptr ) + fileSystem = &fileSystemPhysical; + + Diagnostics diag; + Platform platform = cache->platform(); + const GradedArchs& archs = GradedArchs::forName(cache->archName(), true); + char realerPath[MAXPATHLEN]; + dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, *fileSystem, path, archs, platform, realerPath); + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent; + if ( ma != nullptr ) { + uuid_t uuid; + uuid_t image_uuid; + if ( !ma->getUuid(uuid) ) + memset(&uuid, 0, sizeof(uuid_t)); + if ( !image->getUuid(image_uuid) ) + memset(&image_uuid, 0, sizeof(uuid_t)); + uuidMatchesCache = ( memcmp(uuid, image_uuid, sizeof(uuid_t)) == 0 ); + fileSystem->unloadFile(loadedFileInfo); + } + return uuidMatchesCache; +} + +bool RootsChecker::uuidMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem, + const DyldSharedCache* cache) { + const dyld3::closure::ImageArray* images = cache->cachedDylibsImageArray(); + const closure::Image* image = nullptr; + uint32_t imageIndex; + if ( cache->hasImagePath(path, imageIndex) ) { + image = images->imageForNum(imageIndex+1); + } + return imageUUIDMatchesSharedCache(path, fileSystem, cache, image); +} +#endif + +bool RootsChecker::onDiskFileIsRoot(const char* path, const DyldSharedCache* cache, + const closure::Image* image, + const closure::FileSystem* fileSystem, + uint64_t inode, uint64_t modtime) const { + + // Upon entry, we know the dylib exists and has a mod time and inode. Now + // we need to see if its a root or not + + // Note we don't check if dylibs are expected on disk here. We assume that an + // image only has a mod time and inode if its expected on disk + uint64_t expectedINode; + uint64_t expectedMtime; + if ( image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) { + if ( (expectedMtime == modtime) && (expectedINode == inode) ) + return false; + // mod time or inode don't match, so this is a root + return true; + } + +#if DYLD_SIMULATOR_ROOTS_SUPPORT + if ( strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0 ) { + // If this was a root when launchd checked, then assume we are a root now + if ( libsystemKernelIsRoot ) + return true; + + // If the file system is read-only, then this cannot be a root now + if ( !fileSystemCanBeModified ) + return false; + + // Possibly a root. Open the file and check + return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image); + } else if ( strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0 ) { + // If this was a root when launchd checked, then assume we are a root now + if ( libsystemPlatformIsRoot ) + return true; + + // If the file system is read-only, then this cannot be a root now + if ( !fileSystemCanBeModified ) + return false; + + // Possibly a root. Open the file and check + return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image); + } else if ( strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0 ) { + // If this was a root when launchd checked, then assume we are a root now + if ( libsystemPThreadIsRoot ) + return true; + + // If the file system is read-only, then this cannot be a root now + if ( !fileSystemCanBeModified ) + return false; + + // Possibly a root. Open the file and check + return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image); + } +#endif + + // If we aren't a special simulator dylib, then we must be a root + return true; +} + +} // namespace dyld3 diff --git a/dyld3/RootsChecker.h b/dyld3/RootsChecker.h new file mode 100644 index 0000000..87e7195 --- /dev/null +++ b/dyld3/RootsChecker.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 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_ROOTS_CHECKER_H__ +#define __DYLD_ROOTS_CHECKER_H__ + +#include + +#if TARGET_OS_OSX && (BUILDING_DYLD || BUILDING_CLOSURE_UTIL) && !TARGET_OS_SIMULATOR +#define DYLD_SIMULATOR_ROOTS_SUPPORT 1 +#else +#define DYLD_SIMULATOR_ROOTS_SUPPORT 0 +#endif + +class DyldSharedCache; + +namespace dyld3 { + +namespace closure { +class FileSystem; +struct Image; +} + +#define VIS_HIDDEN __attribute__((visibility("hidden"))) + +class VIS_HIDDEN RootsChecker +{ +public: + RootsChecker() = default; + + // Does a on-disk binary represent a root. + // If the cache expects dylibs on disk, then so long as the dylib matches + // the mod time and inode in the cache, it is not a root. + // For the macOS simulator support dylibs, which are on disk, whether they + // are roots depends on the state we are tracking in the comm page. + // For all other dylibs and all other platforms, on-disk dylibs are always roots + bool onDiskFileIsRoot(const char* path, const DyldSharedCache* cache, const closure::Image* image, + const closure::FileSystem* fileSystem, + uint64_t inode, uint64_t modtime) const; + +#if DYLD_SIMULATOR_ROOTS_SUPPORT + static bool uuidMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem, + const DyldSharedCache* cache); + + void setLibsystemKernelIsRoot(bool v) { + libsystemKernelIsRoot = v; + } + void setLibsystemPlatformIsRoot(bool v) { + libsystemPlatformIsRoot = v; + } + void setLibsystemPThreadIsRoot(bool v) { + libsystemPThreadIsRoot = v; + } + void setFileSystemCanBeModified(bool v) { + fileSystemCanBeModified = v; + } +#endif + +private: + // By default, assume nothing is a root, but that the file system is writable. + // This should be conservatively correct. +#if DYLD_SIMULATOR_ROOTS_SUPPORT + bool libsystemKernelIsRoot = false; + bool libsystemPlatformIsRoot = false; + bool libsystemPThreadIsRoot = false; + bool fileSystemCanBeModified = true; +#endif +}; + +} // namespace dyld3 + +#endif // __DYLD_ROOTS_CHECKER_H__ diff --git a/dyld3/SharedCacheRuntime.cpp b/dyld3/SharedCacheRuntime.cpp index 0c1a14a..590918b 100644 --- a/dyld3/SharedCacheRuntime.cpp +++ b/dyld3/SharedCacheRuntime.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -53,12 +54,20 @@ // should be in mach/shared_region.h extern "C" int __shared_region_check_np(uint64_t* startaddress); extern "C" int __shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], long slide, const dyld_cache_slide_info2* slideInfo, size_t slideInfoSize); +extern "C" int __shared_region_map_and_slide_2_np(uint32_t files_count, const shared_file_np files[], uint32_t mappings_count, const shared_file_mapping_slide_np mappings[]); +#ifndef VM_PROT_NOAUTH +#define VM_PROT_NOAUTH 0x40 /* must not interfere with normal prot assignments */ +#endif + +extern bool gEnableSharedCacheDataConst; namespace dyld { - extern int my_stat(const char* path, struct stat* buf); - extern int my_open(const char* path, int flag, int other); extern void log(const char*, ...); + extern void logToConsole(const char* format, ...); +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + bool isTranslated(); +#endif } @@ -67,13 +76,13 @@ namespace dyld3 { struct CacheInfo { - int fd; - shared_file_mapping_np mappings[3]; - uint64_t slideInfoAddressUnslid; - size_t slideInfoSize; - uint64_t sharedRegionStart; - uint64_t sharedRegionSize; - uint64_t maxSlide; + shared_file_mapping_slide_np mappings[DyldSharedCache::MaxMappings]; + uint32_t mappingsCount; + // All mappings come from the same file + int fd = 0; + uint64_t sharedRegionStart; + uint64_t sharedRegionSize; + uint64_t maxSlide; }; @@ -169,57 +178,123 @@ static void rebaseChainV4(uint8_t* pageContent, uint16_t startOffset, uintptr_t } #endif -static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[]) -{ +#if TARGET_OS_OSX +bool getMacOSCachePath(char pathBuffer[], size_t pathBufferSize, + const char* cacheDir, bool useHaswell) { + // Clear old attempts at finding a cache, if any + pathBuffer[0] = '\0'; + // set cache dir - if ( options.cacheDirOverride != nullptr ) { - strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize); - } - else { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR)); -#else - strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR)); -#endif - } + strlcpy(pathBuffer, cacheDir, pathBufferSize); // append file component of cache file if ( pathBuffer[strlen(pathBuffer)-1] != '/' ) strlcat(pathBuffer, "/", pathBufferSize); -#if __x86_64__ && !__IPHONE_OS_VERSION_MIN_REQUIRED - if ( options.useHaswell ) { + +#if __x86_64__ + if ( useHaswell ) { size_t len = strlen(pathBuffer); struct stat haswellStatBuf; strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, pathBufferSize); - if ( dyld::my_stat(pathBuffer, &haswellStatBuf) == 0 ) - return; + if ( dyld3::stat(pathBuffer, &haswellStatBuf) == 0 ) + return true; // no haswell cache file, use regular x86_64 cache pathBuffer[len] = '\0'; } #endif + struct stat statBuf; + strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize); + if ( dyld3::stat(pathBuffer, &statBuf) == 0 ) + return true; + + return false; +} +#endif // TARGET_OS_OSX + +static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[]) +{ +#if TARGET_OS_OSX + + if ( options.cacheDirOverride != nullptr ) { + getMacOSCachePath(pathBuffer, pathBufferSize, options.cacheDirOverride, options.useHaswell); + } else { + getMacOSCachePath(pathBuffer, pathBufferSize, MACOSX_MRM_DYLD_SHARED_CACHE_DIR, options.useHaswell); + } + +#else // TARGET_OS_OSX + + // Non-macOS path + if ( options.cacheDirOverride != nullptr ) { + strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize); + } else { + strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR)); + } + + // append file component of cache file + if ( pathBuffer[strlen(pathBuffer)-1] != '/' ) + strlcat(pathBuffer, "/", pathBufferSize); + strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize); -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // use .development cache if it exists - struct stat enableStatBuf; + if ( BootArgs::forceCustomerCache() ) { + // The boot-arg always wins. Use the customer cache if we are told to + return; + } + if ( !dyld3::internalInstall() ) { + // We can't use the development cache on customer installs + return; + } + if ( BootArgs::forceDevelopmentCache() ) { + // The boot-arg always wins. Use the development cache if we are told to + strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize); + return; + } + + // If only one or the other caches exists, then use the one we have 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 ( !BootArgs::forceCustomerCache() && developmentDevice && ((enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists) ) + bool devCacheExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0); + bool optCacheExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &optCacheStatBuf) == 0); + if ( !devCacheExists ) { + // If the dev cache doesn't exist, then use the customer cache + return; + } + if ( !optCacheExists ) { + // If the customer cache doesn't exist, then use the development cache strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize); + return; + } + + // Finally, check for the sentinels + struct stat enableStatBuf; + //struct stat sentinelStatBuf; + bool enableFileExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0); + // FIXME: rdar://problem/59813537 Re-enable once automation is updated to use boot-arg + bool sentinelFileExists = false; + //bool sentinelFileExists = (dyld3::stat(MACOSX_MRM_DYLD_SHARED_CACHE_DIR "enable_development_mode", &sentinelStatBuf) == 0); + if ( enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) ) { + // if the old enable file exists, use the development cache + strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize); + return; + } + if ( sentinelFileExists ) { + // If the new sentinel exists, then use the development cache + strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize); + return; + } #endif +#endif //!TARGET_OS_OSX } int openSharedCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results) { getCachePath(options, sizeof(results->path), results->path); - return dyld::my_open(results->path, O_RDONLY, 0); + return dyld3::open(results->path, O_RDONLY, 0); } static bool validMagic(const SharedCacheOptions& options, const DyldSharedCache* cache) @@ -258,15 +333,56 @@ static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCac } #if !TARGET_OS_SIMULATOR -static void verboseSharedCacheMappings(const shared_file_mapping_np mappings[3]) +static void verboseSharedCacheMappings(const shared_file_mapping_slide_np mappings[DyldSharedCache::MaxMappings], + uint32_t mappingsCount) { - for (int i=0; i < 3; ++i) { - dyld::log(" 0x%08llX->0x%08llX init=%x, max=%x %s%s%s\n", - mappings[i].sfm_address, mappings[i].sfm_address+mappings[i].sfm_size-1, - mappings[i].sfm_init_prot, mappings[i].sfm_init_prot, - ((mappings[i].sfm_init_prot & VM_PROT_READ) ? "read " : ""), - ((mappings[i].sfm_init_prot & VM_PROT_WRITE) ? "write " : ""), - ((mappings[i].sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : "")); + for (int i=0; i < mappingsCount; ++i) { + const char* mappingName = ""; + if ( mappings[i].sms_max_prot & VM_PROT_WRITE ) { + if ( mappings[i].sms_max_prot & VM_PROT_NOAUTH ) { + // __DATA* + mappingName = "data"; + } else { + // __AUTH* + mappingName = "auth"; + } + } + uint32_t init_prot = mappings[i].sms_init_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + uint32_t max_prot = mappings[i].sms_max_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + dyld::log(" 0x%08llX->0x%08llX init=%x, max=%x %s%s%s%s\n", + mappings[i].sms_address, mappings[i].sms_address+mappings[i].sms_size-1, + init_prot, max_prot, + ((mappings[i].sms_init_prot & VM_PROT_READ) ? "read " : ""), + ((mappings[i].sms_init_prot & VM_PROT_WRITE) ? "write " : ""), + ((mappings[i].sms_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), + mappingName); + } +} + + +static void verboseSharedCacheMappingsToConsole(const shared_file_mapping_slide_np mappings[DyldSharedCache::MaxMappings], + uint32_t mappingsCount) +{ + for (int i=0; i < mappingsCount; ++i) { + const char* mappingName = ""; + if ( mappings[i].sms_max_prot & VM_PROT_WRITE ) { + if ( mappings[i].sms_max_prot & VM_PROT_NOAUTH ) { + // __DATA* + mappingName = "data"; + } else { + // __AUTH* + mappingName = "auth"; + } + } + uint32_t init_prot = mappings[i].sms_init_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + uint32_t max_prot = mappings[i].sms_max_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); + dyld::logToConsole("dyld: mapping 0x%08llX->0x%08llX init=%x, max=%x %s%s%s%s\n", + mappings[i].sms_address, mappings[i].sms_address+mappings[i].sms_size-1, + init_prot, max_prot, + ((mappings[i].sms_init_prot & VM_PROT_READ) ? "read " : ""), + ((mappings[i].sms_init_prot & VM_PROT_WRITE) ? "write " : ""), + ((mappings[i].sms_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), + mappingName); } } #endif @@ -282,7 +398,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa } struct stat cacheStatBuf; - if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) { + if ( dyld3::stat(results->path, &cacheStatBuf) != 0 ) { results->errorMessage = "shared cache file stat() failed"; ::close(fd); return false; @@ -307,39 +423,41 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa ::close(fd); return false; } - if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x138) ) { + if ( (cache->header.mappingCount < 3) || (cache->header.mappingCount > DyldSharedCache::MaxMappings) || (cache->header.mappingOffset > 0x168) ) { results->errorMessage = "shared cache file mappings are invalid"; ::close(fd); return false; } const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset]; - if ( (fileMappings[0].fileOffset != 0) - || ((fileMappings[0].address + fileMappings[0].size) > fileMappings[1].address) - || ((fileMappings[1].address + fileMappings[1].size) > fileMappings[2].address) - || ((fileMappings[0].fileOffset + fileMappings[0].size) != fileMappings[1].fileOffset) - || ((fileMappings[1].fileOffset + fileMappings[1].size) != fileMappings[2].fileOffset) + const dyld_cache_mapping_info* textMapping = &fileMappings[0]; + const dyld_cache_mapping_info* firstDataMapping = &fileMappings[1]; + const dyld_cache_mapping_info* linkeditMapping = &fileMappings[cache->header.mappingCount - 1]; + if ( (textMapping->fileOffset != 0) + || ((fileMappings[0].address + fileMappings[0].size) > firstDataMapping->address) + || ((fileMappings[0].fileOffset + fileMappings[0].size) != firstDataMapping->fileOffset) || ((cache->header.codeSignatureOffset + cache->header.codeSignatureSize) != cacheFileLength) - || (fileMappings[0].maxProt != (VM_PROT_READ|VM_PROT_EXECUTE)) - || (fileMappings[1].maxProt != (VM_PROT_READ|VM_PROT_WRITE)) - || (fileMappings[2].maxProt != VM_PROT_READ) ) { - results->errorMessage = "shared cache file mappings are invalid"; + || (textMapping->maxProt != (VM_PROT_READ|VM_PROT_EXECUTE)) + || (linkeditMapping->maxProt != VM_PROT_READ) ) { + results->errorMessage = "shared cache text/linkedit mappings are invalid"; ::close(fd); return false; } - if ( cache->header.mappingOffset >= 0xF8 ) { - if ( (fileMappings[0].address != cache->header.sharedRegionStart) || ((fileMappings[2].address + fileMappings[2].size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) { - results->errorMessage = "shared cache file mapping addressses invalid"; + // Check the __DATA mappings + for (unsigned i = 1; i != (cache->header.mappingCount - 1); ++i) { + if ( ((fileMappings[i].address + fileMappings[i].size) > fileMappings[i + 1].address) + || ((fileMappings[i].fileOffset + fileMappings[i].size) != fileMappings[i + 1].fileOffset) + || (fileMappings[i].maxProt != (VM_PROT_READ|VM_PROT_WRITE)) ) { + results->errorMessage = "shared cache data mappings are invalid"; ::close(fd); return false; } } - else { - if ( (fileMappings[0].address != SHARED_REGION_BASE) || ((fileMappings[2].address + fileMappings[2].size) > (SHARED_REGION_BASE+SHARED_REGION_SIZE)) ) { - results->errorMessage = "shared cache file mapping addressses invalid"; - ::close(fd); - return false; - } + + if ( (textMapping->address != cache->header.sharedRegionStart) || ((linkeditMapping->address + linkeditMapping->size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) { + results->errorMessage = "shared cache file mapping addressses invalid"; + ::close(fd); + return false; } // register code signature of cache file @@ -375,28 +493,59 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa ::munmap(mappedData, sizeof(firstPage)); // fill out results - info->fd = fd; - for (int i=0; i < 3; ++i) { - info->mappings[i].sfm_address = fileMappings[i].address; - info->mappings[i].sfm_size = fileMappings[i].size; - info->mappings[i].sfm_file_offset = fileMappings[i].fileOffset; - info->mappings[i].sfm_max_prot = fileMappings[i].maxProt; - info->mappings[i].sfm_init_prot = fileMappings[i].initProt; - } - info->mappings[1].sfm_max_prot |= VM_PROT_SLIDE; - info->mappings[1].sfm_init_prot |= VM_PROT_SLIDE; - info->slideInfoAddressUnslid = fileMappings[2].address + cache->header.slideInfoOffset - fileMappings[2].fileOffset; - info->slideInfoSize = (long)cache->header.slideInfoSize; - if ( cache->header.mappingOffset >= 0xf8 ) { - info->sharedRegionStart = cache->header.sharedRegionStart; - info->sharedRegionSize = cache->header.sharedRegionSize; - info->maxSlide = cache->header.maxSlide; - } - else { - info->sharedRegionStart = SHARED_REGION_BASE; - info->sharedRegionSize = SHARED_REGION_SIZE; - info->maxSlide = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address); + info->mappingsCount = cache->header.mappingCount; + // We have to emit the mapping for the __LINKEDIT before the slid mappings + // This is so that the kernel has already mapped __LINKEDIT in to its address space + // for when it copies the slid info for each __DATA mapping + for (int i=0; i < cache->header.mappingCount; ++i) { + uint64_t slideInfoFileOffset = 0; + uint64_t slideInfoFileSize = 0; + vm_prot_t authProt = 0; + vm_prot_t initProt = fileMappings[i].initProt; + if ( cache->header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) { + // Old cache without the new slid mappings + if ( i == 1 ) { + // Add slide info to the __DATA mapping + slideInfoFileOffset = cache->header.slideInfoOffsetUnused; + slideInfoFileSize = cache->header.slideInfoSizeUnused; + // Don't set auth prot to anything interseting on the old mapppings + authProt = 0; + } + } else { + // New cache where each mapping has a corresponding slid mapping + const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)&firstPage[cache->header.mappingWithSlideOffset]; + slideInfoFileOffset = slidableMappings[i].slideInfoFileOffset; + slideInfoFileSize = slidableMappings[i].slideInfoFileSize; + if ( (slidableMappings[i].flags & DYLD_CACHE_MAPPING_AUTH_DATA) == 0 ) + authProt = VM_PROT_NOAUTH; + if ( (slidableMappings[i].flags & DYLD_CACHE_MAPPING_CONST_DATA) != 0 ) { + // The cache was built with __DATA_CONST being read-only. We can override that + // with a boot-arg + if ( !gEnableSharedCacheDataConst ) + initProt |= VM_PROT_WRITE; + } + } + + // Add a file for each mapping + info->fd = fd; + info->mappings[i].sms_address = fileMappings[i].address; + info->mappings[i].sms_size = fileMappings[i].size; + info->mappings[i].sms_file_offset = fileMappings[i].fileOffset; + info->mappings[i].sms_slide_size = 0; + info->mappings[i].sms_slide_start = 0; + info->mappings[i].sms_max_prot = fileMappings[i].maxProt; + info->mappings[i].sms_init_prot = initProt; + if ( slideInfoFileSize != 0 ) { + uint64_t offsetInLinkEditRegion = (slideInfoFileOffset - linkeditMapping->fileOffset); + info->mappings[i].sms_slide_start = (user_addr_t)(linkeditMapping->address + offsetInLinkEditRegion); + info->mappings[i].sms_slide_size = (user_addr_t)slideInfoFileSize; + info->mappings[i].sms_init_prot |= (VM_PROT_SLIDE | authProt); + info->mappings[i].sms_max_prot |= (VM_PROT_SLIDE | authProt); + } } + info->sharedRegionStart = cache->header.sharedRegionStart; + info->sharedRegionSize = cache->header.sharedRegionSize; + info->maxSlide = cache->header.maxSlide; return true; } @@ -404,14 +553,10 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa #if !TARGET_OS_SIMULATOR // update all __DATA pages with slide info -static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo* results) +static bool rebaseDataPages(bool isVerbose, const dyld_cache_slide_info* slideInfo, const uint8_t *dataPagesStart, + uint64_t sharedRegionStart, 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; + const dyld_cache_slide_info* slideInfoHeader = slideInfo; if ( slideInfoHeader != nullptr ) { if ( slideInfoHeader->version == 2 ) { const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo; @@ -450,6 +595,7 @@ static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo 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]; + //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, delta); if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) continue; delta = delta/sizeof(uint64_t); // initial offset is byte based @@ -459,7 +605,7 @@ static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo delta = loc->plain.offsetToNextPointer; if ( loc->auth.authenticated ) { #if __has_feature(ptrauth_calls) - uint64_t target = info.sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide; + uint64_t target = sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide; MachOLoaded::ChainedFixupPointerOnDisk ptr; ptr.raw64 = *((uint64_t*)loc); loc->raw = ptr.arm64e.signPointer(loc, target); @@ -532,42 +678,66 @@ static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoa // we don't know the path this cache was previously loaded from, assume default getCachePath(options, sizeof(results->path), results->path); if ( options.verbose ) { - const shared_file_mapping_np* const mappings = (shared_file_mapping_np*)(cacheBaseAddress + existingCache->header.mappingOffset); + const dyld_cache_mapping_and_slide_info* const mappings = (const dyld_cache_mapping_and_slide_info*)(cacheBaseAddress + existingCache->header.mappingWithSlideOffset); dyld::log("re-using existing shared cache (%s):\n", results->path); - shared_file_mapping_np slidMappings[3]; - for (int i=0; i < 3; ++i) { - slidMappings[i] = mappings[i]; - slidMappings[i].sfm_address += results->slide; + shared_file_mapping_slide_np slidMappings[DyldSharedCache::MaxMappings]; + for (int i=0; i < DyldSharedCache::MaxMappings; ++i) { + slidMappings[i].sms_address = mappings[i].address; + slidMappings[i].sms_size = mappings[i].size; + slidMappings[i].sms_file_offset = mappings[i].fileOffset; + slidMappings[i].sms_max_prot = mappings[i].maxProt; + slidMappings[i].sms_init_prot = mappings[i].initProt; + slidMappings[i].sms_address += results->slide; + if ( existingCache->header.mappingOffset > __offsetof(dyld_cache_header, mappingWithSlideOffset) ) { + // New caches have slide info on each new mapping + const dyld_cache_mapping_and_slide_info* const slidableMappings = (dyld_cache_mapping_and_slide_info*)(cacheBaseAddress + existingCache->header.mappingWithSlideOffset); + assert(existingCache->header.mappingWithSlideCount <= DyldSharedCache::MaxMappings); + if ( !(slidableMappings[i].flags & DYLD_CACHE_MAPPING_AUTH_DATA) ) { + slidMappings[i].sms_max_prot |= VM_PROT_NOAUTH; + slidMappings[i].sms_init_prot |= VM_PROT_NOAUTH; + } + if ( (slidableMappings[i].flags & DYLD_CACHE_MAPPING_CONST_DATA) != 0 ) { + // The cache was built with __DATA_CONST being read-only. We can override that + // with a boot-arg + if ( !gEnableSharedCacheDataConst ) + slidMappings[i].sms_init_prot |= VM_PROT_WRITE; + } + } } - verboseSharedCacheMappings(slidMappings); + verboseSharedCacheMappings(slidMappings, existingCache->header.mappingCount); } } else { results->errorMessage = "existing shared cache in memory is not compatible"; } + return true; } return false; } -static long pickCacheASLR(CacheInfo& info) +static long pickCacheASLRSlide(CacheInfo& info) { // choose new random slide -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE || (TARGET_OS_OSX && TARGET_CPU_ARM64) // change shared cache slide for 32-bit arm to always be 16k aligned - long slide = ((arc4random() % info.maxSlide) & (-16384)); -#else - long slide = ((arc4random() % info.maxSlide) & (-4096)); -#endif - - // respect -disable_aslr boot-arg - if ( BootArgs::contains("-disable_aslr") ) + long slide; + if (info.maxSlide == 0) slide = 0; - - // update mappings - for (uint32_t i=0; i < 3; ++i) { - info.mappings[i].sfm_address += slide; + else + slide = ((arc4random() % info.maxSlide) & (-16384)); +#else + long slide; + if (info.maxSlide == 0) + slide = 0; + else + slide = ((arc4random() % info.maxSlide) & (-4096)); +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if (dyld::isTranslated()) { + slide &= (-16384); } +#endif +#endif return slide; } @@ -578,16 +748,68 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa if ( !preflightCacheFile(options, results, &info) ) return false; - const dyld_cache_slide_info2* slideInfo = nullptr; - if ( info.slideInfoSize != 0 ) { - results->slide = pickCacheASLR(info); - slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide); + int result = 0; + if ( info.mappingsCount != 3 ) { + uint32_t maxSlide = options.disableASLR ? 0 : (uint32_t)info.maxSlide; + + shared_file_np file; + file.sf_fd = info.fd; + file.sf_mappings_count = info.mappingsCount; + // For the new syscall, this is actually the max slide. The kernel now owns the actual slide + file.sf_slide = maxSlide; + result = __shared_region_map_and_slide_2_np(1, &file, info.mappingsCount, info.mappings); + } else { + // With the old syscall, dyld has to choose the slide + results->slide = options.disableASLR ? 0 : pickCacheASLRSlide(info); + + // update mappings based on the slide we choose + for (uint32_t i=0; i < info.mappingsCount; ++i) { + info.mappings[i].sms_address += results->slide; + if ( info.mappings[i].sms_slide_size != 0 ) + info.mappings[i].sms_slide_start += (uint32_t)results->slide; + } + + // If we get here then we don't have the new kernel function, so use the old one + const dyld_cache_slide_info2* slideInfo = nullptr; + size_t slideInfoSize = 0; + shared_file_mapping_np mappings[3]; + for (unsigned i = 0; i != 3; ++i) { + mappings[i].sfm_address = info.mappings[i].sms_address; + mappings[i].sfm_size = info.mappings[i].sms_size; + mappings[i].sfm_file_offset = info.mappings[i].sms_file_offset; + mappings[i].sfm_max_prot = info.mappings[i].sms_max_prot; + mappings[i].sfm_init_prot = info.mappings[i].sms_init_prot; + if ( info.mappings[i].sms_slide_size != 0 ) { + slideInfo = (dyld_cache_slide_info2*)info.mappings[i].sms_slide_start; + slideInfoSize = (size_t)info.mappings[i].sms_slide_size; + } + } + result = __shared_region_map_and_slide_np(info.fd, 3, mappings, results->slide, slideInfo, slideInfoSize); } - int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize); ::close(info.fd); if ( result == 0 ) { - results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address); + results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sms_address); + if ( info.mappingsCount != 3 ) { + // We don't know our own slide any more as the kernel owns it, so ask for it again now + if ( reuseExistingCache(options, results) ) { + + // update mappings based on the slide the kernel chose + for (uint32_t i=0; i < info.mappingsCount; ++i) { + info.mappings[i].sms_address += results->slide; + if ( info.mappings[i].sms_slide_size != 0 ) + info.mappings[i].sms_slide_start += (uint32_t)results->slide; + } + + if ( options.verbose ) + verboseSharedCacheMappingsToConsole(info.mappings, info.mappingsCount); + return true; + } + // Uh oh, we mapped the kernel, but we didn't find the slide + if ( options.verbose ) + dyld::logToConsole("dyld: error finding shared cache slide for system wide mapping\n"); + return false; + } } else { // could be another process beat us to it @@ -601,7 +823,7 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa if ( options.verbose ) { dyld::log("mapped dyld cache file system wide: %s\n", results->path); - verboseSharedCacheMappings(info.mappings); + verboseSharedCacheMappings(info.mappings, info.mappingsCount); } return true; } @@ -616,12 +838,18 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn // compute ALSR slide results->slide = 0; -#if !TARGET_OS_SIMULATOR // simulator caches do not support sliding - if ( info.slideInfoSize != 0 ) { - results->slide = pickCacheASLR(info); - } +#if !TARGET_OS_SIMULATOR + results->slide = options.disableASLR ? 0 : pickCacheASLRSlide(info); #endif - results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address); + + // update mappings + for (uint32_t i=0; i < info.mappingsCount; ++i) { + info.mappings[i].sms_address += (uint32_t)results->slide; + if ( info.mappings[i].sms_slide_size != 0 ) + info.mappings[i].sms_slide_start += (uint32_t)results->slide; + } + + results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sms_address); // deallocate any existing system wide shared cache deallocateExistingSharedCache(); @@ -634,18 +862,18 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn #endif // map cache just for this process with mmap() - for (int i=0; i < 3; ++i) { - void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sfm_address); - size_t size = (size_t)(info.mappings[i].sfm_size); + for (int i=0; i < info.mappingsCount; ++i) { + void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sms_address); + size_t size = (size_t)(info.mappings[i].sms_size); //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size); int protection = 0; - if ( info.mappings[i].sfm_init_prot & VM_PROT_EXECUTE ) + if ( info.mappings[i].sms_init_prot & VM_PROT_EXECUTE ) protection |= PROT_EXEC; - if ( info.mappings[i].sfm_init_prot & VM_PROT_READ ) + if ( info.mappings[i].sms_init_prot & VM_PROT_READ ) protection |= PROT_READ; - if ( info.mappings[i].sfm_init_prot & VM_PROT_WRITE ) + if ( info.mappings[i].sms_init_prot & VM_PROT_WRITE ) protection |= PROT_WRITE; - off_t offset = info.mappings[i].sfm_file_offset; + off_t offset = info.mappings[i].sms_file_offset; if ( ::mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, info.fd, offset) != mmapAddress ) { // failed to map some chunk of this shared cache file // clear shared region @@ -662,11 +890,22 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn #if TARGET_OS_SIMULATOR // simulator caches do not support sliding return true; #else - bool success = rebaseDataPages(options.verbose, info, results); + + // Change __DATA_CONST to read-write for this block + DyldSharedCache::DataConstScopedWriter patcher(results->loadAddress, mach_task_self(), options.verbose ? &dyld::log : nullptr); + + __block bool success = true; + for (int i=0; i < info.mappingsCount; ++i) { + if ( info.mappings[i].sms_slide_size == 0 ) + continue; + const dyld_cache_slide_info* slideInfoHeader = (const dyld_cache_slide_info*)info.mappings[i].sms_slide_start; + const uint8_t* mappingPagesStart = (const uint8_t*)info.mappings[i].sms_address; + success &= rebaseDataPages(options.verbose, slideInfoHeader, mappingPagesStart, info.sharedRegionStart, results); + } if ( options.verbose ) { dyld::log("mapped dyld cache file private to process (%s):\n", results->path); - verboseSharedCacheMappings(info.mappings); + verboseSharedCacheMappings(info.mappings, info.mappingsCount); } return success; #endif @@ -710,7 +949,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl if ( loadInfo.loadAddress->header.formatVersion != dyld3::closure::kFormatVersion ) { // support for older cache with a different Image* format -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE uint64_t hash = 0; for (const char* s=dylibPathToFind; *s != '\0'; ++s) hash += hash*4 + *s; @@ -718,7 +957,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl 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 +#if TARGET_OS_IPHONE // on iOS, inode is used to hold hash of path if ( (p->modTime == 0) && (p->inode != hash) ) continue; @@ -741,34 +980,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl if ( loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex) ) { results->image = images->imageForNum(imageIndex+1); } - #if __MAC_OS_X_VERSION_MIN_REQUIRED - else { - // 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 ( results->image == nullptr ) return false; @@ -793,7 +1005,7 @@ void deallocateExistingSharedCache() #if TARGET_OS_SIMULATOR // dyld deallocated macOS shared cache before jumping into dyld_sim #else - // remove the shared region sub-map + // remove the shared region sub-map uint64_t existingCacheAddress = 0; if ( __shared_region_check_np(&existingCacheAddress) == 0 ) { ::mmap((void*)((long)SHARED_REGION_BASE), SHARED_REGION_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE| MAP_ANON, 0, 0); diff --git a/dyld3/SharedCacheRuntime.h b/dyld3/SharedCacheRuntime.h index 7c06e6d..7d3a369 100644 --- a/dyld3/SharedCacheRuntime.h +++ b/dyld3/SharedCacheRuntime.h @@ -30,6 +30,7 @@ #include #include "DyldSharedCache.h" +#include "PointerAuth.h" namespace dyld3 { @@ -38,10 +39,12 @@ struct SharedCacheOptions { bool forcePrivate; bool useHaswell; bool verbose; + bool disableASLR; }; struct SharedCacheLoadInfo { - const DyldSharedCache* loadAddress; + typedef const DyldSharedCache* __ptrauth_dyld_address_auth DyldCachePtrType; + DyldCachePtrType loadAddress; long slide; const char* errorMessage; char path[256]; diff --git a/dyld3/SupportedArchs.h b/dyld3/SupportedArchs.h index 425c23f..ef3d5e1 100644 --- a/dyld3/SupportedArchs.h +++ b/dyld3/SupportedArchs.h @@ -25,6 +25,8 @@ #ifndef _DYLD_SUPPORTED_ARCHS_H_ #define _DYLD_SUPPORTED_ARCHS_H_ +#include + #endif // _DYLD_SUPPORTED_ARCHS_H_ diff --git a/dyld3/Tracing.cpp b/dyld3/Tracing.cpp index 7e1571d..233ef25 100644 --- a/dyld3/Tracing.cpp +++ b/dyld3/Tracing.cpp @@ -28,12 +28,9 @@ #include #include #include + #include "Loading.h" #include "Tracing.h" -#ifdef DARLING -#define kdebug_trace(...) -#define kdebug_is_enabled(...) false -#endif // Workaround for header issues in rdar://49073930 // #include @@ -52,7 +49,7 @@ void kdebug_trace_dyld_image(const uint32_t code, const fsid_t fsid, const mach_header* load_addr) { - uint64_t id = kdebug_trace_string(code, 0, imagePath); + uint64_t id = kdebug_trace_string(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code), 0, imagePath); #if __ARM_ARCH_7K__ uint32_t *uuid = (uint32_t *)uuid_bytes; kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 2), uuid[0], @@ -72,7 +69,7 @@ void kdebug_trace_dyld_image(const uint32_t code, ((uint64_t)fsobjid.fid_generation << 32), id, 0, 0); #endif /* !__ARM_ARCH_7K__ */ - kdebug_trace_string(code, id, nullptr); + kdebug_trace_string(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code), id, nullptr); } // FIXME diff --git a/dyld3/Tracing.h b/dyld3/Tracing.h index 8d86650..fab3fd1 100644 --- a/dyld3/Tracing.h +++ b/dyld3/Tracing.h @@ -35,10 +35,6 @@ #include #include -#ifdef DARLING -#define kdebug_trace_string(...) ((uint64_t)-1) -#endif - #define DBG_DYLD_INTERNAL_SUBCLASS (7) #define DBG_DYLD_API_SUBCLASS (8) @@ -69,7 +65,8 @@ #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)) - +#define DBG_DYLD_TASK_NOTIFY_REGISTER (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 4)) +#define DBG_DYLD_TASK_NOTIFY_DEREGISTER (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 5)) #define VIS_HIDDEN __attribute__((visibility("hidden"))) diff --git a/dyld3/dyld_app_cache_util.cpp b/dyld3/dyld_app_cache_util.cpp new file mode 100644 index 0000000..0fe8495 --- /dev/null +++ b/dyld3/dyld_app_cache_util.cpp @@ -0,0 +1,1801 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "kernel_collection_builder.h" +#include "ClosureFileSystemPhysical.h" +#include "FileUtils.h" +#include "JSONWriter.h" +#include "MachOAppCache.h" + +using namespace dyld3::json; + +using dyld3::closure::FileSystemPhysical; +using dyld3::closure::LoadedFileInfo; +using dyld3::GradedArchs; +using dyld3::MachOAnalyzer; +using dyld3::MachOAppCache; +using dyld3::Platform; +using dyld3::json::Node; + +__attribute__((noreturn)) +static void exit_usage(const char* missingOption = nullptr) { + if ( missingOption != nullptr ) { + fprintf(stderr, "Missing option '%s'\n", missingOption); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "Usage: dyld_app_cache_util [-layout] [-entrypoint] [-fixups] [-symbols] [-kmod] [-uuid] [-fips] -arch arch -platform platform -app-cache app-cache-path\n"); + fprintf(stderr, " -layout print the layout of an existing app cache\n"); + fprintf(stderr, " -entrypoint print the entrypoint of an existing app cache\n"); + fprintf(stderr, " -fixups print the fixups of an existing app cache\n"); + fprintf(stderr, " -symbols print the symbols of an existing app cache\n"); + fprintf(stderr, " -kmod print the kmod_info of an existing app cache\n"); + fprintf(stderr, " -uuid print the UUID of an existing app cache\n"); + fprintf(stderr, " -fips print the FIPS section of an existing app cache\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Usage: dyld_app_cache_util -validate file-path -arch arch -platform platform\n"); + fprintf(stderr, " -validate the path to check is valid for inserting in to an app cache\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Usage: dyld_app_cache_util -list-bundles directory-path\n"); + fprintf(stderr, " -list-bundles the directory to index for bundles\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Usage: dyld_app_cache_util -create-kernel-collection kernel-collection -kernel kernel-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n"); + fprintf(stderr, " -create-kernel-collection create a kernel collection and write to the given path\n"); + fprintf(stderr, " -kernel path to the kernel static executable\n"); + fprintf(stderr, " -extensions path to the kernel extensions directory\n"); + fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n"); + fprintf(stderr, " -sectcreate segment name, section name, and payload file path for more data to embed in the kernel\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Usage: dyld_app_cache_util -create-pageable-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n"); + fprintf(stderr, " -create-pageable-kernel-collection create a pageable kernel collection and write to the given path\n"); + fprintf(stderr, " -kernel-collection path to the kernel collection collection\n"); + fprintf(stderr, " -extensions path to the kernel extensions directory\n"); + fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Usage: dyld_app_cache_util -create-aux-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n"); + fprintf(stderr, " -create-aux-kernel-collection create an aux kernel collection and write to the given path\n"); + fprintf(stderr, " -kernel-collection path to the kernel collection\n"); + fprintf(stderr, " -pageable-collection path to the pageable collection\n"); + fprintf(stderr, " -extensions path to the kernel extensions directory\n"); + fprintf(stderr, " -bundle-id zero or more bundle-ids to link in to the kernel collection\n"); + fprintf(stderr, "\n"); + + fprintf(stderr, "Common options:\n"); + fprintf(stderr, " -arch xxx the arch to use to create the app cache\n"); + fprintf(stderr, " -platform xxx the platform to use to create the app cache\n"); + + exit(1); +} + +struct DumpOptions { + bool printLayout = false; + bool printEntryPoint = false; + bool printFixups = false; + bool printSymbols = false; + bool printUUID = false; + bool printKModInfo = false; + bool printFIPS = false; +}; + +struct ValidateOptions { + const char* filePath = nullptr; +}; + +struct ListBundlesOptions { + const char* directoryPath = nullptr; +}; + +// The payload of -sectcreate +struct SectionData { + const char* segmentName = nullptr; + const char* sectionName = nullptr; + const char* payloadFilePath = nullptr; +}; + +struct CreateKernelCollectionOptions { + const char* outputCachePath = nullptr; + const char* kernelPath = nullptr; + const char* kernelCollectionPath = nullptr; + const char* pageableCollectionPath = nullptr; + const char* extensionsPath = nullptr; + const char* volumeRoot = ""; + std::vector bundleIDs; + bool verbose = false; + bool printJSONErrors = false; + CollectionKind collectionKind = unknownKC; + StripMode stripMode = unknownStripMode; + std::vector sections; + const char* prelinkInfoExtraData = nullptr; +}; + +typedef std::variant OptionsVariants; + +struct CommonOptions { + const char* appCachePath = nullptr; + std::vector archs; + const char* platform = nullptr; +}; + +CommonOptions gOpts; + +template +static T& exitOrGetState(OptionsVariants& options, const char* argv) { + if (std::holds_alternative(options)) { + return options.emplace(); + } + if (std::holds_alternative(options)) + return std::get(options); + exit_usage(); +} + +static bool parseArgs(int argc, const char* argv[], OptionsVariants& options) { + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] != '-') { + fprintf(stderr, "unknown option: %s\n", arg); + exit_usage(); + } + + // Common options + if (strcmp(arg, "-app-cache") == 0) { + if (gOpts.appCachePath != nullptr) + exit_usage(); + gOpts.appCachePath = argv[++i]; + continue; + } + if (strcmp(arg, "-arch") == 0) { + gOpts.archs.push_back(argv[++i]); + continue; + } + if (strcmp(arg, "-platform") == 0) { + if (gOpts.platform != nullptr) + exit_usage(); + gOpts.platform = argv[++i]; + continue; + } + + // DumpOptions + if (strcmp(arg, "-layout") == 0) { + exitOrGetState(options, arg).printLayout = true; + continue; + } + if (strcmp(arg, "-entrypoint") == 0) { + exitOrGetState(options, arg).printEntryPoint = true; + continue; + } + if (strcmp(arg, "-fixups") == 0) { + exitOrGetState(options, arg).printFixups = true; + continue; + } + if (strcmp(arg, "-symbols") == 0) { + exitOrGetState(options, arg).printSymbols = true; + continue; + } + if (strcmp(arg, "-uuid") == 0) { + exitOrGetState(options, arg).printUUID = true; + continue; + } + if (strcmp(arg, "-kmod") == 0) { + exitOrGetState(options, arg).printKModInfo = true; + continue; + } + if (strcmp(arg, "-fips") == 0) { + exitOrGetState(options, arg).printFIPS = true; + continue; + } + + // ValidateOptions + if (strcmp(arg, "-validate") == 0) { + exitOrGetState(options, arg).filePath = argv[++i]; + continue; + } + + // ListBundlesOptions + if (strcmp(arg, "-list-bundles") == 0) { + exitOrGetState(options, arg).directoryPath = argv[++i]; + continue; + } + + // CreateKernelCollectionOptions + if (strcmp(arg, "-create-kernel-collection") == 0) { + exitOrGetState(options, arg).outputCachePath = argv[++i]; + exitOrGetState(options, arg).collectionKind = baseKC; + continue; + } + if (strcmp(arg, "-kernel") == 0) { + exitOrGetState(options, arg).kernelPath = argv[++i]; + continue; + } + if (strcmp(arg, "-create-pageable-kernel-collection") == 0) { + exitOrGetState(options, arg).outputCachePath = argv[++i]; + exitOrGetState(options, arg).collectionKind = pageableKC; + continue; + } + if (strcmp(arg, "-create-aux-kernel-collection") == 0) { + exitOrGetState(options, arg).outputCachePath = argv[++i]; + exitOrGetState(options, arg).collectionKind = auxKC; + continue; + } + if (strcmp(arg, "-kernel-collection") == 0) { + exitOrGetState(options, arg).kernelCollectionPath = argv[++i]; + continue; + } + if (strcmp(arg, "-pageable-collection") == 0) { + exitOrGetState(options, arg).pageableCollectionPath = argv[++i]; + continue; + } + if (strcmp(arg, "-extensions") == 0) { + exitOrGetState(options, arg).extensionsPath = argv[++i]; + continue; + } + if (strcmp(arg, "-volume-root") == 0) { + exitOrGetState(options, arg).volumeRoot = argv[++i]; + continue; + } + if (strcmp(arg, "-bundle-id") == 0) { + exitOrGetState(options, arg).bundleIDs.push_back(argv[++i]); + continue; + } + if (strcmp(arg, "-verbose") == 0) { + exitOrGetState(options, arg).verbose = true; + continue; + } + if (strcmp(arg, "-json-errors") == 0) { + exitOrGetState(options, arg).printJSONErrors = true; + continue; + } + if (strcmp(arg, "-strip-all") == 0) { + exitOrGetState(options, arg).stripMode = stripAll; + continue; + } + if (strcmp(arg, "-strip-all-kexts") == 0) { + exitOrGetState(options, arg).stripMode = stripAllKexts; + continue; + } + if (strcmp(arg, "-sectcreate") == 0) { + const char* segmentName = argv[++i]; + const char* sectionName = argv[++i]; + const char* payloadFilePath = argv[++i]; + SectionData sectData = { segmentName, sectionName, payloadFilePath }; + exitOrGetState(options, arg).sections.push_back(sectData); + continue; + } + if (strcmp(arg, "-prelink-info-extra") == 0) { + const char* payloadFilePath = argv[++i]; + exitOrGetState(options, arg).prelinkInfoExtraData = payloadFilePath; + continue; + } + + + fprintf(stderr, "unknown option: %s\n", arg); + exit_usage(); + } + + return true; +} + +static Platform stringToPlatform(const std::string& str) { + if (str == "unknown") + return Platform::unknown; + if (str == "macOS") + return Platform::macOS; + if (str == "iOS") + return Platform::iOS; + if (str == "tvOS") + return Platform::tvOS; + if (str == "watchOS") + return Platform::watchOS; + if (str == "bridgeOS") + return Platform::bridgeOS; + if (str == "iOSMac") + return Platform::iOSMac; + if (str == "UIKitForMac") + return Platform::iOSMac; + if (str == "iOS_simulator") + return Platform::iOS_simulator; + if (str == "tvOS_simulator") + return Platform::tvOS_simulator; + if (str == "watchOS_simulator") + return Platform::watchOS_simulator; + return Platform::unknown; +} + +static int dumpAppCache(const DumpOptions& options) { + // Verify any required options + if (gOpts.archs.size() != 1) + exit_usage("-arch"); + if (gOpts.platform == nullptr) + exit_usage("-platform"); + + if (gOpts.appCachePath == nullptr) + exit_usage(); + + FileSystemPhysical fileSystem; + if (!fileSystem.fileExists(gOpts.appCachePath)) { + fprintf(stderr, "App-cache path does not exist: %s\n", gOpts.appCachePath); + return 1; + } + + const GradedArchs& archs = GradedArchs::forName(gOpts.archs[0]); + Platform platform = Platform::unknown; + bool isKernelCollection = false; + + // HACK: Pass a real option for building a kernel app cache + if (!strcmp(gOpts.platform, "kernel")) { + isKernelCollection = true; + } else { + platform = stringToPlatform(gOpts.platform); + if (platform == Platform::unknown) { + fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform); + return 1; + } + } + + __block Diagnostics diag; + char appCacheRealPath[MAXPATHLEN]; + LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, gOpts.appCachePath, archs, platform, appCacheRealPath); + if (diag.hasError()) { + fprintf(stderr, "Could not load app cache because: %s\n", diag.errorMessage().c_str()); + return 1; + } + + MachOAppCache* appCacheMA = (MachOAppCache*)loadedFileInfo.fileContent; + if (appCacheMA == nullptr) { + fprintf(stderr, "Could not load app cache: %s\n", gOpts.appCachePath); + return 1; + } + + if (options.printLayout) { + __block Node topNode; + + // Add the segments for the app cache + __block Node segmentsNode; + __block bool hasError = false; + appCacheMA->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) { + Node segmentNode; + segmentNode.map["name"] = makeNode(info.segName); + segmentNode.map["vmAddr"] = makeNode(hex(info.vmAddr)); + segmentNode.map["vmSize"] = makeNode(hex(info.vmSize)); + segmentNode.map["vmEnd"] = makeNode(hex(info.vmAddr + info.vmSize)); + switch (info.protections) { + case VM_PROT_READ: + segmentNode.map["permissions"] = makeNode("r--"); + break; + case VM_PROT_WRITE: + segmentNode.map["permissions"] = makeNode("-w-"); + break; + case VM_PROT_EXECUTE: + segmentNode.map["permissions"] = makeNode("--x"); + break; + case VM_PROT_READ | VM_PROT_WRITE: + segmentNode.map["permissions"] = makeNode("rw-"); + break; + case VM_PROT_READ | VM_PROT_EXECUTE: + segmentNode.map["permissions"] = makeNode("r-x"); + break; + case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: + segmentNode.map["permissions"] = makeNode("rwx"); + break; + default: + fprintf(stderr, "Unknown permissions on segment '%s'\n", info.segName); + hasError = true; + stop = true; + } + + __block Node sectionsNode; + appCacheMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { + if ( strncmp(sectInfo.segInfo.segName, info.segName, 16) != 0 ) + return; + + Node sectionNode; + sectionNode.map["name"] = makeNode(sectInfo.sectName); + sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.sectAddr)); + sectionNode.map["vmSize"] = makeNode(hex(sectInfo.sectSize)); + sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.sectAddr + sectInfo.sectSize)); + + sectionsNode.array.push_back(sectionNode); + }); + + if ( !sectionsNode.array.empty() ) { + segmentNode.map["sections"] = sectionsNode; + } + + segmentsNode.array.push_back(segmentNode); + }); + + if (hasError) + return 1; + + topNode.map["cache-segments"] = segmentsNode; + + // Map from name to relative path + __block std::unordered_map relativePaths; + appCacheMA->forEachPrelinkInfoLibrary(diag, ^(const char *bundleName, const char* relativePath, + const std::vector &deps) { + if ( relativePath != nullptr ) + relativePaths[bundleName] = relativePath; + }); + + __block Node dylibsNode; + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + __block Node segmentsNode; + ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) { + Node segmentNode; + segmentNode.map["name"] = makeNode(info.segName); + segmentNode.map["vmAddr"] = makeNode(hex(info.vmAddr)); + segmentNode.map["vmSize"] = makeNode(hex(info.vmSize)); + segmentNode.map["vmEnd"] = makeNode(hex(info.vmAddr + info.vmSize)); + + switch (info.protections) { + case VM_PROT_READ: + segmentNode.map["permissions"] = makeNode("r--"); + break; + case VM_PROT_WRITE: + segmentNode.map["permissions"] = makeNode("-w-"); + break; + case VM_PROT_EXECUTE: + segmentNode.map["permissions"] = makeNode("--x"); + break; + case VM_PROT_READ | VM_PROT_WRITE: + segmentNode.map["permissions"] = makeNode("rw-"); + break; + case VM_PROT_READ | VM_PROT_EXECUTE: + segmentNode.map["permissions"] = makeNode("r-x"); + break; + default: + fprintf(stderr, "Unknown permissions on segment '%s'\n", info.segName); + hasError = true; + stop = true; + } + + __block Node sectionsNode; + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { + if ( strncmp(sectInfo.segInfo.segName, info.segName, 16) != 0 ) + return; + + Node sectionNode; + sectionNode.map["name"] = makeNode(sectInfo.sectName); + sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.sectAddr)); + sectionNode.map["vmSize"] = makeNode(hex(sectInfo.sectSize)); + sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.sectAddr + sectInfo.sectSize)); + + sectionsNode.array.push_back(sectionNode); + }); + + if ( !sectionsNode.array.empty() ) { + segmentNode.map["sections"] = sectionsNode; + } + + segmentsNode.array.push_back(segmentNode); + }); + + Node dylibNode; + dylibNode.map["name"] = makeNode(name); + dylibNode.map["segments"] = segmentsNode; + + auto relativePathIt = relativePaths.find(name); + if ( relativePathIt != relativePaths.end() ) + dylibNode.map["relativePath"] = makeNode(relativePathIt->second); + + dylibsNode.array.push_back(dylibNode); + }); + + topNode.map["dylibs"] = dylibsNode; + + printJSON(topNode, 0, std::cout); + } + + if (options.printEntryPoint) { + __block Node topNode; + + // add entry + uint64_t entryOffset; + bool usesCRT; + Node entryPointNode; + if ( appCacheMA->getEntry(entryOffset, usesCRT) ) { + entryPointNode.value = hex(appCacheMA->preferredLoadAddress() + entryOffset); + } + + topNode.map["entrypoint"] = entryPointNode; + + printJSON(topNode, 0, std::cout); + } + + if (options.printFixups) { + __block Node topNode; + + __block uint64_t baseAddress = ~0ULL; + appCacheMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + uint64_t cacheBaseAddress = baseAddress; + uint64_t textSegVMAddr = appCacheMA->preferredLoadAddress(); + + auto getFixupsNode = [cacheBaseAddress, textSegVMAddr](const dyld3::MachOAnalyzer* ma) { + __block Node fixupsNode; + + if (!ma->hasChainedFixups()) { + return makeNode("none"); + } + + // Keep track of the fixups seen by chained fixups. The remainder might be + // classic relocs if we are the x86_64 kernel collection + __block std::set seenFixupVMOffsets; + + __block Diagnostics diag; + ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { + ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + seenFixupVMOffsets.insert(vmOffset); + + // Correct for __DATA being before __TEXT, in which case the offset + // is from __DATA, not a mach header offset + vmOffset += (textSegVMAddr - cacheBaseAddress); + + fixupsNode.map[hex(vmOffset)] = makeNode("fixup"); + switch (segInfo->pointer_format) { + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: { + uint64_t targetVMOffset = fixupLoc->kernel64.target; + uint64_t targetVMAddr = targetVMOffset + cacheBaseAddress; + std::string level = "kc(" + decimal(fixupLoc->kernel64.cacheLevel) + ")"; + std::string fixup = level + " + " + hex(targetVMAddr); + if (fixupLoc->kernel64.isAuth) { + fixup += " auth("; + fixup += fixupLoc->kernel64.keyName(); + fixup += " "; + fixup += fixupLoc->kernel64.addrDiv ? "addr" : "!addr"; + fixup += " "; + fixup += decimal(fixupLoc->kernel64.diversity); + fixup += ")"; + } + fixupsNode.map[hex(vmOffset)] = makeNode(fixup); + break; + } + default: + diag.error("unknown pointer type %d", segInfo->pointer_format); + break; + } + }); + }); + diag.assertNoError(); + + ma->forEachRebase(diag, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo, + const dyld3::MachOAnalyzer::SegmentInfo *segments, + bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, + uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) { + uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset; + uint64_t runtimeOffset = rebaseVmAddr - textSegVMAddr; + const uint8_t* fixupLoc = (const uint8_t*)ma + runtimeOffset; + + // Correct for __DATA being before __TEXT, in which case the offset + // is from __DATA, not a mach header offset + runtimeOffset += (textSegVMAddr - cacheBaseAddress); + + std::string fixup = "kc(0) + "; + switch ( kind ) { + case dyld3::MachOAnalyzer::Rebase::unknown: + fixup += " : unhandled"; + break; + case dyld3::MachOAnalyzer::Rebase::pointer32: { + uint32_t value = *(uint32_t*)(fixupLoc); + fixup += hex(value) + " : pointer32"; + break; + } + case dyld3::MachOAnalyzer::Rebase::pointer64: { + uint64_t value = *(uint64_t*)(fixupLoc); + fixup += hex(value) + " : pointer64"; + break; + } + case dyld3::MachOAnalyzer::Rebase::textPCrel32: + fixup += " : pcrel32"; + break; + case dyld3::MachOAnalyzer::Rebase::textAbsolute32: + fixup += " : absolute32"; + break; + } + fixupsNode.map[hex(runtimeOffset)] = makeNode(fixup); + }); + diag.assertNoError(); + + return fixupsNode; + }; + + topNode.map["fixups"] = getFixupsNode(appCacheMA); + + __block Node dylibsNode; + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + Node dylibNode; + dylibNode.map["name"] = makeNode(name); + dylibNode.map["fixups"] = getFixupsNode(ma); + + dylibsNode.array.push_back(dylibNode); + }); + + topNode.map["dylibs"] = dylibsNode; + + printJSON(topNode, 0, std::cout); + } + + if (options.printSymbols) { + __block Node topNode; + + __block Node dylibsNode; + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + __block Node globalSymbolsNode; + ma->forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + Node symbolNode; + symbolNode.map["name"] = makeNode(symbolName); + symbolNode.map["vmAddr"] = makeNode(hex(n_value)); + + globalSymbolsNode.array.push_back(symbolNode); + }); + + __block Node localSymbolsNode; + ma->forEachLocalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + Node symbolNode; + symbolNode.map["name"] = makeNode(symbolName); + symbolNode.map["vmAddr"] = makeNode(hex(n_value)); + + localSymbolsNode.array.push_back(symbolNode); + }); + + if (globalSymbolsNode.array.empty()) + globalSymbolsNode = makeNode("none"); + if (localSymbolsNode.array.empty()) + localSymbolsNode = makeNode("none"); + + Node dylibNode; + dylibNode.map["name"] = makeNode(name); + dylibNode.map["global-symbols"] = globalSymbolsNode; + dylibNode.map["local-symbols"] = localSymbolsNode; + + dylibsNode.array.push_back(dylibNode); + }); + + topNode.map["dylibs"] = dylibsNode; + + printJSON(topNode, 0, std::cout); + } + + if (options.printUUID) { + __block Node topNode; + + // add uuid + Node uuidNode; + uuid_t uuid = {}; + if ( appCacheMA->getUuid(uuid) ) { + uuid_string_t uuidString; + uuid_unparse_upper(uuid, uuidString); + uuidNode.value = uuidString; + } + + topNode.map["uuid"] = uuidNode; + + auto getPlistUUID = ^(const char* jsonNodeName, CFStringRef keyName) { + uuid_t uuid = {}; + const uint8_t* prelinkInfoBuffer = nullptr; + uint64_t prelinkInfoBufferSize = 0; + prelinkInfoBuffer = (const uint8_t*)appCacheMA->findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize); + if ( prelinkInfoBuffer != nullptr ) { + CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull); + if ( !CFReadStreamOpen(readStreamRef) ) { + fprintf(stderr, "Could not open plist stream\n"); + exit(1); + } + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef); + if ( errorRef != nullptr ) { + CFStringRef stringRef = CFErrorCopyFailureReason(errorRef); + fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII)); + CFRelease(stringRef); + exit(1); + } + assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID()); + CFDataRef uuidDataRef = (CFDataRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, keyName); + if ( uuidDataRef != nullptr ) { + CFDataGetBytes(uuidDataRef, CFRangeMake(0, CFDataGetLength(uuidDataRef)), uuid); + + Node uuidNode; + uuid_string_t uuidString; + uuid_unparse_upper(uuid, uuidString); + uuidNode.value = uuidString; + topNode.map[jsonNodeName] = uuidNode; + } + CFRelease(plistRef); + CFRelease(readStreamRef); + } + }; + + getPlistUUID("prelink-info-uuid", CFSTR("_PrelinkKCID")); + + // If we are an auxKC, then we should also have a reference to the baseKC UUID + getPlistUUID("prelink-info-base-uuid", CFSTR("_BootKCID")); + + // If we are an pageableKC, then we should also have a reference to the pageableKC UUID + getPlistUUID("prelink-info-pageable-uuid", CFSTR("_PageableKCID")); + + printJSON(topNode, 0, std::cout); + } + + if (options.printKModInfo) { + __block Node topNode; + + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + Node dylibNode; + dylibNode.map["name"] = makeNode(name); + + // Check for a global first + __block uint64_t kmodInfoVMOffset = 0; + __block bool found = false; + { + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + found = ma->findExportedSymbol(diag, "_kmod_info", true, foundInfo, nullptr); + if ( found ) { + kmodInfoVMOffset = foundInfo.value; + } + } + // And fall back to a local if we need to + if ( !found ) { + ma->forEachLocalSymbol(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, "_kmod_info") == 0 ) { + kmodInfoVMOffset = n_value - ma->preferredLoadAddress(); + found = true; + stop = true; + } + }); + } + + if ( found ) { + dyld3::MachOAppCache::KModInfo64_v1* kmodInfo = (dyld3::MachOAppCache::KModInfo64_v1*)((uint8_t*)ma + kmodInfoVMOffset); + Node kmodInfoNode; + kmodInfoNode.map["info-version"] = makeNode(decimal(kmodInfo->info_version)); + kmodInfoNode.map["name"] = makeNode((const char*)&kmodInfo->name[0]); + kmodInfoNode.map["version"] = makeNode((const char*)&kmodInfo->version[0]); + kmodInfoNode.map["address"] = makeNode(hex(kmodInfo->address)); + kmodInfoNode.map["size"] = makeNode(hex(kmodInfo->size)); + + dylibNode.map["kmod_info"] = kmodInfoNode; + } else { + dylibNode.map["kmod_info"] = makeNode("none"); + } + + topNode.array.push_back(dylibNode); + }); + + printJSON(topNode, 0, std::cout); + } + + if (options.printFIPS) { + __block Node topNode; + + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + if ( strcmp(name, "com.apple.kec.corecrypto") != 0 ) + return; + + uint64_t hashStoreSize; + const void* hashStoreLocation = ma->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize); + assert(hashStoreLocation != nullptr); + + const uint8_t* hashBuffer = (const uint8_t*)hashStoreLocation; + std::string hashString; + + for (int i = 0; i < hashStoreSize; ++i) { + uint8_t byte = hashBuffer[i]; + uint8_t nibbleL = byte & 0x0F; + uint8_t nibbleH = byte >> 4; + if ( nibbleH < 10 ) { + hashString += '0' + nibbleH; + } else { + hashString += 'a' + (nibbleH-10); + } + if ( nibbleL < 10 ) { + hashString += '0' + nibbleL; + } else { + hashString += 'a' + (nibbleL-10); + } + } + + stop = true; + + topNode.map["fips"] = makeNode(hashString); + }); + + printJSON(topNode, 0, std::cout); + } + + return 0; +} + +static int validateFile(const ValidateOptions& options) { + // Verify any required options + if (gOpts.archs.size() != 1) + exit_usage("-arch"); + if (gOpts.platform == nullptr) + exit_usage("-platform"); + if (options.filePath == nullptr) + exit_usage(); + + const GradedArchs& archs = GradedArchs::forName(gOpts.archs[0]); + Platform platform = Platform::unknown; + + // HACK: Pass a real option for building a kernel app cache + if (strcmp(gOpts.platform, "kernel")) { + fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform); + return 1; + } + + __block Diagnostics diag; + + std::string file = options.filePath; + { + FileSystemPhysical fileSystem; + char fileRealPath[MAXPATHLEN]; + LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, file.c_str(), archs, platform, fileRealPath); + if (diag.hasError()) { + fprintf(stderr, "Could not load file '%s' because: %s\n", file.c_str(), diag.errorMessage().c_str()); + diag.clearError(); + return 1; + } + + MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent; + if (ma == nullptr) { + fprintf(stderr, "Could not load file: %s\n", file.c_str()); + return 1; + } + + auto errorHandler = ^(const char* msg) { + diag.warning("File '%s' cannot be placed in kernel collection because: %s", file.c_str(), msg); + }; + if (ma->canBePlacedInKernelCollection(file.c_str(), errorHandler)) { + return 0; + } else { + fileSystem.unloadFile(loadedFileInfo); + } + } + + { + // Since we found no files, print warnings for the ones we tried + if (diag.warnings().empty()) { + fprintf(stderr, "File '%s' was not valid for app-cache\n", file.c_str()); + } else { + for (const std::string& msg : diag.warnings()) { + fprintf(stderr, " %s\n", msg.c_str()); + } + } + return 1; + } + + return 0; +} + +static void forEachBundle(const char* bundlesDirectoryPath, + void (^callback)(CFBundleRef bundleRef, const char* bundleName)) { + CFStringRef sourcePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, bundlesDirectoryPath, + kCFStringEncodingASCII, kCFAllocatorNull); + CFURLRef sourceURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, sourcePath, + kCFURLPOSIXPathStyle, true); + CFArrayRef bundles = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault, sourceURL, nullptr); + + for (CFIndex i = 0, e = CFArrayGetCount(bundles); i != e; ++i) { + CFBundleRef bundleRef = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i); + CFStringRef bundleID = CFBundleGetIdentifier(bundleRef); + if (!bundleID) + continue; + const char* bundleName = CFStringGetCStringPtr(bundleID, kCFStringEncodingASCII); + callback(bundleRef, bundleName); + } + + CFRelease(sourcePath); + CFRelease(sourceURL); + CFRelease(bundles); +} + +static int listBundles(const ListBundlesOptions& options) { + // Verify any required options + if (options.directoryPath == nullptr) + exit_usage(); + + forEachBundle(options.directoryPath, ^(CFBundleRef bundleRef, const char* bundleName){ + printf("Bundle: %s\n", bundleName); + }); + + return 0; +} + +static CFDataRef +createKernelCollectionForArch(const CreateKernelCollectionOptions& options, const char* arch, + Diagnostics& diag) { + const GradedArchs& archs = GradedArchs::forName(arch); + Platform platform = Platform::unknown; + + + KernelCollectionBuilder* kcb = nullptr; + { + CFStringRef archStringRef = CFStringCreateWithCString(kCFAllocatorDefault, arch, kCFStringEncodingASCII); + BuildOptions_v1 buildOptions = { 1, options.collectionKind, options.stripMode, archStringRef, options.verbose }; + kcb = createKernelCollectionBuilder(&buildOptions); + CFRelease(archStringRef); + } + + FileSystemPhysical fileSystem; + LoadedFileInfo kernelCollectionLoadedFileInfo; + + auto loadKernelCollection = ^(const char* kernelCollectionPath, CollectionKind collectionKind) { + if (!fileSystem.fileExists(kernelCollectionPath)) { + fprintf(stderr, "kernel collection path does not exist: %s\n", options.kernelPath); + return false; + } + LoadedFileInfo info; + char realerPath[MAXPATHLEN]; + bool loadedFile = fileSystem.loadFile(kernelCollectionPath, info, realerPath, ^(const char *format, ...) { + va_list list; + va_start(list, format); + diag.error(format, list); + va_end(list); + }); + if ( !loadedFile ) + return false; + CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, kernelCollectionPath, kCFStringEncodingASCII); + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull); + if ( !addCollectionFile(kcb, pathStringRef, dataRef, collectionKind) ) { + diag.error("Could not load kernel collection file"); + return false; + } + CFRelease(dataRef); + CFRelease(pathStringRef); + return true; + }; + + switch (options.collectionKind) { + case unknownKC: + fprintf(stderr, "Invalid kernel collection kind\n"); + exit(1); + case baseKC: { + if (!fileSystem.fileExists(options.kernelPath)) { + fprintf(stderr, "Kernel path does not exist: %s\n", options.kernelPath); + return {}; + } + LoadedFileInfo info; + char realerPath[MAXPATHLEN]; + bool loadedFile = fileSystem.loadFile(options.kernelPath, info, realerPath, ^(const char *format, ...) { + va_list list; + va_start(list, format); + diag.error(format, list); + va_end(list); + }); + if ( !loadedFile ) + return {}; + CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, options.kernelPath, kCFStringEncodingASCII); + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull); + if ( !addKernelFile(kcb, pathStringRef, dataRef) ) { + uint64_t errorCount = 0; + const char* const* errors = getErrors(kcb, &errorCount); + for (uint64_t i = 0; i != errorCount; ++i) + diag.error("Could not load kernel file because: '%s'", errors[i]); + return {}; + } + CFRelease(dataRef); + CFRelease(pathStringRef); + break; + } + case pageableKC: + if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) ) + return {}; + break; + case auxKC: + if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) ) + return {}; + // Pageable is optional + if ( options.pageableCollectionPath != nullptr ) { + if ( !loadKernelCollection(options.pageableCollectionPath, pageableKC) ) + return {}; + } + break; + } + + if ( !options.bundleIDs.empty() ) { + struct BundleData { + std::string executablePath; + std::string bundlePath; + std::vector dependencies; + CFDictionaryRef infoPlist = nullptr; + }; + __block std::map foundBundles; + + // Look for bundles in the extensions directory and any PlugIns directories its kext's contain + __block std::list> kextDirectoriesToProcess; + kextDirectoriesToProcess.push_back({ options.extensionsPath, true }); + while ( !kextDirectoriesToProcess.empty() ) { + std::string kextDir = kextDirectoriesToProcess.front().first; + bool lookForPlugins = kextDirectoriesToProcess.front().second; + kextDirectoriesToProcess.pop_front(); + + __block bool foundError = false; + forEachBundle(kextDir.c_str(), ^(CFBundleRef bundleRef, const char* bundleName) { + + if (foundError) + return; + + // If the directory contains a PlugIns directory, then add it to the list to seach for kexts + if (lookForPlugins) { + CFURLRef pluginsRelativeURL = CFBundleCopyBuiltInPlugInsURL(bundleRef); + if ( pluginsRelativeURL != nullptr ) { + CFURLRef pluginsAbsoluteURL = CFURLCopyAbsoluteURL(pluginsRelativeURL); + CFStringRef pluginString = CFURLCopyFileSystemPath(pluginsAbsoluteURL, kCFURLPOSIXPathStyle); + const char* pluginPath = CFStringGetCStringPtr(pluginString, kCFStringEncodingASCII); + kextDirectoriesToProcess.push_back({ pluginPath, false }); + + CFRelease(pluginString); + CFRelease(pluginsAbsoluteURL); + CFRelease(pluginsRelativeURL); + } + } + +#if 0 + // For now always load bundles as we don't require every bundle to be listed on the command line + // but can instead bring them in on demand. + + // Once we've looked for plugins, if we don't want this bundle then we can skip validating it. + if ( foundBundles.count(bundleName) == 0 ) + return; +#endif + + BundleData bundleData; + bundleData.infoPlist = CFBundleGetInfoDictionary(bundleRef); + + CFURLRef bundleExecutableRelativeURL = CFBundleCopyExecutableURL(bundleRef); + + // Its ok to be missing an executable. We'll just skip this bundle + if ( bundleExecutableRelativeURL == nullptr ) { + // FIXME: Its possibly not ok to be missing the executable if its actually listed + // as a CFBundleExecutable path in the plist + foundBundles[bundleName] = bundleData; + return; + } + + CFURLRef bundleExecutableAbsoluteURL = CFURLCopyAbsoluteURL(bundleExecutableRelativeURL); + CFStringRef bundleExecutableString = CFURLCopyFileSystemPath(bundleExecutableAbsoluteURL, kCFURLPOSIXPathStyle); + const char* bundleExecutablePath = CFStringGetCStringPtr(bundleExecutableString, kCFStringEncodingASCII); + + // Check for an arch specific dependency list first + std::string archBundleLibraries = std::string("OSBundleLibraries") + "_" + arch; + CFStringRef archBundleLibrariesStringRef = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, archBundleLibraries.c_str(), + kCFStringEncodingASCII, kCFAllocatorNull); + CFTypeRef depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, archBundleLibrariesStringRef); + if ( depsRef == nullptr ) { + // No arch specific deps, so try the defaults + depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, CFSTR("OSBundleLibraries")); + } + if (depsRef != nullptr) { + if (CFGetTypeID(depsRef) != CFDictionaryGetTypeID()) { + fprintf(stderr, "Bad bundle '%s' (\"OSBundleLibraries\" is not a dictionary)\n", bundleName); + foundError = true; + return; + } + + CFDictionaryRef dictRef = (CFDictionaryRef)depsRef; + CFDictionaryApplyFunction(dictRef, [](const void *key, const void *value, void *context) { + BundleData* bundleData = (BundleData*)context; + CFStringRef keyRef = (CFStringRef)key; + //CFStringRef valueRef = (CFStringRef)value; + bundleData->dependencies.push_back(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII)); + }, &bundleData); + } + + // Make sure no-one tries to link the kernel directly. They must do so via symbol sets + if ( !bundleData.dependencies.empty() ) { + for (const std::string& dep : bundleData.dependencies) { + if (dep == "com.apple.kernel") { + fprintf(stderr, "Rejecting bundle '%s' as it is trying to link directly to the kernel\n", + bundleName); + foundError = true; + return; + } + } + } + + bundleData.executablePath = bundleExecutablePath; + + CFURLRef bundleURLRef = CFBundleCopyBundleURL(bundleRef); + CFStringRef bundleURLString = CFURLCopyFileSystemPath(bundleURLRef, kCFURLPOSIXPathStyle); + const char* bundleURLPath = CFStringGetCStringPtr(bundleURLString, kCFStringEncodingASCII); + if (strncmp(bundleURLPath, options.extensionsPath, strlen(options.extensionsPath)) != 0) { + fprintf(stderr, "Bundle path '%s' is not within extensions directory '%s'\n", + bundleURLPath, options.extensionsPath); + } + // Don't remove the whole extensions prefix, but instead the volume root, if we have one + bundleData.bundlePath = bundleURLPath + strlen(options.volumeRoot); + foundBundles[bundleName] = bundleData; + + CFRelease(bundleExecutableString); + CFRelease(bundleExecutableAbsoluteURL); + CFRelease(bundleExecutableRelativeURL); + }); + + if (foundError) + return {}; + } + + __block std::set existingBundles; + auto addSymbolSetsBundleIDs = ^(const dyld3::MachOAnalyzer* kernelMA) { + assert(kernelMA != nullptr); + + __block std::list nonASCIIStrings; + auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) { + const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8); + if ( symbolName != nullptr ) + return symbolName; + + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); + char buffer[len + 1]; + if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { + diags.error("Could not convert string to ASCII"); + return (const char*)nullptr; + } + buffer[len] = '\0'; + nonASCIIStrings.push_back(buffer); + return nonASCIIStrings.back().c_str(); + }; + + uint64_t symbolSetsSize = 0; + const void* symbolSetsContent = kernelMA->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize); + if ( symbolSetsContent != nullptr ) { + + // A helper to automatically call CFRelease when we go out of scope + struct AutoReleaseTypeRef { + AutoReleaseTypeRef() = default; + ~AutoReleaseTypeRef() { + if ( ref != nullptr ) { + CFRelease(ref); + } + } + void setRef(CFTypeRef typeRef) { + assert(ref == nullptr); + ref = typeRef; + } + + CFTypeRef ref = nullptr; + }; + + AutoReleaseTypeRef dataRefReleaser; + AutoReleaseTypeRef plistRefReleaser; + + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull); + if ( dataRef == nullptr ) { + diag.error("Could not create data ref for symbol sets"); + return false; + } + dataRefReleaser.setRef(dataRef); + + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + diag.error("Could not load plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(errorRef); + return false; + } + if ( plistRef == nullptr ) { + diag.error("Could not create plist ref for symbol sets"); + return false; + } + plistRefReleaser.setRef(plistRef); + + if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) { + diag.error("Symbol set plist should be a dictionary"); + return false; + } + CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef; + CFArrayRef symbolSetArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("SymbolsSets")); + if ( symbolSetArrayRef != nullptr ) { + if ( CFGetTypeID(symbolSetArrayRef) != CFArrayGetTypeID() ) { + diag.error("SymbolsSets value should be an array"); + return false; + } + for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolSetArrayRef); ++symbolSetIndex) { + CFDictionaryRef symbolSetDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolSetArrayRef, symbolSetIndex); + if ( CFGetTypeID(symbolSetDictRef) != CFDictionaryGetTypeID() ) { + diag.error("Symbol set element should be a dictionary"); + return false; + } + + // CFBundleIdentifier + CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("CFBundleIdentifier")); + if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) { + diag.error("Symbol set bundle ID should be a string"); + return false; + } + + const char* dylibID = getString(diag, bundleIDRef); + if ( dylibID == nullptr ) + return false; + existingBundles.insert(dylibID); + } + } + } + return true; + }; + + auto addExistingBundleIDs = ^(const char* path, bool isBaseKC) { + char fileRealPath[MAXPATHLEN]; + auto kernelCollectionLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath); + if ( diag.hasError() ) { + fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str()); + return false; + } + const MachOAppCache* appCacheMA = (const MachOAppCache*)kernelCollectionLoadedFileInfo.fileContent; + if (appCacheMA == nullptr) { + fprintf(stderr, "Could not load file: %s\n", path); + return false; + } + if ( !appCacheMA->isFileSet() ) { + fprintf(stderr, "kernel collection is not a cache file: %s\n", path); + return false; + } + __block const dyld3::MachOAnalyzer* kernelMA = nullptr; + appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) { + if ( ma->isStaticExecutable() ) { + kernelMA = ma; + } + existingBundles.insert(name); + }); + + if ( isBaseKC ) { + if ( !addSymbolSetsBundleIDs(kernelMA) ) + return false; + } + fileSystem.unloadFile(kernelCollectionLoadedFileInfo); + return true; + }; + if ( options.collectionKind == baseKC ) { + char fileRealPath[MAXPATHLEN]; + auto kernelLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, options.kernelPath, archs, platform, fileRealPath); + if ( diag.hasError() ) { + fprintf(stderr, "Could not load file '%s' because: %s\n", options.kernelPath, diag.errorMessage().c_str()); + return {}; + } + const MachOAppCache* kernelMA = (const MachOAppCache*)kernelLoadedFileInfo.fileContent; + if (kernelMA == nullptr) { + fprintf(stderr, "Could not load file: %s\n", options.kernelPath); + return {}; + } + if ( !kernelMA->isStaticExecutable() ) { + fprintf(stderr, "kernel is not a static executable: %s\n", options.kernelPath); + return {}; + } + + if ( !addSymbolSetsBundleIDs(kernelMA) ) + return {}; + fileSystem.unloadFile(kernelLoadedFileInfo); + } + if ( (options.collectionKind == auxKC) || (options.collectionKind == pageableKC) ) { + // Work out which bundle-ids are already in the base KC + if ( !addExistingBundleIDs(options.kernelCollectionPath, true) ) + return {}; + } + if ( options.pageableCollectionPath != nullptr ) { + // Work out which bundle-ids are already in the pageable KC + if ( !addExistingBundleIDs(options.pageableCollectionPath, false) ) + return {}; + } + + std::set processedBundleIDs; + std::list bundleIDsToLoad; + bundleIDsToLoad.insert(bundleIDsToLoad.end(), options.bundleIDs.begin(), options.bundleIDs.end()); + while (!bundleIDsToLoad.empty()) { + std::string bundleID = bundleIDsToLoad.front(); + bundleIDsToLoad.pop_front(); + + std::string stripModeString; + if ( const char* colonPos = strchr(bundleID.c_str(), ':') ) { + stripModeString = colonPos + 1; + bundleID.erase(colonPos - bundleID.data()); + } + + // If we've seen this one already then skip it + if (!processedBundleIDs.insert(bundleID).second) + continue; + + // Find the bundle for this ID + auto it = foundBundles.find(bundleID); + if (it == foundBundles.end()) { + fprintf(stderr, "[WARNING]: Could not find bundle with ID '%s'\n", bundleID.c_str()); + continue; + } + + BundleData& bundleData = it->second; + + LoadedFileInfo info; + + // Codeless kexts don't have an executable path, but we still want to put their + // plist in the prelink info + bool isCodeless = bundleData.executablePath.empty(); + if ( !isCodeless ) { + char realerPath[MAXPATHLEN]; + bool loadedFile = fileSystem.loadFile(bundleData.executablePath.c_str(), info, realerPath, + ^(const char *format, ...) { + va_list list; + va_start(list, format); + diag.error(format, list); + va_end(list); + }); + if ( !loadedFile ) + return {}; + } + + std::vector deps; + for (const std::string& dependency : bundleData.dependencies) + deps.push_back(dependency.c_str()); + + CFStringRef kextPathStringRef = nullptr; + CFDataRef kextDataRef = nullptr; + if ( !isCodeless) { + kextPathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.executablePath.c_str(), kCFStringEncodingASCII); + kextDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull); + } + + CFMutableArrayRef kextDepsArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, bundleData.dependencies.size(), &kCFTypeArrayCallBacks); + for (const std::string& dependency : bundleData.dependencies) { + CFStringRef depStringRef = CFStringCreateWithCString(kCFAllocatorDefault, dependency.c_str(), kCFStringEncodingASCII); + CFArrayAppendValue(kextDepsArrayRef, depStringRef); + CFRelease(depStringRef); + } + CFStringRef kextBundleIDStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleID.c_str(), kCFStringEncodingASCII); + CFStringRef kextBundlePathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.bundlePath.c_str(), kCFStringEncodingASCII); + + BinaryStripMode stripMode = binaryStripNone; + if ( !stripModeString.empty() ) { + if ( stripModeString == "locals" ) { + stripMode = binaryStripLocals; + } else if ( stripModeString == "exports" ) { + stripMode = binaryStripExports; + } else if ( stripModeString == "all" ) { + stripMode = binaryStripAll; + } else { + diag.error("Unknown strip mode: '%s'", stripModeString.c_str()); + return {}; + } + } + + KextFileData_v1 fileData = { 1, kextPathStringRef, kextDataRef, + kextDepsArrayRef, kextBundleIDStringRef, kextBundlePathStringRef, + bundleData.infoPlist, stripMode }; + + if ( !addKextDataFile(kcb, &fileData) ) { + uint64_t errorCount = 0; + const char* const* errors = getErrors(kcb, &errorCount); + for (uint64_t i = 0; i != errorCount; ++i) + diag.error("Could not load kext file because: '%s'", errors[i]); + return {}; + } + + // Walk the dependencies and add any new ones to the list + for (const std::string& dependency : bundleData.dependencies) { + if ( existingBundles.find(dependency) == existingBundles.end() ) + bundleIDsToLoad.push_back(dependency.c_str()); + } + } + + // Filter dependencies to kext's with binaries +#if 0 + const std::map* foundBundlesPtr = &foundBundles; + for (AppCacheBuilder::InputDylib& file : loadedFiles) { + file.dylibDeps.erase(std::remove_if(file.dylibDeps.begin(), file.dylibDeps.end(), + [&](const std::string& depName) { + auto it = foundBundlesPtr->find(depName); + assert(it != foundBundlesPtr->end()); + return it->second.executablePath.empty(); + }),file.dylibDeps.end()); + } +#endif + } + +#if 0 + for (AppCacheBuilder::InputDylib& file : loadedFiles) { + char fileRealPath[MAXPATHLEN]; + const char* path = file.dylib.loadedFileInfo.path; + LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath); + if ( diag.hasError() ) { + fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str()); + return {}; + } + + MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent; + if (ma == nullptr) { + fprintf(stderr, "Could not load file: %s\n", path); + return {}; + } + + auto errorHandler = ^(const char* msg) { + diag.error("Binary located at '%s' cannot be placed in kernel collection because: %s", path, msg); + }; + if (ma->canBePlacedInKernelCollection(path, errorHandler)) { + DyldSharedCache::MappedMachO mappedFile(path, ma, loadedFileInfo.sliceLen, false, false, + loadedFileInfo.sliceOffset, loadedFileInfo.mtime, + loadedFileInfo.inode); + CacheBuilder::LoadedMachO loadedMachO = { mappedFile, loadedFileInfo, nullptr }; + file.dylib = loadedMachO; + } else { + fileSystem.unloadFile(loadedFileInfo); + } + if ( diag.hasError() ) { + fprintf(stderr, "%s\n", diag.errorMessage().c_str()); + return {}; + } + } +#endif + +#if 0 + if (loadedFiles.empty()) { + fprintf(stderr, "Could not find any valid files to create kernel collection\n"); + + // Since we found no files, print warnings for the ones we tried + if (!diag.warnings().empty()) { + fprintf(stderr, "Failed to use the following files:\n"); + for (const std::string& msg : diag.warnings()) { + fprintf(stderr, " %s\n", msg.c_str()); + } + } + return {}; + } + + if (options.verbose) { + for (const AppCacheBuilder::InputDylib& loadedFile : loadedFiles) + fprintf(stderr, "Building cache with file: %s\n", loadedFile.dylib.loadedFileInfo.path); + } +#endif + + for (const SectionData& sectData : options.sections) { + CFStringRef segmentName = CFStringCreateWithCString(kCFAllocatorDefault, sectData.segmentName, kCFStringEncodingASCII); + CFStringRef sectionName = nullptr; + if ( sectData.sectionName != nullptr ) + sectionName = CFStringCreateWithCString(kCFAllocatorDefault, sectData.sectionName, kCFStringEncodingASCII); + + CFDataRef sectionData = nullptr; + { + struct stat stat_buf; + int fd = ::open(sectData.payloadFilePath, O_RDONLY, 0); + if (fd == -1) { + diag.error("can't open file '%s', errno=%d\n", sectData.payloadFilePath, errno); + return {}; + } + + if (fstat(fd, &stat_buf) == -1) { + diag.error("can't stat open file '%s', errno=%d\n", sectData.payloadFilePath, 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) { + diag.error("mmap() for file at %s failed, errno=%d\n", sectData.payloadFilePath, errno); + ::close(fd); + return {}; + } + ::close(fd); + + sectionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull); + } + + if ( !addSegmentData(kcb, segmentName, sectionName, sectionData) ) { + uint64_t errorCount = 0; + const char* const* errors = getErrors(kcb, &errorCount); + for (uint64_t i = 0; i != errorCount; ++i) + diag.error("Could not load section data file because: '%s'", errors[i]); + return {}; + } + } + + if ( options.prelinkInfoExtraData != nullptr ) { + struct stat stat_buf; + int fd = ::open(options.prelinkInfoExtraData, O_RDONLY, 0); + if (fd == -1) { + diag.error("can't open file '%s', errno=%d\n", options.prelinkInfoExtraData, errno); + return {}; + } + + if (fstat(fd, &stat_buf) == -1) { + diag.error("can't stat open file '%s', errno=%d\n", options.prelinkInfoExtraData, 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) { + diag.error("mmap() for file at %s failed, errno=%d\n", options.prelinkInfoExtraData, errno); + ::close(fd); + return {}; + } + ::close(fd); + + CFDataRef prelinkInfoData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull); + + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, prelinkInfoData, kCFPropertyListImmutable, nullptr, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + diag.error("Could not load prelink info plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(errorRef); + return {}; + } + if ( plistRef == nullptr ) { + diag.error("Could not create plist ref for prelink info"); + return {}; + } + if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) { + diag.error("Prelink info plist should be a dictionary"); + return {}; + } + + if ( !addPrelinkInfo(kcb, (CFDictionaryRef)plistRef) ) { + uint64_t errorCount = 0; + const char* const* errors = getErrors(kcb, &errorCount); + for (uint64_t i = 0; i != errorCount; ++i) + diag.error("Could not prelink data file because: '%s'", errors[i]); + return {}; + } + } + + bool success = runKernelCollectionBuilder(kcb); + uint64_t errorCount = 0; + const char* const* errors = getErrors(kcb, &errorCount); + if ( errors != nullptr ) { + if ( !options.printJSONErrors ) { + for (uint64_t i = 0; i != errorCount; ++i) { + fprintf(stderr, "Could not build kernel collection because '%s'\n", errors[i]); + } + } + CFDictionaryRef errorDictRef = getKextErrors(kcb); + if ( errorDictRef != nullptr ) { + Node rootNode; + + CFDictionaryApplyFunction(errorDictRef, [](const void *key, const void *value, void *context) { + Node* rootNode = (Node*)context; + CFStringRef keyRef = (CFStringRef)key; + CFArrayRef valueRef = (CFArrayRef)value; + + Node bundleNode; + bundleNode.map["id"] = Node(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII)); + + Node errorsNode; + CFArrayApplyFunction(valueRef, CFRangeMake(0, CFArrayGetCount(valueRef)), [](const void *value, void *context) { + Node* errorsNode = (Node*)context; + CFStringRef valueRef = (CFStringRef)value; + + errorsNode->array.push_back(Node(CFStringGetCStringPtr(valueRef, kCFStringEncodingASCII))); + }, &errorsNode); + + bundleNode.map["errors"] = errorsNode; + + rootNode->array.push_back(bundleNode); + }, &rootNode); + + // sort the nodes so that the output is reproducible + std::sort(rootNode.array.begin(), rootNode.array.end(), + [](const Node& a, const Node&b) { + return a.map.find("id")->second.value < b.map.find("id")->second.value; + }); + + printJSON(rootNode); + } else { + Node rootNode; + for (uint64_t i = 0; i != errorCount; ++i) { + rootNode.array.push_back(Node(errors[i])); + } + printJSON(rootNode); + } + return {}; + } + + if ( !success ) + return {}; + + uint64_t fileResultCount = 0; + const auto* fileResults = getCollectionFileResults(kcb, &fileResultCount); + if ( fileResults == nullptr ) { + fprintf(stderr, "Could not get file results\n"); + return {}; + } + if ( fileResultCount != 1 ) { + fprintf(stderr, "Unsupported file result count: %lld\n", fileResultCount); + return {}; + } + + CFDataRef dataRef = fileResults[0]->data; + CFRetain(dataRef); + + destroyKernelCollectionBuilder(kcb); + + return dataRef; +} + +static int createKernelCollection(const CreateKernelCollectionOptions& options) { + // Verify any required options + if (gOpts.archs.empty()) { + exit_usage("-arch"); + } else { + std::set archs(gOpts.archs.begin(), gOpts.archs.end()); + if (archs.size() != gOpts.archs.size()) { + fprintf(stderr, "Duplicate -arch specified\n"); + exit(1); + } + } + if (options.outputCachePath == nullptr) + exit_usage(); + + switch (options.stripMode) { + case unknownStripMode: + case stripNone: + break; + case stripAll: + case stripAllKexts: + if ( options.collectionKind != baseKC ) { + fprintf(stderr, "Cannot use -strip-all-kexts with auxKC. Use strip-all instead\n"); + exit(1); + } + break; + } + + switch (options.collectionKind) { + case unknownKC: + fprintf(stderr, "Invalid kernel collection kind\n"); + exit(1); + case baseKC: + if (options.kernelPath == nullptr) + exit_usage("-kernel"); + break; + case pageableKC: + case auxKC: + if (options.kernelCollectionPath == nullptr) + exit_usage("-kernel-collection"); + break; + } + + if ( !options.bundleIDs.empty() ) { + if (options.extensionsPath == nullptr) + exit_usage("-extensions"); + } + + // Volume root should be a prefix of extensions path + if ( options.extensionsPath != nullptr ) { + if ( strncmp(options.extensionsPath, options.volumeRoot, strlen(options.volumeRoot)) != 0 ) { + fprintf(stderr, "Volume root '%s' is not a prefix of extensions path '%s'\n", + options.volumeRoot, options.extensionsPath); + } + } + + std::vector buffers; + for (const char* arch : gOpts.archs) { + Diagnostics diag; + CFDataRef bufferRef = createKernelCollectionForArch(options, arch, diag); + if ( diag.hasError() ) { + fprintf(stderr, "%s\n", diag.errorMessage().c_str()); + return 1; + } + if ( bufferRef == nullptr ) { + // If we want errors then return 0 + if ( options.printJSONErrors ) + return 0; + return 1; + } + buffers.push_back(bufferRef); + } + + if (buffers.size() == 1) { + // Single arch. Just write the file directly + CFDataRef bufferRef = buffers.front(); + if ( !safeSave(CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef), options.outputCachePath) ) { + fprintf(stderr, "Could not write app cache\n"); + return 1; + } + CFRelease(bufferRef); + } else { + // Multiple buffers. Create a FAT file + std::vector fatBuffer; + + // Add the FAT header to the start of the buffer + fatBuffer.resize(0x4000, 0); + fat_header* header = (fat_header*)&fatBuffer.front(); + header->magic = OSSwapHostToBigInt32(FAT_MAGIC); + header->nfat_arch = OSSwapHostToBigInt32((uint32_t)buffers.size()); + + for (uint32_t i = 0; i != buffers.size(); ++i) { + CFDataRef bufferRef = buffers[i]; + mach_header* mh = (mach_header*)CFDataGetBytePtr(bufferRef); + + uint32_t offsetInBuffer = (uint32_t)fatBuffer.size(); + + fat_arch* archBuffer = (fat_arch*)(&fatBuffer.front() + sizeof(fat_header)); + archBuffer[i].cputype = OSSwapHostToBigInt32(mh->cputype); + archBuffer[i].cpusubtype = OSSwapHostToBigInt32(mh->cpusubtype); + archBuffer[i].offset = OSSwapHostToBigInt32(offsetInBuffer); + archBuffer[i].size = OSSwapHostToBigInt32((uint32_t)CFDataGetLength(bufferRef)); + archBuffer[i].align = OSSwapHostToBigInt32(14); + + auto align = [](uint64_t addr, uint8_t p2) { + uint64_t mask = (1 << p2); + return (addr + mask - 1) & (-mask); + }; + + uint32_t alignedSize = (uint32_t)align((uint32_t)CFDataGetLength(bufferRef), 14); + fatBuffer.resize(fatBuffer.size() + alignedSize, 0); + memcpy(&fatBuffer.front() + offsetInBuffer, CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef)); + } + + if ( !safeSave(&fatBuffer.front(), fatBuffer.size(), options.outputCachePath) ) { + fprintf(stderr, "Could not write app cache\n"); + return 1; + } + } + + return 0; +} + +int main(int argc, const char* argv[]) { + OptionsVariants options; + if (!parseArgs(argc, argv, options)) + return 1; + + if (std::holds_alternative(options)) { + return dumpAppCache(std::get(options)); + } + + if (std::holds_alternative(options)) { + return validateFile(std::get(options)); + } + + if (std::holds_alternative(options)) { + return listBundles(std::get(options)); + } + + if (std::holds_alternative(options)) { + return createKernelCollection(std::get(options)); + } + + assert(std::holds_alternative(options)); + + exit_usage(); +} diff --git a/dyld3/libdyldEntryVector.cpp b/dyld3/libdyldEntryVector.cpp index 8879971..99d9ad8 100644 --- a/dyld3/libdyldEntryVector.cpp +++ b/dyld3/libdyldEntryVector.cpp @@ -38,7 +38,8 @@ extern "C" char start; VIS_HIDDEN const char** appleParams; -extern bool gUseDyld3; +extern void* __ptrauth_dyld_address_auth gUseDyld3; +extern bool gEnableSharedCacheDataConst; namespace dyld3 { @@ -57,7 +58,8 @@ static const char* leafName(const char* argv0) return argv0; } -static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[], + bool keysOff, bool platformBinariesOnly, bool enableSharedCacheDataConst) { NXArgc = argc; NXArgv = argv; @@ -70,11 +72,13 @@ static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[ sVars.NXArgvPtr = &NXArgv; sVars.environPtr = (const char***)&environ; sVars.__prognamePtr = &__progname; - gAllImages.setProgramVars(&sVars); + gAllImages.setProgramVars(&sVars, keysOff, platformBinariesOnly); - gUseDyld3 = true; + gUseDyld3 = (void*)1; setLoggingFromEnvs(envp); + + gEnableSharedCacheDataConst = enableSharedCacheDataConst; } static void entry_setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)) ) @@ -107,11 +111,12 @@ static void entry_setNotifyMonitoringDyld(void (*notifyMonitoringDyld)(bool unlo } static void entry_setInitialImageList(const closure::LaunchClosure* closure, - const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath, - const Array& initialImages, LoadedImage& libSystem) + const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath, + const Array& initialImages, LoadedImage& libSystem, + mach_port_t mach_task_self) { gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages); - gAllImages.applyInterposingToDyldCache(closure); + gAllImages.applyInterposingToDyldCache(closure, mach_task_self); // run initializer for libSytem.B.dylib // this calls back into _dyld_initializer which calls gAllIimages.addImages() @@ -147,6 +152,24 @@ static void entry_setHasCacheOverrides(bool someCacheImageOverriden) gAllImages.setHasCacheOverrides(someCacheImageOverriden); } + +static void entry_setProgramVars(ProgramVars* progVars) +{ + // this entry only called when running crt1.o based old macOS programs + gAllImages.setProgramVars((AllImages::ProgramVars*)progVars, false, false); +} + +static void entry_setLaunchMode(uint32_t flags) +{ + gAllImages.setLaunchMode(flags); +} + +static MainFunc entry_getDriverkitMain(void) +{ + return gAllImages.getDriverkitMain(); +} + + static_assert((closure::kFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == closure::kFormatVersion, "binary format version overflow"); const LibDyldEntryVector entryVectorForDyld = { @@ -163,7 +186,10 @@ const LibDyldEntryVector entryVectorForDyld = { &entry_setRestrictions, &entry_setNotifyMonitoringDyldMain, &entry_setNotifyMonitoringDyld, - &entry_setHasCacheOverrides + &entry_setHasCacheOverrides, + &entry_setProgramVars, + &entry_setLaunchMode, + &entry_getDriverkitMain, }; VIS_HIDDEN void _dyld_atfork_prepare() diff --git a/dyld3/libdyldEntryVector.h b/dyld3/libdyldEntryVector.h index bcef2fc..3a07464 100644 --- a/dyld3/libdyldEntryVector.h +++ b/dyld3/libdyldEntryVector.h @@ -27,30 +27,35 @@ #define __DYLD_ENTRY_VECTOR_H__ #include +#include #include #include "Loading.h" struct dyld_all_image_infos; class DyldSharedCache; +struct ProgramVars; namespace dyld3 { +typedef void (*MainFunc)(void); struct LibDyldEntryVector { - enum { kCurrentVectorVersion = 7 }; + enum { kCurrentVectorVersion = 10 }; // The 32-bit caches steal bits to make rebase chains, so use 32-bits for the binary format version storage, but mask only some to actually use enum { kBinaryFormatVersionMask = 0x00FFFFFF }; uint32_t vectorVersion; // should be kCurrentVectorVersion 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 (*setVars)(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[], + bool keysOff, bool platformBinariesOnly, bool enableSharedCacheDataConst); void (*setHaltFunction)(void (*func)(const char* message) __attribute__((noreturn)) ); void (*setOldAllImageInfo)(dyld_all_image_infos*); void (*setInitialImageList)(const closure::LaunchClosure* closure, - const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath, - const Array& initialImages, LoadedImage& libSystem); + const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath, + const Array& initialImages, LoadedImage& libSystem, + mach_port_t mach_task_self); void (*runInitialzersBottomUp)(const mach_header* topImageLoadAddress); void (*startFunc)(); // added in version 3 @@ -66,6 +71,15 @@ struct LibDyldEntryVector const char* imagePaths[])); // added in version 7 void (*setHasCacheOverrides)(bool someCacheImageOverriden); + + // added in version 8 + void (*setProgramVars)(struct ProgramVars* progVars); + + // added in version 9 + void (*setLaunchMode)(uint32_t flags); + + // added in version 10 + MainFunc (*getDriverkitMain)(void); }; extern const LibDyldEntryVector entryVectorForDyld; diff --git a/dyld3/shared-cache/AdjustDylibSegments.cpp b/dyld3/shared-cache/AdjustDylibSegments.cpp index c01f2c9..fb33057 100644 --- a/dyld3/shared-cache/AdjustDylibSegments.cpp +++ b/dyld3/shared-cache/AdjustDylibSegments.cpp @@ -43,6 +43,7 @@ #include "MachOFileAbstraction.hpp" #include "MachOLoaded.h" #include "MachOAnalyzer.h" +#include "mach-o/fixup-chains.h" #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE @@ -54,100 +55,107 @@ namespace { template class Adjustor { public: - Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const std::vector& mappingInfo, Diagnostics& diag); + Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID, + const std::vector& mappingInfo, Diagnostics& diag); void adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker, - CacheBuilder::LOH_Tracker& lohTracker, - const CacheBuilder::CacheCoalescedText& coalescedText, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText, const CacheBuilder::DylibTextCoalescer& textCoalescer); private: - void adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker& lohTracker, - const CacheBuilder::CacheCoalescedText& coalescedText, + void adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText, const CacheBuilder::DylibTextCoalescer& textCoalescer); 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, + uint64_t imageStartAddress, uint64_t imageEndAddress, bool convertRebaseChains, CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress); void adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker); + void adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker); void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker); void adjustSymbolTable(); - void adjustChainedFixups(); + void adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer); + void adjustExternalRelocations(); void adjustExportsTrie(std::vector& newTrieBytes); void rebuildLinkEdit(); void adjustCode(); void adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta); void rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCoalescer& textCoalescer); uint64_t slideForOrigAddress(uint64_t addr); + void convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide); + void convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, + uint64_t targetSlide, bool convertRebaseChains); + typedef typename P::uint_t pint_t; typedef typename P::E E; - DyldSharedCache* _cacheBuffer; - macho_header

* _mh; + uint64_t _cacheBaseAddress = 0; + dyld3::MachOAnalyzer* _mh; Diagnostics& _diagnostics; const uint8_t* _linkeditBias = nullptr; unsigned _linkeditSegIndex = 0; bool _maskPointers = false; bool _splitSegInfoV2 = false; - const char* _installName = nullptr; - macho_symtab_command

* _symTabCmd = nullptr; - macho_dysymtab_command

* _dynSymTabCmd = nullptr; - macho_dyld_info_command

* _dyldInfo = nullptr; - macho_linkedit_data_command

* _splitSegInfoCmd = nullptr; - macho_linkedit_data_command

* _functionStartsCmd = nullptr; - macho_linkedit_data_command

* _dataInCodeCmd = nullptr; - macho_linkedit_data_command

* _exportTrieCmd = nullptr; - macho_linkedit_data_command

* _chainedFixupsCmd = nullptr; + const char* _dylibID = nullptr; + symtab_command* _symTabCmd = nullptr; + dysymtab_command* _dynSymTabCmd = nullptr; + dyld_info_command* _dyldInfo = nullptr; + linkedit_data_command* _splitSegInfoCmd = nullptr; + linkedit_data_command* _functionStartsCmd = nullptr; + linkedit_data_command* _dataInCodeCmd = nullptr; + linkedit_data_command* _exportTrieCmd = nullptr; + linkedit_data_command* _chainedFixupsCmd = nullptr; + uint16_t _chainedFixupsFormat = 0; std::vector _segOrigStartAddresses; + std::vector _segSizes; std::vector _segSlides; std::vector*> _segCmds; const std::vector& _mappingInfo; }; template -Adjustor

::Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const std::vector& mappingInfo, Diagnostics& diag) - : _cacheBuffer(cacheBuffer), _mh(mh), _diagnostics(diag), _mappingInfo(mappingInfo) +Adjustor

::Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID, + const std::vector& mappingInfo, Diagnostics& diag) + : _cacheBaseAddress(cacheBaseAddress), _mh(mh), _diagnostics(diag), _dylibID(dylibID), _mappingInfo(mappingInfo) { - assert((mh->magic() == MH_MAGIC) || (mh->magic() == MH_MAGIC_64)); - macho_segment_command

* segCmd; - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); - const uint32_t cmd_count = mh->ncmds(); - const macho_load_command

* cmd = cmds; - unsigned segIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_ID_DYLIB: - _installName = ((macho_dylib_command

*)cmd)->name(); - break; + assert((_mh->magic == MH_MAGIC) || (_mh->magic == MH_MAGIC_64)); + + __block unsigned segIndex = 0; + mh->forEachLoadCommand(diag, ^(const load_command *cmd, bool &stop) { + switch ( cmd->cmd ) { case LC_SYMTAB: - _symTabCmd = (macho_symtab_command

*)cmd; + _symTabCmd = (symtab_command*)cmd; break; case LC_DYSYMTAB: - _dynSymTabCmd = (macho_dysymtab_command

*)cmd; + _dynSymTabCmd = (dysymtab_command*)cmd; break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: - _dyldInfo = (macho_dyld_info_command

*)cmd; + _dyldInfo = (dyld_info_command*)cmd; break; case LC_SEGMENT_SPLIT_INFO: - _splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + _splitSegInfoCmd = (linkedit_data_command*)cmd; break; case LC_FUNCTION_STARTS: - _functionStartsCmd = (macho_linkedit_data_command

*)cmd; + _functionStartsCmd = (linkedit_data_command*)cmd; break; case LC_DATA_IN_CODE: - _dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + _dataInCodeCmd = (linkedit_data_command*)cmd; break; case LC_DYLD_CHAINED_FIXUPS: - _chainedFixupsCmd = (macho_linkedit_data_command

*)cmd; + _chainedFixupsCmd = (linkedit_data_command*)cmd; + _chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff]); break; case LC_DYLD_EXPORTS_TRIE: - _exportTrieCmd = (macho_linkedit_data_command

*)cmd; + _exportTrieCmd = (linkedit_data_command*)cmd; break; case macho_segment_command

::CMD: - segCmd = (macho_segment_command

*)cmd; + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; _segCmds.push_back(segCmd); _segOrigStartAddresses.push_back(segCmd->vmaddr()); + _segSizes.push_back(segCmd->vmsize()); _segSlides.push_back(_mappingInfo[segIndex].dstCacheUnslidAddress - segCmd->vmaddr()); if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { _linkeditBias = (uint8_t*)_mappingInfo[segIndex].dstSegment - segCmd->fileoff(); @@ -156,28 +164,49 @@ Adjustor

::Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const s ++segIndex; break; } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64) || (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64_32); + }); + + _maskPointers = (mh->cputype == CPU_TYPE_ARM64) || (mh->cputype == CPU_TYPE_ARM64_32); if ( _splitSegInfoCmd != NULL ) { - const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff]; _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT); } else { - _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _installName); + bool canHaveMissingSplitSeg = false; +#if BUILDING_APP_CACHE_UTIL + if ( mh->isKextBundle() ) { + if ( mh->isArch("x86_64") || mh->isArch("x86_64h") ) + canHaveMissingSplitSeg = true; + } +#endif + if ( !canHaveMissingSplitSeg ) + _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _dylibID); + } + + // Set the chained pointer format on old arm64e binaries using threaded rebase, and + // which don't have LC_DYLD_CHAINED_FIXUPS + if ( (_chainedFixupsCmd == nullptr) && mh->isArch("arm64e") ) { + _chainedFixupsFormat = DYLD_CHAINED_PTR_ARM64E; } } template void Adjustor

::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker, - CacheBuilder::LOH_Tracker& lohTracker, - const CacheBuilder::CacheCoalescedText& coalescedText, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText, const CacheBuilder::DylibTextCoalescer& textCoalescer) { if ( _diagnostics.hasError() ) return; if ( _splitSegInfoV2 ) { adjustReferencesUsingInfoV2(aslrTracker, lohTracker, coalescedText, textCoalescer); + adjustChainedFixups(textCoalescer); + } + else if ( _chainedFixupsCmd != nullptr ) { + // need to adjust the chain fixup segment_offset fields in LINKEDIT before chains can be walked + adjustChainedFixups(textCoalescer); + adjustRebaseChains(aslrTracker); + adjustCode(); } else { adjustDataPointers(aslrTracker); @@ -188,14 +217,15 @@ void Adjustor

::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& adjustSymbolTable(); if ( _diagnostics.hasError() ) return; - adjustChainedFixups(); + + adjustExternalRelocations(); if ( _diagnostics.hasError() ) return; rebuildLinkEditAndLoadCommands(textCoalescer); #if DEBUG Diagnostics diag; - ((dyld3::MachOAnalyzer*)_mh)->validateDyldCacheDylib(diag, _installName); + _mh->validateDyldCacheDylib(diag, _dylibID); if ( diag.hasError() ) { fprintf(stderr, "%s\n", diag.errorMessage().c_str()); } @@ -213,7 +243,7 @@ uint64_t Adjustor

::slideForOrigAddress(uint64_t addr) if ( _maskPointers && (addr & 0xF000000000000000) ) { return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF); } - _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _installName); + _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _dylibID); return 0; } @@ -226,58 +256,62 @@ void Adjustor

::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo // Remove: code signature, rebase info, code-sign-dirs, split seg info uint32_t chainedFixupsOffset = 0; - uint32_t chainedFixupsSize = _chainedFixupsCmd ? _chainedFixupsCmd->datasize() : 0; + uint32_t chainedFixupsSize = _chainedFixupsCmd ? _chainedFixupsCmd->datasize : 0; uint32_t bindOffset = chainedFixupsOffset + chainedFixupsSize; - uint32_t bindSize = _dyldInfo ? _dyldInfo->bind_size() : 0; + uint32_t bindSize = _dyldInfo ? _dyldInfo->bind_size : 0; uint32_t weakBindOffset = bindOffset + bindSize; - uint32_t weakBindSize = _dyldInfo ? _dyldInfo->weak_bind_size() : 0; + uint32_t weakBindSize = _dyldInfo ? _dyldInfo->weak_bind_size : 0; uint32_t lazyBindOffset = weakBindOffset + weakBindSize; - uint32_t lazyBindSize = _dyldInfo ? _dyldInfo->lazy_bind_size() : 0; + uint32_t lazyBindSize = _dyldInfo ? _dyldInfo->lazy_bind_size : 0; uint32_t exportOffset = lazyBindOffset + lazyBindSize; uint32_t exportSize = (uint32_t)newTrieBytes.size(); uint32_t splitSegInfoOffset = exportOffset + exportSize; - uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0); + uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize : 0); uint32_t funcStartsOffset = splitSegInfoOffset + splitSegInfosSize; - uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize() : 0); + uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize : 0); uint32_t dataInCodeOffset = funcStartsOffset + funcStartsSize; - uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize() : 0); + uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize : 0); uint32_t symbolTableOffset = dataInCodeOffset + dataInCodeSize; - uint32_t symbolTableSize = _symTabCmd->nsyms() * sizeof(macho_nlist

); + uint32_t symbolTableSize = _symTabCmd->nsyms * sizeof(macho_nlist

); uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize; - uint32_t indirectTableSize = _dynSymTabCmd->nindirectsyms() * sizeof(uint32_t); - uint32_t symbolStringsOffset = indirectTableOffset + indirectTableSize; - uint32_t symbolStringsSize = _symTabCmd->strsize(); + uint32_t indirectTableSize = _dynSymTabCmd ? (_dynSymTabCmd->nindirectsyms * sizeof(uint32_t)) : 0; + uint32_t externalRelocOffset = indirectTableOffset + indirectTableSize; + uint32_t externalRelocSize = _dynSymTabCmd ? (_dynSymTabCmd->nextrel * sizeof(relocation_info)) : 0; + uint32_t symbolStringsOffset = externalRelocOffset + externalRelocSize; + uint32_t symbolStringsSize = _symTabCmd->strsize; uint32_t newLinkEditSize = symbolStringsOffset + symbolStringsSize; size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12); if ( linkeditBufferSize < newLinkEditSize ) { - _diagnostics.error("LINKEDIT overflow in %s", _installName); + _diagnostics.error("LINKEDIT overflow in %s", _dylibID); return; } uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1); if ( chainedFixupsSize ) - memcpy(&newLinkeditBufer[chainedFixupsOffset], &_linkeditBias[_chainedFixupsCmd->dataoff()], chainedFixupsSize); + memcpy(&newLinkeditBufer[chainedFixupsOffset], &_linkeditBias[_chainedFixupsCmd->dataoff], chainedFixupsSize); if ( bindSize ) - memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off()], bindSize); + memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off], bindSize); if ( lazyBindSize ) - memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off()], lazyBindSize); + memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off], lazyBindSize); if ( weakBindSize ) - memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off()], weakBindSize); + memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off], weakBindSize); if ( exportSize ) memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize); if ( splitSegInfosSize ) - memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff()], splitSegInfosSize); + memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff], splitSegInfosSize); if ( funcStartsSize ) - memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff()], funcStartsSize); + memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff], funcStartsSize); if ( dataInCodeSize ) - memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff()], dataInCodeSize); + memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff], dataInCodeSize); if ( symbolTableSize ) - memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff()], symbolTableSize); + memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff], symbolTableSize); if ( indirectTableSize ) - memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff()], indirectTableSize); + memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff], indirectTableSize); + if ( externalRelocSize ) + memcpy(&newLinkeditBufer[externalRelocOffset], &_linkeditBias[_dynSymTabCmd->extreloff], externalRelocSize); if ( symbolStringsSize ) - memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize); + memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff], symbolStringsSize); memcpy(_mappingInfo[_linkeditSegIndex].dstSegment, newLinkeditBufer, newLinkEditSize); ::bzero(((uint8_t*)_mappingInfo[_linkeditSegIndex].dstSegment)+newLinkEditSize, linkeditBufferSize-newLinkEditSize); @@ -285,73 +319,71 @@ void Adjustor

::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheFileOffset; // updates load commands and removed ones no longer needed - macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)_mh + sizeof(macho_header

)); - uint32_t cmd_count = _mh->ncmds(); - const macho_load_command

* cmd = cmds; - const unsigned origLoadCommandsSize = _mh->sizeofcmds(); - unsigned bytesRemaining = origLoadCommandsSize; - unsigned removedCount = 0; - unsigned segIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - macho_symtab_command

* symTabCmd; - macho_dysymtab_command

* dynSymTabCmd; - macho_dyld_info_command

* dyldInfo; - macho_linkedit_data_command

* functionStartsCmd; - macho_linkedit_data_command

* dataInCodeCmd; - macho_linkedit_data_command

* chainedFixupsCmd; - macho_linkedit_data_command

* exportTrieCmd; - macho_linkedit_data_command

* splitSegInfoCmd; + + __block unsigned segIndex = 0; + _mh->forEachLoadCommand(_diagnostics, ^(const load_command *cmd, bool &stop) { + symtab_command* symTabCmd; + dysymtab_command* dynSymTabCmd; + dyld_info_command* dyldInfo; + linkedit_data_command* functionStartsCmd; + linkedit_data_command* dataInCodeCmd; + linkedit_data_command* chainedFixupsCmd; + linkedit_data_command* exportTrieCmd; + linkedit_data_command* splitSegInfoCmd; macho_segment_command

* segCmd; macho_routines_command

* routinesCmd; - macho_dylib_command

* dylibIDCmd; - uint32_t cmdSize = cmd->cmdsize(); + dylib_command* dylibIDCmd; int32_t segFileOffsetDelta; - bool remove = false; - switch ( cmd->cmd() ) { + switch ( cmd->cmd ) { case LC_ID_DYLIB: - dylibIDCmd = (macho_dylib_command

*)cmd; - dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB + dylibIDCmd = (dylib_command*)cmd; + dylibIDCmd->dylib.timestamp = 2; // match what static linker sets in LC_LOAD_DYLIB break; case LC_SYMTAB: - symTabCmd = (macho_symtab_command

*)cmd; - symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset); - symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset); - break; + symTabCmd = (symtab_command*)cmd; + symTabCmd->symoff = linkeditStartOffset + symbolTableOffset; + symTabCmd->stroff = linkeditStartOffset + symbolStringsOffset; + break; case LC_DYSYMTAB: - dynSymTabCmd = (macho_dysymtab_command

*)cmd; - dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset); + dynSymTabCmd = (dysymtab_command*)cmd; + dynSymTabCmd->indirectsymoff = linkeditStartOffset + indirectTableOffset; + // Clear local relocations (ie, old style rebases) as they were tracked earlier when we applied split seg + dynSymTabCmd->locreloff = 0; + dynSymTabCmd->nlocrel = 0 ; + // Update external relocations as we need these later to resolve binds from kexts + dynSymTabCmd->extreloff = linkeditStartOffset + externalRelocOffset; break; case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: - dyldInfo = (macho_dyld_info_command

*)cmd; - dyldInfo->set_rebase_off(0); - dyldInfo->set_rebase_size(0); - dyldInfo->set_bind_off(bindSize ? linkeditStartOffset+bindOffset : 0); - dyldInfo->set_bind_size(bindSize); - dyldInfo->set_weak_bind_off(weakBindSize ? linkeditStartOffset+weakBindOffset : 0); - dyldInfo->set_weak_bind_size(weakBindSize); - dyldInfo->set_lazy_bind_off(lazyBindSize ? linkeditStartOffset+lazyBindOffset : 0); - dyldInfo->set_lazy_bind_size(lazyBindSize); - dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0); - dyldInfo->set_export_size(exportSize); + dyldInfo = (dyld_info_command*)cmd; + dyldInfo->rebase_off = 0; + dyldInfo->rebase_size = 0; + dyldInfo->bind_off = bindSize ? linkeditStartOffset + bindOffset : 0; + dyldInfo->bind_size = bindSize; + dyldInfo->weak_bind_off = weakBindSize ? linkeditStartOffset + weakBindOffset : 0; + dyldInfo->weak_bind_size = weakBindSize; + dyldInfo->lazy_bind_off = lazyBindSize ? linkeditStartOffset + lazyBindOffset : 0; + dyldInfo->lazy_bind_size = lazyBindSize; + dyldInfo->export_off = exportSize ? linkeditStartOffset + exportOffset : 0; + dyldInfo->export_size = exportSize; break; case LC_FUNCTION_STARTS: - functionStartsCmd = (macho_linkedit_data_command

*)cmd; - functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset); - break; + functionStartsCmd = (linkedit_data_command*)cmd; + functionStartsCmd->dataoff = linkeditStartOffset + funcStartsOffset; + break; case LC_DATA_IN_CODE: - dataInCodeCmd = (macho_linkedit_data_command

*)cmd; - dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset); + dataInCodeCmd = (linkedit_data_command*)cmd; + dataInCodeCmd->dataoff = linkeditStartOffset + dataInCodeOffset; break; case LC_DYLD_CHAINED_FIXUPS: - chainedFixupsCmd = (macho_linkedit_data_command

*)cmd; - chainedFixupsCmd->set_dataoff(chainedFixupsSize ? linkeditStartOffset+chainedFixupsOffset : 0); - chainedFixupsCmd->set_datasize(chainedFixupsSize); + chainedFixupsCmd = (linkedit_data_command*)cmd; + chainedFixupsCmd->dataoff = chainedFixupsSize ? linkeditStartOffset + chainedFixupsOffset : 0; + chainedFixupsCmd->datasize = chainedFixupsSize; break; case LC_DYLD_EXPORTS_TRIE: - exportTrieCmd = (macho_linkedit_data_command

*)cmd; - exportTrieCmd->set_dataoff(exportSize ? linkeditStartOffset+exportOffset : 0); - exportTrieCmd->set_datasize(exportSize); + exportTrieCmd = (linkedit_data_command*)cmd; + exportTrieCmd->dataoff = exportSize ? linkeditStartOffset + exportOffset : 0; + exportTrieCmd->datasize = exportSize; break; case macho_routines_command

::CMD: routinesCmd = (macho_routines_command

*)cmd; @@ -369,9 +401,21 @@ void Adjustor

::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo if ( segCmd->nsects() > 0 ) { macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (strcmp(segCmd->segname(), "__TEXT") == 0) && textCoalescer.sectionWasCoalesced(sect->sectname())) { + bool coalescedSection = false; + if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) { + coalescedSection = true; + } +#if BUILDING_APP_CACHE_UTIL + if ( strcmp(segCmd->segname(), "__CTF") == 0 ) { + // The kernel __CTF segment data is completely removed when we link the baseKC + if ( _mh->isStaticExecutable() ) + coalescedSection = true; + } +#endif + + if ( coalescedSection ) { // Put coalesced sections at the end of the segment sect->set_addr(segCmd->vmaddr() + segCmd->filesize()); sect->set_offset(0); @@ -385,13 +429,20 @@ void Adjustor

::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo } ++segIndex; break; - case LC_RPATH: - _diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _installName); - remove = true; - break; case LC_SEGMENT_SPLIT_INFO: - splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; - splitSegInfoCmd->set_dataoff(linkeditStartOffset+splitSegInfoOffset); + splitSegInfoCmd = (linkedit_data_command*)cmd; + splitSegInfoCmd->dataoff = linkeditStartOffset + splitSegInfoOffset; + break; + default: + break; + } + }); + + _mh->removeLoadCommand(_diagnostics, ^(const load_command *cmd, bool &remove, bool &stop) { + switch ( cmd->cmd ) { + case LC_RPATH: + _diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _dylibID); + remove = true; break; case LC_CODE_SIGNATURE: case LC_DYLIB_CODE_SIGN_DRS: @@ -400,40 +451,29 @@ void Adjustor

::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo default: break; } - macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); - if ( remove ) { - ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); - ++removedCount; - } - else { - bytesRemaining -= cmdSize; - cmd = nextCmd; - } - } - // zero out stuff removed - ::bzero((void*)cmd, bytesRemaining); - // update header - _mh->set_ncmds(cmd_count-removedCount); - _mh->set_sizeofcmds(origLoadCommandsSize-bytesRemaining); - _mh->set_flags(_mh->flags() | 0x80000000); + }); + _mh->flags |= 0x80000000; } template void Adjustor

::adjustSymbolTable() { - macho_nlist

* symbolTable = (macho_nlist

*)&_linkeditBias[_symTabCmd->symoff()]; + if ( _dynSymTabCmd == nullptr ) + return; + + macho_nlist

* symbolTable = (macho_nlist

*)&_linkeditBias[_symTabCmd->symoff]; // adjust global symbol table entries - macho_nlist

* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()]; - for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->iextdefsym()]; entry < lastExport; ++entry) { + macho_nlist

* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym + _dynSymTabCmd->nextdefsym]; + for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->iextdefsym]; entry < lastExport; ++entry) { if ( (entry->n_type() & N_TYPE) == N_SECT ) entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); } // adjust local symbol table entries - macho_nlist

* lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()]; - for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->ilocalsym()]; entry < lastLocal; ++entry) { + macho_nlist

* lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym+_dynSymTabCmd->nlocalsym]; + for (macho_nlist

* entry = &symbolTable[_dynSymTabCmd->ilocalsym]; entry < lastLocal; ++entry) { if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); } @@ -441,27 +481,77 @@ void Adjustor

::adjustSymbolTable() template -void Adjustor

::adjustChainedFixups() +void Adjustor

::adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer) { if ( _chainedFixupsCmd == nullptr ) return; // Pass a start hint in to withChainStarts which takes account of the LINKEDIT shifting but we haven't // yet updated that LC_SEGMENT to point to the new data - const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff()]; + const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff]; uint64_t startsOffset = ((uint64_t)header + header->starts_offset) - (uint64_t)_mh; // segment_offset in dyld_chained_starts_in_segment is wrong. We need to move it to the new segment offset - ((dyld3::MachOAnalyzer*)_mh)->withChainStarts(_diagnostics, startsOffset, ^(const dyld_chained_starts_in_image* starts) { + _mh->withChainStarts(_diagnostics, startsOffset, ^(const dyld_chained_starts_in_image* starts) { for (uint32_t segIndex=0; segIndex < starts->seg_count; ++segIndex) { if ( starts->seg_info_offset[segIndex] == 0 ) continue; dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]); segInfo->segment_offset = (uint64_t)_mappingInfo[segIndex].dstSegment - (uint64_t)_mh; + + // If the whole segment was coalesced the remove its chain starts + if ( textCoalescer.segmentWasCoalesced(_segCmds[segIndex]->segname()) ) { + segInfo->page_count = 0; + } } }); } +template +static uint64_t externalRelocBaseAddress(const dyld3::MachOAnalyzer* ma, + std::vector*> segCmds, + std::vector segOrigStartAddresses) +{ + if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) { +#if BUILDING_APP_CACHE_UTIL + if ( ma->isKextBundle() ) { + // for kext bundles the reloc base address starts at __TEXT segment + return segOrigStartAddresses[0]; + } +#endif + // for x86_64 reloc base address starts at first writable segment (usually __DATA) + for (uint32_t i=0; i < segCmds.size(); ++i) { + if ( segCmds[i]->initprot() & VM_PROT_WRITE ) + return segOrigStartAddresses[i]; + } + } + // For everyone else we start at 0 + return 0; +} + + +template +void Adjustor

::adjustExternalRelocations() +{ + if ( _dynSymTabCmd == nullptr ) + return; + + // section index 0 refers to mach_header + uint64_t baseAddress = _mappingInfo[0].dstCacheUnslidAddress; + + const uint64_t relocsStartAddress = externalRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses); + relocation_info* relocsStart = (relocation_info*)&_linkeditBias[_dynSymTabCmd->extreloff]; + relocation_info* relocsEnd = &relocsStart[_dynSymTabCmd->nextrel]; + for (relocation_info* reloc = relocsStart; reloc < relocsEnd; ++reloc) { + // External relocations should be relative to the base address of the mach-o as otherwise they + // probably won't fit in 32-bits. + uint64_t newAddress = reloc->r_address + slideForOrigAddress(relocsStartAddress + reloc->r_address); + newAddress -= baseAddress; + reloc->r_address = (int32_t)newAddress; + assert((uint64_t)reloc->r_address == newAddress); + } +} + template void Adjustor

::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker) { @@ -484,7 +574,7 @@ void Adjustor

::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, C case REBASE_TYPE_TEXT_PCREL32: // general text relocs not support default: - _diagnostics.error("unknown rebase type 0x%02X in %s", type, _installName); + _diagnostics.error("unknown rebase type 0x%02X in %s", type, _dylibID); } } @@ -539,9 +629,187 @@ static uint32_t setArmWord(uint32_t instruction, uint16_t word) { return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; } + +template +void Adjustor

::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, + uint64_t targetSlide, bool convertRebaseChains) +{ + assert(chainPtr->arm64e.authRebase.bind == 0); + assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E) + || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND) + || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24) + || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL) ); + dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr; + dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp; + if ( chainPtr->arm64e.authRebase.auth ) { + uint64_t targetVMAddr = orgPtr.arm64e.authRebase.target + _segOrigStartAddresses[0] + targetSlide; + + // The merging code may have set the high bits, eg, to a tagged pointer + // Note authRebase has no high8, so this is invalid if it occurs + uint8_t high8 = targetVMAddr >> 56; + if ( high8 ) { + // The kernel uses the high bits in the vmAddr, so don't error there + bool badPointer = true; + if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL ) { + uint64_t vmOffset = targetVMAddr - _cacheBaseAddress; + if ( (vmOffset >> 56) == 0 ) + badPointer = false; + } + + if ( badPointer ) { + _diagnostics.error("Cannot set tag on pointer in '%s' as high bits are incompatible with pointer authentication", _dylibID); + return; + } + } + + if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND ) { + // the rebase target is a vmoffset, so we need to switch to tracking the target out of line + aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key); + aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr); + chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table + chainPtr->arm64e.rebase.high8 = 0; + chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next; + chainPtr->arm64e.rebase.bind = 0; + chainPtr->arm64e.rebase.auth = 0; + return; + } + + if ( convertRebaseChains ) { + // This chain has been broken by merging CF constants. + // Instead of trying to maintain the chain, just set the raw value now + aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key); + chainPtr->raw64 = targetVMAddr; + return; + } + + // we need to change the rebase to point to the new address in the dyld cache, but it may not fit + tmp.arm64e.authRebase.target = targetVMAddr; + if ( tmp.arm64e.authRebase.target == targetVMAddr ) { + // everything fits, just update target + chainPtr->arm64e.authRebase.target = targetVMAddr; + return; + } + // see if it fits in a plain rebase + tmp.arm64e.rebase.target = targetVMAddr; + if ( tmp.arm64e.rebase.target == targetVMAddr ) { + // does fit in plain rebase, so convert to that and store auth data in side table + aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key); + chainPtr->arm64e.rebase.target = targetVMAddr; + chainPtr->arm64e.rebase.high8 = 0; + chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next; + chainPtr->arm64e.rebase.bind = 0; + chainPtr->arm64e.rebase.auth = 0; + return; + } + // target cannot fit into rebase chain, so store target in side table + aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key); + aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr); + chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table + chainPtr->arm64e.rebase.high8 = 0; + chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next; + chainPtr->arm64e.rebase.bind = 0; + chainPtr->arm64e.rebase.auth = 0; + return; + } + else { + uint64_t targetVMAddr = 0; + switch (_chainedFixupsFormat) { + case DYLD_CHAINED_PTR_ARM64E: + targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide; + break; + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + // the rebase target is a vmoffset, so we need to switch to tracking the target out of line + aslrTracker.setRebaseTarget64(chainPtr, orgPtr.arm64e.rebase.target + targetSlide); + orgPtr.arm64e.rebase.target = 0; + targetVMAddr = 0; + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + targetVMAddr = orgPtr.arm64e.rebase.target + _segOrigStartAddresses[0] + targetSlide; + break; + default: + _diagnostics.error("Unknown chain format"); + return; + } + + // The merging code may have set the high bits, eg, to a tagged pointer + uint8_t high8 = targetVMAddr >> 56; + if ( chainPtr->arm64e.rebase.high8 ) { + if ( high8 ) { + _diagnostics.error("Cannot set tag on pointer as high bits are in use"); + return; + } + aslrTracker.setHigh8(chainPtr, chainPtr->arm64e.rebase.high8); + } else { + if ( high8 ) { + aslrTracker.setHigh8(chainPtr, high8); + targetVMAddr &= 0x00FFFFFFFFFFFFFF; + } + } + + if ( convertRebaseChains ) { + // This chain has been broken by merging CF constants. + // Instead of trying to maintain the chain, just set the raw value now + chainPtr->raw64 = targetVMAddr; + return; + } + + tmp.arm64e.rebase.target = targetVMAddr; + if ( tmp.arm64e.rebase.target == targetVMAddr ) { + // target dyld cache address fits in plain rebase, so all we need to do is adjust that + chainPtr->arm64e.rebase.target = targetVMAddr; + return; + } + + // target cannot fit into rebase chain, so store target in side table + aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr); + chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table + } +} + + +template +void Adjustor

::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide) +{ + assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_64) || (_chainedFixupsFormat == DYLD_CHAINED_PTR_64_OFFSET) ); + dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr; + dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp; + + uint64_t targetVMAddr = 0; + switch (_chainedFixupsFormat) { + case DYLD_CHAINED_PTR_64: + targetVMAddr = orgPtr.generic64.rebase.target + targetSlide; + break; + case DYLD_CHAINED_PTR_64_OFFSET: + // the rebase target is a vmoffset, so we need to switch to tracking the target out of line + targetVMAddr = orgPtr.generic64.rebase.target + _segOrigStartAddresses[0] + targetSlide; + aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr); + chainPtr->generic64.rebase.target = 0; + return; + break; + default: + _diagnostics.error("Unknown chain format"); + return; + } + + // we need to change the rebase to point to the new address in the dyld cache, but it may not fit + tmp.generic64.rebase.target = targetVMAddr; + if ( tmp.generic64.rebase.target == targetVMAddr ) { + // everything fits, just update target + chainPtr->generic64.rebase.target = targetVMAddr; + return; + } + + // target cannot fit into rebase chain, so store target in side table + aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr); + chainPtr->generic64.rebase.target = 0; // actual target vmAddr stored in side table +} + + template void Adjustor

::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, + bool convertRebaseChains, CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress) { @@ -550,8 +818,8 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f uint32_t value32; uint32_t* mappedAddr32 = 0; uint32_t instruction; - dyld3::MachOLoaded::ChainedFixupPointerOnDisk chainPtr; - int64_t offsetAdjust; + dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr; + uint32_t newPageOffset; int64_t delta; switch ( kind ) { case DYLD_CACHE_ADJ_V2_DELTA_32: @@ -560,44 +828,113 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f delta = (int32_t)value32; delta += adjust; if ( (delta > 0x80000000) || (-delta > 0x80000000) ) { - _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName); + _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _dylibID); return; } P::E::set32(*mappedAddr32, (int32_t)delta); break; case DYLD_CACHE_ADJ_V2_POINTER_32: mappedAddr32 = (uint32_t*)mappedAddr; - 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; + if ( _chainedFixupsCmd != nullptr ) { + chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr32; + switch (_chainedFixupsFormat) { + case DYLD_CHAINED_PTR_32: + // ignore binds, fix up rebases to have new targets + if ( chainPtr->generic32.rebase.bind == 0 ) { + // there is not enough space in 32-bit pointer to store new vmaddr in cache in 26-bit target + // so store target in side table that will be applied when binds are resolved + aslrTracker.add(mappedAddr32); + uint32_t target = (uint32_t)(chainPtr->generic32.rebase.target + targetSlide); + aslrTracker.setRebaseTarget32(chainPtr, target); + chainPtr->generic32.rebase.target = 0; // actual target stored in side table + } + break; + default: + _diagnostics.error("unknown 32-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID); + break; + } + } + else if ( _mh->usesClassicRelocationsInKernelCollection() ) { + // Classic relocs are not guaranteed to be aligned, so always store them in the side table + if ( (uint32_t)toNewAddress != (uint32_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, _dylibID); + return; + } + aslrTracker.setRebaseTarget32(mappedAddr32, (uint32_t)toNewAddress); + E::set32(*mappedAddr32, 0); + aslrTracker.add(mappedAddr32); + } + else { + 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, _dylibID); + return; + } + E::set32(*mappedAddr32, (uint32_t)toNewAddress); + aslrTracker.add(mappedAddr32); } - E::set32(*mappedAddr32, (uint32_t)toNewAddress); - aslrTracker.add(mappedAddr32); break; case DYLD_CACHE_ADJ_V2_POINTER_64: mappedAddr64 = (uint64_t*)mappedAddr; - if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) { - _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName); - return; + if ( _chainedFixupsCmd != nullptr ) { + chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr64; + switch (_chainedFixupsFormat) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + // ignore binds and adjust rebases to new segment locations + if ( chainPtr->arm64e.authRebase.bind == 0 ) { + convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains); + // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location + aslrTracker.add(chainPtr); + } + break; + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + // ignore binds and adjust rebases to new segment locations + if ( chainPtr->generic64.rebase.bind == 0 ) { + convertGeneric64RebaseToIntermediate(chainPtr, aslrTracker, targetSlide); + // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location + aslrTracker.add(chainPtr); + } + break; + default: + _diagnostics.error("unknown 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID); + break; + } + } + else if ( _mh->usesClassicRelocationsInKernelCollection() ) { + if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) { + _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID); + return; + } + aslrTracker.setRebaseTarget64(mappedAddr64, toNewAddress); + E::set64(*mappedAddr64, 0); // actual target vmAddr stored in side table + aslrTracker.add(mappedAddr64); + uint8_t high8 = toNewAddress >> 56; + if ( high8 ) + aslrTracker.setHigh8(mappedAddr64, high8); + } + else { + if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) { + _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID); + return; + } + E::set64(*mappedAddr64, toNewAddress); + aslrTracker.add(mappedAddr64); + uint8_t high8 = toNewAddress >> 56; + if ( high8 ) + aslrTracker.setHigh8(mappedAddr64, high8); } - E::set64(*mappedAddr64, toNewAddress); - aslrTracker.add(mappedAddr64); break; case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64: - mappedAddr64 = (uint64_t*)mappedAddr; - chainPtr.raw64 = E::get64(*mappedAddr64); - // ignore binds, fix up rebases to have new targets - if ( chainPtr.arm64e.authRebase.bind == 0 ) { - if ( chainPtr.arm64e.authRebase.auth ) { - // auth pointer target is offset in dyld cache - chainPtr.arm64e.authRebase.target += (((dyld3::MachOAnalyzer*)_mh)->preferredLoadAddress() + targetSlide - _cacheBuffer->header.sharedRegionStart); - } - else { - // plain pointer target is unslid address of target - chainPtr.arm64e.rebase.target += targetSlide; - } + // old style arm64e binary + chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr; + // ignore binds, they are proccessed later + if ( chainPtr->arm64e.authRebase.bind == 0 ) { + convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains); // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location - E::set64(*mappedAddr64, chainPtr.raw64); + aslrTracker.add(chainPtr); } break; case DYLD_CACHE_ADJ_V2_DELTA_64: @@ -612,7 +949,7 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f value32 = P::E::get32(*mappedAddr32); value64 = toNewAddress - imageStartAddress; if ( value64 > imageEndAddress ) { - _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName); + _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _dylibID); return; } P::E::set32(*mappedAddr32, (uint32_t)value64); @@ -626,7 +963,7 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)); int64_t newPage21 = pageDistance >> 12; if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) { - _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName); + _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _dylibID); return; } instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0); @@ -641,83 +978,81 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f if (lohTracker) (*lohTracker)[toNewAddress].insert(mappedAddr); instruction = P::E::get32(*mappedAddr32); - offsetAdjust = (adjust & 0xFFF); - if ( offsetAdjust == 0 ) - break; + // This is a page offset, so if we pack both the __TEXT page with the add/ldr, and + // the destination page with the target data, then the adjust isn't correct. Instead + // we always want the page offset of the target, ignoring where the source add/ldr slid + newPageOffset = (uint32_t)(toNewAddress & 0xFFF); if ( (instruction & 0x3B000000) == 0x39000000 ) { // LDR/STR imm12 - if ( offsetAdjust != 0 ) { - uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); - uint32_t newAddend = 0; - switch ( instruction & 0xC0000000 ) { - case 0x00000000: - if ( (instruction & 0x04800000) == 0x04800000 ) { - if ( offsetAdjust & 0xF ) { - _diagnostics.error("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); - return; - } - if ( encodedAddend*16 >= 4096 ) { - _diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); - } - newAddend = (encodedAddend + offsetAdjust/16) % 256; - } - else { - // scale=1 - newAddend = (encodedAddend + (int32_t)offsetAdjust) % 4096; - } - break; - case 0x40000000: - if ( offsetAdjust & 1 ) { - _diagnostics.error("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = 0; + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + if ( (instruction & 0x04800000) == 0x04800000 ) { + if ( newPageOffset & 0xF ) { + _diagnostics.error("can't adjust off12 scale=16 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID); return; } - if ( encodedAddend*2 >= 4096 ) { - _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); - return; + if ( encodedAddend*16 >= 4096 ) { + _diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID); } - newAddend = (encodedAddend + offsetAdjust/2) % 2048; - break; - case 0x80000000: - if ( offsetAdjust & 3 ) { - _diagnostics.error("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); - return; - } - if ( encodedAddend*4 >= 4096 ) { - _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); - return; - } - newAddend = (encodedAddend + offsetAdjust/4) % 1024; - break; - case 0xC0000000: - if ( offsetAdjust & 7 ) { - _diagnostics.error("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); - return; - } - if ( encodedAddend*8 >= 4096 ) { - _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); - return; - } - newAddend = (encodedAddend + offsetAdjust/8) % 512; - break; - } - uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); - P::E::set32(*mappedAddr32, newInstruction); + newAddend = (newPageOffset/16); + } + else { + // scale=1 + newAddend = newPageOffset; + } + break; + case 0x40000000: + if ( newPageOffset & 1 ) { + _diagnostics.error("can't adjust off12 scale=2 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID); + return; + } + if ( encodedAddend*2 >= 4096 ) { + _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID); + return; + } + newAddend = (newPageOffset/2); + break; + case 0x80000000: + if ( newPageOffset & 3 ) { + _diagnostics.error("can't adjust off12 scale=4 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID); + return; + } + if ( encodedAddend*4 >= 4096 ) { + _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID); + return; + } + newAddend = (newPageOffset/4); + break; + case 0xC0000000: + if ( newPageOffset & 7 ) { + _diagnostics.error("can't adjust off12 scale=8 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID); + return; + } + if ( encodedAddend*8 >= 4096 ) { + _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID); + return; + } + newAddend = (newPageOffset/8); + break; } + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + P::E::set32(*mappedAddr32, newInstruction); } else if ( (instruction & 0xFFC00000) == 0x91000000 ) { // ADD imm12 if ( instruction & 0x00C00000 ) { - _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName); + _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _dylibID); return; } - uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); - uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF; + uint32_t newAddend = newPageOffset; uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); P::E::set32(*mappedAddr32, newInstruction); } else if ( instruction != 0xD503201F ) { // ignore imm12 instructions optimized into a NOP, but warn about others - _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName); + _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _dylibID); return; } break; @@ -746,7 +1081,7 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f instruction1 = setThumbWord(instruction1, full >> 16); } else { - _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName); + _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _dylibID); return; } P::E::set32(*lastMappedAddr32, instruction1); @@ -754,7 +1089,7 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f kind = 0; } else { - _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName); + _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _dylibID); return; } } @@ -784,7 +1119,7 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f instruction1 = setArmWord(instruction1, full >> 16); } else { - _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName); + _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _dylibID); return; } P::E::set32(*lastMappedAddr32, instruction1); @@ -792,18 +1127,35 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f kind = 0; } else { - _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName); + _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _dylibID); return; } } break; - case DYLD_CACHE_ADJ_V2_ARM64_BR26: + case DYLD_CACHE_ADJ_V2_ARM64_BR26: { + if ( adjust == 0 ) + break; + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = P::E::get32(*mappedAddr32); + + int64_t deltaToFinalTarget = toNewAddress - fromNewAddress; + // Make sure the target is in range + static const int64_t b128MegLimit = 0x07FFFFFF; + if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + instruction = (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF); + P::E::set32(*mappedAddr32, instruction); + break; + } else { + _diagnostics.error("br26 instruction exceeds maximum range at mapped address=%p in %s", mappedAddr, _dylibID); + return; + } + } case DYLD_CACHE_ADJ_V2_THUMB_BR22: case DYLD_CACHE_ADJ_V2_ARM_BR24: // nothing to do with calls to stubs break; default: - _diagnostics.error("unknown split seg kind=%d in %s", kind, _installName); + _diagnostics.error("unknown split seg kind=%d in %s", kind, _dylibID); return; } lastKind = kind; @@ -813,66 +1165,101 @@ void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f template void Adjustor

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, - CacheBuilder::LOH_Tracker& lohTracker, - const CacheBuilder::CacheCoalescedText& coalescedText, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText, const CacheBuilder::DylibTextCoalescer& textCoalescer) { - static const bool log = false; + static const bool logDefault = false; + bool log = logDefault; - const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; - const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize]; if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) { - _diagnostics.error("malformed split seg info in %s", _installName); + _diagnostics.error("malformed split seg info in %s", _dylibID); return; } // build section arrays of slide and mapped address for each section std::vector sectionSlides; std::vector sectionNewAddress; std::vector sectionMappedAddress; + + // Also track coalesced sections, if we have any + typedef CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset DylibSectionOffsetToCacheSectionOffset; + std::vector coalescedSectionOriginalVMAddrs; + std::vector coalescedSectionNewVMAddrs; + std::vector coalescedSectionBufferAddrs; + std::vector coalescedSectionOffsetMaps; + std::vector coalescedSectionObjcTags; + sectionSlides.reserve(16); sectionNewAddress.reserve(16); sectionMappedAddress.reserve(16); + coalescedSectionOriginalVMAddrs.reserve(16); + coalescedSectionNewVMAddrs.reserve(16); + coalescedSectionBufferAddrs.reserve(16); + coalescedSectionOffsetMaps.reserve(16); + coalescedSectionObjcTags.reserve(16); + // section index 0 refers to mach_header sectionMappedAddress.push_back((uint8_t*)_mappingInfo[0].dstSegment); sectionSlides.push_back(_segSlides[0]); sectionNewAddress.push_back(_mappingInfo[0].dstCacheUnslidAddress); + coalescedSectionOriginalVMAddrs.push_back(0); + coalescedSectionNewVMAddrs.push_back(0); + coalescedSectionBufferAddrs.push_back(nullptr); + coalescedSectionOffsetMaps.push_back(nullptr); + coalescedSectionObjcTags.push_back(0); + + uint64_t imageStartAddress = sectionNewAddress.front(); + uint64_t imageEndAddress = 0; + // section 1 and later refer to real sections unsigned sectionIndex = 0; unsigned objcSelRefsSectionIndex = ~0U; - std::map coalescedSectionNames; - std::map coalescedSectionOriginalVMAddrs; for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) { macho_segment_command

* segCmd = _segCmds[segmentIndex]; macho_section

* const sectionsStart = (macho_section

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

)); macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - if ( (strcmp(segCmd->segname(), "__TEXT") == 0) && textCoalescer.sectionWasCoalesced(sect->sectname())) { + if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) { // If we coalesced the segment then the sections aren't really there to be fixed up + const DylibSectionOffsetToCacheSectionOffset& offsetMap = textCoalescer.getSectionCoalescer(sect->segname(), + sect->sectname()); + uint64_t coalescedSectionNewVMAddr = coalescedText->getSectionVMAddr(sect->segname(), sect->sectname()); + uint8_t* coalescedSectionNewBufferAddr = coalescedText->getSectionBufferAddr(sect->segname(), sect->sectname()); + uint64_t coalescedSectionObjcTag = coalescedText->getSectionObjcTag(sect->segname(), sect->sectname()); sectionMappedAddress.push_back(nullptr); sectionSlides.push_back(0); sectionNewAddress.push_back(0); + coalescedSectionOriginalVMAddrs.push_back(sect->addr()); + coalescedSectionNewVMAddrs.push_back(coalescedSectionNewVMAddr); + coalescedSectionBufferAddrs.push_back(coalescedSectionNewBufferAddr); + coalescedSectionOffsetMaps.push_back(&offsetMap); + coalescedSectionObjcTags.push_back(coalescedSectionObjcTag); + ++sectionIndex; if (log) { fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n", sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back()); } - ++sectionIndex; - std::string_view sectionName = sect->sectname(); - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - coalescedSectionNames[sectionIndex] = sectionName; - coalescedSectionOriginalVMAddrs[sectionIndex] = sect->addr(); } else { sectionMappedAddress.push_back((uint8_t*)_mappingInfo[segmentIndex].dstSegment + sect->addr() - segCmd->vmaddr()); sectionSlides.push_back(_segSlides[segmentIndex]); sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheUnslidAddress + sect->addr() - segCmd->vmaddr()); + coalescedSectionOriginalVMAddrs.push_back(0); + coalescedSectionNewVMAddrs.push_back(0); + coalescedSectionBufferAddrs.push_back(nullptr); + coalescedSectionOffsetMaps.push_back(nullptr); + coalescedSectionObjcTags.push_back(0); + ++sectionIndex; 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; + + imageEndAddress = sectionNewAddress.back(); } } } @@ -895,15 +1282,10 @@ void Adjustor

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex]; uint64_t toSectionSlide = sectionSlides[toSectionIndex]; uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex]; - CacheBuilder::LOH_Tracker* lohTrackerPtr = (toSectionIndex == objcSelRefsSectionIndex) ? &lohTracker : nullptr; + 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; - // We don't support updating split seg from a coalesced segment - if (coalescedSectionNames.find(fromSectionIndex) != coalescedSectionNames.end()) { - _diagnostics.error("split seg from coalesced segment in %s", _installName); - return; - } for (uint64_t j=0; j < toOffsetCount; ++j) { uint64_t toSectionDelta = read_uleb128(p, infoEnd); uint64_t fromOffsetCount = read_uleb128(p, infoEnd); @@ -911,7 +1293,7 @@ void Adjustor

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr for (uint64_t k=0; k < fromOffsetCount; ++k) { uint64_t kind = read_uleb128(p, infoEnd); if ( kind > 13 ) { - _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _installName); + _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _dylibID); return; } uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); @@ -919,37 +1301,84 @@ void Adjustor

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr for (uint64_t l=0; l < fromSectDeltaCount; ++l) { uint64_t delta = read_uleb128(p, infoEnd); fromSectionOffset += delta; - //if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); + if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, delta, toSectionSlide); - uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; - uint64_t toNewAddress = toSectionNewAddress + toSectionOffset; - uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset; - uint64_t imageStartAddress = sectionNewAddress.front(); - uint64_t imageEndAddress = sectionNewAddress.back(); - if ( toSectionIndex != 255 ) { - auto textCoalIt = coalescedSectionNames.find(toSectionIndex); - if (textCoalIt != coalescedSectionNames.end() ) { - //printf("Section name: %s\n", textCoalIt->second.data()); - const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& offsetMap = textCoalescer.getSectionCoalescer(textCoalIt->second); - auto offsetIt = offsetMap.find((uint32_t)toSectionOffset); - assert(offsetIt != offsetMap.end()); - uint64_t baseVMAddr = coalescedText.getSectionData(textCoalIt->second).bufferVMAddr; - toNewAddress = baseVMAddr + offsetIt->second; - - // The 'to' section is gone, but we still need the 'to' slide. Instead of a section slide, compute the slide - // for this individual atom - uint64_t toAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[toSectionIndex] + toSectionOffset; - uint64_t toAtomSlide = toNewAddress - toAtomOriginalVMAddr; - int64_t deltaAdjust = toAtomSlide - fromSectionSlide; - adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toAtomSlide, - imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress); - - } else { - int64_t deltaAdjust = toSectionSlide - fromSectionSlide; - adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, - imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress); + // It's possible for all either of from/to sectiobs to be coalesced/optimized. + // Handle each of those combinations. + uint8_t* fromMappedAddr = nullptr; + uint64_t fromNewAddress = 0; + uint64_t fromAtomSlide = 0; + bool convertRebaseChains = false; + if ( coalescedSectionOffsetMaps[fromSectionIndex] != nullptr ) { + // From was optimized/coalesced + // This is only supported on pointer kind fixups, ie, pointers in RW segments + assert( (kind == DYLD_CACHE_ADJ_V2_POINTER_64) || (kind == DYLD_CACHE_ADJ_V2_THREADED_POINTER_64) ); + // Find where the atom moved to with its new section + // CFString's and similar may have fixups in the middle of the atom, but the map only + // tracks the start offset for the atom. We use lower_bound to find the atom containing + // the offset we are looking for + const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[fromSectionIndex]; + auto offsetIt = offsetMap->lower_bound((uint32_t)fromSectionOffset); + if ( offsetIt->first != fromSectionOffset ) { + // This points to the middle of the atom, so check the previous atom + assert(offsetIt != offsetMap->begin()); + --offsetIt; + assert(offsetIt->first <= fromSectionOffset); } + assert(offsetIt != offsetMap->end()); + // FIXME: Other CF type's have different atom sizes + uint64_t offsetInAtom = fromSectionOffset - offsetIt->first; + assert(offsetInAtom < (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize); + + uint8_t* baseMappedAddr = coalescedSectionBufferAddrs[fromSectionIndex]; + fromMappedAddr = baseMappedAddr + offsetIt->second + offsetInAtom; + uint64_t baseVMAddr = coalescedSectionNewVMAddrs[fromSectionIndex]; + fromNewAddress = baseVMAddr + offsetIt->second + offsetInAtom; + + // The 'from' section is gone, but we still need the 'from' slide. Instead of a section slide, + // compute the slide for this individual atom + uint64_t fromAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[fromSectionIndex] + fromSectionOffset; + fromAtomSlide = fromNewAddress - fromAtomOriginalVMAddr; + convertRebaseChains = true; + } else { + // From was not optimized/coalesced + fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; + fromNewAddress = fromSectionNewAddress + fromSectionOffset; + fromAtomSlide = fromSectionSlide; } + + uint64_t toNewAddress = 0; + uint64_t toAtomSlide = 0; + if ( coalescedSectionOffsetMaps[toSectionIndex] != nullptr ) { + // To was optimized/coalesced + const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[toSectionIndex]; + auto offsetIt = offsetMap->find((uint32_t)toSectionOffset); + assert(offsetIt != offsetMap->end()); + uint64_t baseVMAddr = coalescedSectionNewVMAddrs[toSectionIndex]; + toNewAddress = baseVMAddr + offsetIt->second; + + // Add in the high bits which are the tagged pointer TBI bits + toNewAddress |= coalescedSectionObjcTags[toSectionIndex]; + + // The 'to' section is gone, but we still need the 'to' slide. Instead of a section slide, + // compute the slide for this individual atom + uint64_t toAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[toSectionIndex] + toSectionOffset; + toAtomSlide = toNewAddress - toAtomOriginalVMAddr; + } else { + // To was not optimized/coalesced + toNewAddress = toSectionNewAddress + toSectionOffset; + toAtomSlide = toSectionSlide; + } + + int64_t deltaAdjust = toAtomSlide - fromAtomSlide; + if (log) { + printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", + kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); + } + adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, + toAtomSlide, imageStartAddress, imageEndAddress, convertRebaseChains, + aslrTracker, lohTrackerPtr, + lastMappedAddr32, lastKind, lastToNewAddress); if ( _diagnostics.hasError() ) return; } @@ -959,11 +1388,126 @@ void Adjustor

::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr } +template +void Adjustor

::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker) +{ + const dyld_chained_fixups_header* chainHeader = (dyld_chained_fixups_header*)(&_linkeditBias[_chainedFixupsCmd->dataoff]); + const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset); + _mh->forEachFixupInAllChains(_diagnostics, startsInfo, false, + ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + switch ( segInfo->pointer_format ) { + case DYLD_CHAINED_PTR_64: + // only look at rebases + if ( fixupLoc->generic64.rebase.bind == 0 ) { + uint64_t rebaseTargetInDylib = fixupLoc->generic64.rebase.target; + uint64_t rebaseTargetInDyldcache = fixupLoc->generic64.rebase.target + slideForOrigAddress(rebaseTargetInDylib); + convertGeneric64RebaseToIntermediate(fixupLoc, aslrTracker, rebaseTargetInDyldcache); + aslrTracker.add(fixupLoc); + } + break; + case DYLD_CHAINED_PTR_64_OFFSET: + _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID); + break; + default: + _diagnostics.error("unsupported chained fixup format %d", segInfo->pointer_format); + stop = true; + } + }); +} + +static int uint32Sorter(const void* l, const void* r) { + if ( *((uint32_t*)l) < *((uint32_t*)r) ) + return -1; + else + return 1; +} + +template +static uint64_t localRelocBaseAddress(const dyld3::MachOAnalyzer* ma, + std::vector*> segCmds, + std::vector segOrigStartAddresses) +{ + if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) { +#if BUILDING_APP_CACHE_UTIL + if ( ma->isKextBundle() ) { + // for kext bundles the reloc base address starts at __TEXT segment + return segOrigStartAddresses[0]; + } +#endif + // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA) + for (uint32_t i=0; i < segCmds.size(); ++i) { + if ( segCmds[i]->initprot() & VM_PROT_WRITE ) + return segOrigStartAddresses[i]; + } + } + return segOrigStartAddresses[0]; +} + +static bool segIndexAndOffsetForAddress(uint64_t addr, const std::vector& segOrigStartAddresses, + std::vector segSizes, uint32_t& segIndex, uint64_t& segOffset) +{ + for (uint32_t i=0; i < segOrigStartAddresses.size(); ++i) { + if ( (segOrigStartAddresses[i] <= addr) && (addr < (segOrigStartAddresses[i] + segSizes[i])) ) { + segIndex = i; + segOffset = addr - segOrigStartAddresses[i]; + return true; + } + } + return false; +} + template void Adjustor

::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker) { - const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()]; - const uint8_t* end = &p[_dyldInfo->rebase_size()]; + if ( (_dynSymTabCmd != nullptr) && (_dynSymTabCmd->locreloff != 0) ) { + // kexts may have old style relocations instead of dyldinfo rebases + assert(_dyldInfo == nullptr); + + // old binary, walk relocations + const uint64_t relocsStartAddress = localRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses); + const relocation_info* const relocsStart = (const relocation_info* const)&_linkeditBias[_dynSymTabCmd->locreloff]; + const relocation_info* const relocsEnd = &relocsStart[_dynSymTabCmd->nlocrel]; + bool stop = false; + const uint8_t relocSize = (_mh->is64() ? 3 : 2); + STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, relocAddrs, 2048); + for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) { + if ( reloc->r_length != relocSize ) { + _diagnostics.error("local relocation has wrong r_length"); + break; + } + if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED + _diagnostics.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, _segOrigStartAddresses, _segSizes, segIndex, segOffset) ) { + uint8_t type = REBASE_TYPE_POINTER; + assert(_mh->cputype != CPU_TYPE_I386); + slidePointer(segIndex, segOffset, type, aslrTracker); + } + else { + _diagnostics.error("local relocation has out of range r_address"); + break; + } + } + } + // then process indirect symbols + // FIXME: Do we need indirect symbols? Aren't those handled as binds? + + return; + } + + if ( _dyldInfo == NULL ) + return; + + const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off]; + const uint8_t* end = &p[_dyldInfo->rebase_size]; uint8_t type = 0; int segIndex = 0; @@ -1018,7 +1562,7 @@ void Adjustor

::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker) } break; default: - _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _installName); + _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _dylibID); done = true; break; } @@ -1154,8 +1698,11 @@ template void Adjustor

::adjustCode() { // find compressed info on how code needs to be updated - const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; - const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];; + if ( _splitSegInfoCmd == nullptr ) + return; + + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize];; // This encoding only works if all data segments slide by the same amount uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0]; @@ -1179,11 +1726,11 @@ void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) uint32_t exportOffset = 0; uint32_t exportSize = 0; if ( _dyldInfo != nullptr ) { - exportOffset = _dyldInfo->export_off(); - exportSize = _dyldInfo->export_size(); - } else { - exportOffset = _exportTrieCmd->dataoff(); - exportSize = _exportTrieCmd->datasize(); + exportOffset = _dyldInfo->export_off; + exportSize = _dyldInfo->export_size; + } else if (_exportTrieCmd != nullptr) { + exportOffset = _exportTrieCmd->dataoff; + exportSize = _exportTrieCmd->datasize; } if ( exportSize == 0 ) @@ -1195,7 +1742,7 @@ void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) const uint8_t* end = &start[exportSize]; std::vector originalExports; if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) { - _diagnostics.error("malformed exports trie in %s", _installName); + _diagnostics.error("malformed exports trie in %s", _dylibID); return; } @@ -1228,16 +1775,27 @@ void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) } // anonymous namespace -void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const +void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag, + uint64_t cacheBaseAddress, + CacheBuilder::ASLR_Tracker& aslrTracker, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText) const { - DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - if ( _archLayout->is64 ) { - Adjustor> adjustor64(cache, (macho_header>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag); - adjustor64.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker, _coalescedText, dylib.textCoalescer); + + dyld3::MachOAnalyzer* mh = (dyld3::MachOAnalyzer*)dylib.cacheLocation[0].dstSegment; + if ( _is64 ) { + Adjustor> adjustor64(cacheBaseAddress, + mh, + dylib.dylibID.c_str(), + dylib.cacheLocation, diag); + adjustor64.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer); } else { - Adjustor> adjustor32(cache, (macho_header>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag); - adjustor32.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker, _coalescedText, dylib.textCoalescer); + Adjustor> adjustor32(cacheBaseAddress, + mh, + dylib.dylibID.c_str(), + dylib.cacheLocation, diag); + adjustor32.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer); } } diff --git a/dyld3/shared-cache/AppCacheBuilder.cpp b/dyld3/shared-cache/AppCacheBuilder.cpp new file mode 100644 index 0000000..2cc9800 --- /dev/null +++ b/dyld3/shared-cache/AppCacheBuilder.cpp @@ -0,0 +1,5438 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- +* +* Copyright (c) 2014 Apple Inc. All rights reserved. +* +* @APPLE_LICENSE_HEADER_START@ +* +* This file contains Original Code and/or Modifications of Original Code +* as defined in and that are subject to the Apple Public Source License +* Version 2.0 (the 'License'). You may not use this file except in +* compliance with the License. Please obtain a copy of the License at +* http://www.opensource.apple.com/apsl/ and read it before using this +* file. +* +* The Original Code and all software distributed under the License are +* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +* Please see the License for the specific language governing rights and +* limitations under the License. +* +* @APPLE_LICENSE_HEADER_END@ +*/ + +#include "AppCacheBuilder.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +AppCacheBuilder::AppCacheBuilder(const DyldSharedCache::CreateOptions& options, + const Options& appCacheOptions, + const dyld3::closure::FileSystem& fileSystem) + : CacheBuilder(options, fileSystem), appCacheOptions(appCacheOptions) +{ + // FIXME: 32-bit support + _is64 = true; +} + +AppCacheBuilder::~AppCacheBuilder() { + if (prelinkInfoDict) { + CFRelease(prelinkInfoDict); + } + if (_fullAllocatedBuffer) { + vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _allocatedBufferSize); + } +} + + +void AppCacheBuilder::makeSortedDylibs(const std::vector& dylibs) +{ + for (const InputDylib& file : dylibs) { + if ( file.dylib.loadedFileInfo.fileContent == nullptr ) { + codelessKexts.push_back(file); + } else { + AppCacheDylibInfo& dylibInfo = sortedDylibs.emplace_back(); + dylibInfo.input = &file.dylib; + dylibInfo.dylibID = file.dylibID; + dylibInfo.dependencies = file.dylibDeps; + dylibInfo.infoPlist = file.infoPlist; + dylibInfo.errors = file.errors; + dylibInfo.bundlePath = file.bundlePath; + dylibInfo.stripMode = file.stripMode; + } + } + + std::sort(sortedDylibs.begin(), sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) { + // Sort the kernel first, then kext's + bool isStaticExecutableA = a.input->mappedFile.mh->isStaticExecutable(); + bool isStaticExecutableB = b.input->mappedFile.mh->isStaticExecutable(); + if (isStaticExecutableA != isStaticExecutableB) + return isStaticExecutableA; + + // Sort split seg next + bool splitSegA = a.input->mappedFile.mh->hasSplitSeg(); + bool splitSegB = b.input->mappedFile.mh->hasSplitSeg(); + if (splitSegA != splitSegB) + return splitSegA; + + // Finally sort by path + return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath; + }); + + // Sort codeless kext's by ID + std::sort(codelessKexts.begin(), codelessKexts.end(), [&](const InputDylib& a, const InputDylib& b) { + return a.dylibID < b.dylibID; + }); +} + + +void AppCacheBuilder::forEachCacheDylib(void (^callback)(const dyld3::MachOAnalyzer* ma, + const std::string& dylibID, + DylibStripMode stripMode, + const std::vector& dependencies, + Diagnostics& dylibDiag, + bool& stop)) const { + bool stop = false; + for (const AppCacheDylibInfo& dylib : sortedDylibs) { + for (const SegmentMappingInfo& loc : dylib.cacheLocation) { + if (!strcmp(loc.segName, "__TEXT")) { + // Assume __TEXT contains the mach header + callback((const dyld3::MachOAnalyzer*)loc.dstSegment, dylib.dylibID, dylib.stripMode, + dylib.dependencies, *dylib.errors, stop); + break; + } + } + if (stop) + break; + } +} + +void AppCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) { + for (const AppCacheDylibInfo& dylibInfo : sortedDylibs) + callback(dylibInfo, *dylibInfo.errors); +} + +const CacheBuilder::DylibInfo* AppCacheBuilder::getKernelStaticExecutableInputFile() const { + for (const auto& dylib : sortedDylibs) { + const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh; + if ( ma->isStaticExecutable() ) + return &dylib; + } + return nullptr; +} + +const dyld3::MachOAnalyzer* AppCacheBuilder::getKernelStaticExecutableFromCache() const { + // FIXME: Support reading this from a prebuilt KC + assert(appCacheOptions.cacheKind == Options::AppCacheKind::kernel); + + __block const dyld3::MachOAnalyzer* kernelMA = nullptr; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector& dependencies, + Diagnostics& dylibDiag, + bool& stop) { + if ( ma->isStaticExecutable() ) { + kernelMA = ma; + stop = true; + } + }); + + assert(kernelMA != nullptr); + return kernelMA; +} + +void AppCacheBuilder::forEachRegion(void (^callback)(const Region& region)) const { + // cacheHeaderRegion + callback(cacheHeaderRegion); + + // readOnlyTextRegion + callback(readOnlyTextRegion); + + // readExecuteRegion + if ( readExecuteRegion.sizeInUse != 0 ) + callback(readExecuteRegion); + + // branchStubsRegion + if ( branchStubsRegion.bufferSize != 0 ) + callback(branchStubsRegion); + + // dataConstRegion + if ( dataConstRegion.sizeInUse != 0 ) + callback(dataConstRegion); + + // branchGOTsRegion + if ( branchGOTsRegion.bufferSize != 0 ) + callback(branchGOTsRegion); + + // readWriteRegion + if ( readWriteRegion.sizeInUse != 0 ) + callback(readWriteRegion); + + // hibernateRegion + if ( hibernateRegion.sizeInUse != 0 ) + callback(hibernateRegion); + + // -sectcreate + for (const Region& region : customDataRegions) + callback(region); + + // prelinkInfoRegion + if ( prelinkInfoDict != nullptr ) + callback(prelinkInfoRegion); + + // nonSplitSegRegions + for (const Region& region : nonSplitSegRegions) + callback(region); + + // _readOnlyRegion + callback(_readOnlyRegion); + + // fixupsRegion + // We don't count this as its not a real region +} + +uint64_t AppCacheBuilder::numRegions() const { + __block uint64_t count = 0; + + forEachRegion(^(const Region ®ion) { + ++count; + }); + + return count; +} + +uint64_t AppCacheBuilder::fixupsPageSize() const { + bool use4K = false; + use4K |= (_options.archs == &dyld3::GradedArchs::x86_64); + use4K |= (_options.archs == &dyld3::GradedArchs::x86_64h); + return use4K ? 4096 : 16384; +} + +uint64_t AppCacheBuilder::numWritablePagesToFixup(uint64_t numBytesToFixup) const { + uint64_t pageSize = fixupsPageSize(); + assert((numBytesToFixup % pageSize) == 0); + uint64_t numPagesToFixup = numBytesToFixup / pageSize; + return numPagesToFixup; +} + +// Returns true if each kext inside the KC needs to be reloadable, ie, have its +// pages reset and its start method rerun. This means we can't pack pages and need +// fixups on each kext individually +bool AppCacheBuilder::fixupsArePerKext() const { + if ( appCacheOptions.cacheKind == Options::AppCacheKind::pageableKC ) + return true; + bool isX86 = (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h); + return isX86 && (appCacheOptions.cacheKind == Options::AppCacheKind::auxKC); +} + +// x86_64 kext's don't contain stubs for branches so we need to generate some +// if branches cross from one KC to another, eg, from the auxKC to the base KC +uint64_t AppCacheBuilder::numBranchRelocationTargets() { + bool mayHaveBranchRelocations = false; + mayHaveBranchRelocations |= (_options.archs == &dyld3::GradedArchs::x86_64); + mayHaveBranchRelocations |= (_options.archs == &dyld3::GradedArchs::x86_64h); + if ( !mayHaveBranchRelocations ) + return 0; + + switch (appCacheOptions.cacheKind) { + case Options::AppCacheKind::none: + case Options::AppCacheKind::kernel: + // Nothing to do here as we can't bind from a lower level up to a higher one + return 0; + case Options::AppCacheKind::pageableKC: + case Options::AppCacheKind::kernelCollectionLevel2: + case Options::AppCacheKind::auxKC: + // Any calls in these might be to a lower level so add space for each call target + break; + } + + uint64_t totalTargets = 0; + for (const DylibInfo& dylib : sortedDylibs) { + // We need the symbol name and libOrdinal just in case we bind to the same symbol name in 2 different KCs + typedef std::pair Symbol; + struct SymbolHash + { + size_t operator() (const Symbol& symbol) const + { + return std::hash{}(symbol.first) ^ std::hash{}(symbol.second); + } + }; + __block std::unordered_set seenSymbols; + dylib.input->mappedFile.mh->forEachBind(_diagnostics, + ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, + const char *symbolName, bool weakImport, + bool lazyBind, uint64_t addend, bool &stop) { + if ( type != BIND_TYPE_TEXT_PCREL32 ) + return; + seenSymbols.insert({ symbolName, libOrdinal }); + }, ^(const char *symbolName) { + }); + totalTargets += seenSymbols.size(); + } + return totalTargets; +} + +void AppCacheBuilder::assignSegmentRegionsAndOffsets() +{ + // Segments can be re-ordered in memory relative to the order of the LC_SEGMENT load comamnds + // so first make space for all the cache location objects so that we get the order the same + // as the LC_SEGMENTs + for (DylibInfo& dylib : sortedDylibs) { + dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + dylib.cacheLocation.push_back({}); + }); + } + + // If we are building the kernel collection, then inherit the base address of the statically linked kernel + const dyld3::MachOAnalyzer* kernelMA = nullptr; + if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) { + for (DylibInfo& dylib : sortedDylibs) { + if ( dylib.input->mappedFile.mh->isStaticExecutable() ) { + kernelMA = dylib.input->mappedFile.mh; + break; + } + } + if ( kernelMA == nullptr ) { + _diagnostics.error("Could not find kernel image"); + return; + } + cacheBaseAddress = kernelMA->preferredLoadAddress(); + } + + // x86_64 doesn't have stubs for kext branches. So work out how many potential targets + // we need to emit stubs for. + uint64_t branchTargetsFromKexts = numBranchRelocationTargets(); + + uint32_t minimumSegmentAlignmentP2 = 14; + if ( (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h) ) { + minimumSegmentAlignmentP2 = 12; + } + + auto getMinAlignment = ^(const dyld3::MachOAnalyzer* ma) { + // The kernel wants to be able to unmap its own segments so always align it. + // And align the pageable KC as each kext can be mapped individually + if ( ma == kernelMA ) + return minimumSegmentAlignmentP2; + if ( fixupsArePerKext() ) + return minimumSegmentAlignmentP2; + if ( _options.archs == &dyld3::GradedArchs::arm64e ) + return minimumSegmentAlignmentP2; + return 0U; + }; + + { + // __TEXT segments with r/o permissions + __block uint64_t offsetInRegion = 0; + for (DylibInfo& dylib : sortedDylibs) { + bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg(); + if (!canBePacked) + continue; + + __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(segInfo.segName, "__DATA_CONST") == 0) + || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0) + || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) ) + return; + if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 ) + return; + if ( strcmp(segInfo.segName, "__LINKINFO") == 0 ) + return; + + uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2); + + // __CTF is not mapped in to the kernel, so remove it from the final binary. + if ( strcmp(segInfo.segName, "__CTF") == 0 ) { + copySize = 0; + dstCacheSegmentSize = 0; + } + + // kxld packs __TEXT so we will do + // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address + // and aligning them less than 16 would break that + offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U)); + offsetInRegion = align(offsetInRegion, minAlignmentP2); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)dstCacheSegmentSize; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &readOnlyTextRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += dstCacheSegmentSize; + }); + } + + // kclist needs this segment, even if its empty, so leave it in there + readOnlyTextRegion.bufferSize = align(offsetInRegion, 14); + readOnlyTextRegion.sizeInUse = readOnlyTextRegion.bufferSize; + readOnlyTextRegion.initProt = VM_PROT_READ; + readOnlyTextRegion.maxProt = VM_PROT_READ; + readOnlyTextRegion.name = "__PRELINK_TEXT"; + } + + // __TEXT segments with r/x permissions + { + // __TEXT segments with r/x permissions + __block uint64_t offsetInRegion = 0; + for (DylibInfo& dylib : sortedDylibs) { + bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg(); + if (!canBePacked) + continue; + + __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 ( strcmp(segInfo.segName, "__HIB") == 0 ) + return; + if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) ) + return; + // kxld packs __TEXT_EXEC so we will do + // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address + // and aligning them less than 16 would break that + uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh); + offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U)); + offsetInRegion = align(offsetInRegion, minAlignmentP2); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)dstCacheSegmentSize; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &readExecuteRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + }); + } + + // align r/x region end + readExecuteRegion.bufferSize = align(offsetInRegion, 14); + readExecuteRegion.sizeInUse = readExecuteRegion.bufferSize; + readExecuteRegion.initProt = VM_PROT_READ | VM_PROT_EXECUTE; + readExecuteRegion.maxProt = VM_PROT_READ | VM_PROT_EXECUTE; + readExecuteRegion.name = "__TEXT_EXEC"; + } + + if ( branchTargetsFromKexts != 0 ) { + // 6-bytes per jmpq + branchStubsRegion.bufferSize = align(branchTargetsFromKexts * 6, 14); + branchStubsRegion.sizeInUse = branchStubsRegion.bufferSize; + branchStubsRegion.initProt = VM_PROT_READ | VM_PROT_EXECUTE; + branchStubsRegion.maxProt = VM_PROT_READ | VM_PROT_EXECUTE; + branchStubsRegion.name = "__BRANCH_STUBS"; + } + + // __DATA_CONST segments + { + __block uint64_t offsetInRegion = 0; + for (DylibInfo& dylib : sortedDylibs) { + if (!dylib.input->mappedFile.mh->hasSplitSeg()) + continue; + + __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_EXECUTE) != 0 ) + return; + if ( (strcmp(segInfo.segName, "__DATA_CONST") != 0) + && (strcmp(segInfo.segName, "__PPLDATA_CONST") != 0) + && (strcmp(segInfo.segName, "__LASTDATA_CONST") != 0) ) + return; + // kxld packs __DATA_CONST so we will do + uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh); + offsetInRegion = align(offsetInRegion, segInfo.p2align); + offsetInRegion = align(offsetInRegion, minAlignmentP2); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)dstCacheSegmentSize; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &dataConstRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + }); + } + + // align r/o region end + dataConstRegion.bufferSize = align(offsetInRegion, 14); + dataConstRegion.sizeInUse = dataConstRegion.bufferSize; + dataConstRegion.initProt = VM_PROT_READ; + dataConstRegion.maxProt = VM_PROT_READ; + dataConstRegion.name = "__DATA_CONST"; + } + + // Branch GOTs + if ( branchTargetsFromKexts != 0 ) { + // 8-bytes per GOT + branchGOTsRegion.bufferSize = align(branchTargetsFromKexts * 8, 14); + branchGOTsRegion.sizeInUse = branchGOTsRegion.bufferSize; + branchGOTsRegion.initProt = VM_PROT_READ | VM_PROT_WRITE; + branchGOTsRegion.maxProt = VM_PROT_READ | VM_PROT_WRITE; + branchGOTsRegion.name = "__BRANCH_GOTS"; + } + + // __DATA* segments + { + __block uint64_t offsetInRegion = 0; + for (DylibInfo& dylib : sortedDylibs) { + if (!dylib.input->mappedFile.mh->hasSplitSeg()) + continue; + + __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 ( strcmp(segInfo.segName, "__HIB") == 0 ) + return; + if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0) + || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0) + || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) ) + return; + if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) ) + return; + // kxld packs __DATA so we will do + uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh); + offsetInRegion = align(offsetInRegion, segInfo.p2align); + offsetInRegion = align(offsetInRegion, minAlignmentP2); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)dstCacheSegmentSize; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &readWriteRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + }); + } + + // align r/w region end + readWriteRegion.bufferSize = align(offsetInRegion, 14); + readWriteRegion.sizeInUse = readWriteRegion.bufferSize; + readWriteRegion.initProt = VM_PROT_READ | VM_PROT_WRITE; + readWriteRegion.maxProt = VM_PROT_READ | VM_PROT_WRITE; + readWriteRegion.name = "__DATA"; + } + + { + // Hibernate region + __block uint64_t offsetInRegion = 0; + for (DylibInfo& dylib : sortedDylibs) { + if ( !dylib.input->mappedFile.mh->isStaticExecutable() ) + continue; + + __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 ( strcmp(segInfo.segName, "__HIB") != 0 ) + return; + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)segInfo.vmSize; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &hibernateRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + + hibernateAddress = segInfo.vmAddr; + }); + + // Only xnu has __HIB, so no need to continue once we've found it. + break; + } + + hibernateRegion.bufferSize = align(offsetInRegion, 14); + hibernateRegion.sizeInUse = hibernateRegion.bufferSize; + hibernateRegion.initProt = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + hibernateRegion.maxProt = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; + hibernateRegion.name = "__HIB"; + } + + // __TEXT and __DATA from non-split seg dylibs, if we have any + { + for (DylibInfo& dylib : sortedDylibs) { + bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg(); + if (canBePacked) + continue; + + __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 ( strcmp(segInfo.segName, "__LINKEDIT") == 0 ) + return; + + nonSplitSegRegions.emplace_back(); + nonSplitSegRegions.back().initProt = segInfo.protections; + nonSplitSegRegions.back().maxProt = segInfo.protections; + nonSplitSegRegions.back().name = "__REGION" + std::to_string(nonSplitSegRegions.size() - 1); + + // Note we don't align the region offset as we have no split seg + uint64_t offsetInRegion = 0; + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)segInfo.vmSize; + loc.dstCacheFileSize = (uint32_t)segInfo.fileSize; + loc.copySegmentSize = (uint32_t)segInfo.fileSize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &nonSplitSegRegions.back(); + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + + // record non-split seg region end + nonSplitSegRegions.back().bufferSize = offsetInRegion; + nonSplitSegRegions.back().sizeInUse = nonSplitSegRegions.back().bufferSize; + }); + } + } + + // -sectcreate + if ( !customSegments.empty() ) { + for (CustomSegment& segment: customSegments) { + uint64_t offsetInRegion = 0; + for (CustomSegment::CustomSection& section : segment.sections) { + section.offsetInRegion = offsetInRegion; + offsetInRegion += section.data.size(); + } + + Region& customRegion = customDataRegions.emplace_back(); + segment.parentRegion = &customRegion; + + // align region end + customRegion.bufferSize = align(offsetInRegion, 14); + customRegion.sizeInUse = customRegion.bufferSize; + customRegion.initProt = VM_PROT_READ; + customRegion.maxProt = VM_PROT_READ; + customRegion.name = segment.segmentName; + } + } + + // __PRELINK_INFO + { + // This is populated with regular kexts and codeless kexts + struct PrelinkInfo { + CFDictionaryRef infoPlist = nullptr; + const dyld3::MachOAnalyzer* ma = nullptr; + std::string_view bundlePath; + std::string_view executablePath; + }; + std::vector infos; + for (AppCacheDylibInfo& dylib : sortedDylibs) { + if (dylib.infoPlist == nullptr) + continue; + infos.push_back({ dylib.infoPlist, dylib.input->mappedFile.mh, dylib.bundlePath, dylib.input->loadedFileInfo.path }); + } + for (InputDylib& dylib : codelessKexts) { + infos.push_back({ dylib.infoPlist, nullptr, dylib.bundlePath, "" }); + } + + CFMutableArrayRef bundlesArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeArrayCallBacks); + for (PrelinkInfo& info : infos) { + CFDictionaryRef infoPlist = info.infoPlist; + // Create a copy of the dictionary so that we can add more fields + CFMutableDictionaryRef dictCopyRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, infoPlist); + + // _PrelinkBundlePath + CFStringRef bundlePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, info.bundlePath.data(), + kCFStringEncodingASCII, kCFAllocatorNull); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkBundlePath"), bundlePath); + CFRelease(bundlePath); + + // Note we want this address to be a large enough integer in the xml format that we have enough space + // to replace it with its real address later + const uint64_t largeAddress = 0x7FFFFFFFFFFFFFFF; + + // _PrelinkExecutableLoadAddr + // Leave a placeholder for this for now just so that we have enough space for it later + CFNumberRef loadAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableLoadAddr"), loadAddrRef); + CFRelease(loadAddrRef); + + // _PrelinkExecutableRelativePath + if ( info.executablePath != "" ) { + const char* relativePath = info.executablePath.data(); + if ( strncmp(relativePath, info.bundlePath.data(), info.bundlePath.size()) == 0 ) { + relativePath = relativePath + info.bundlePath.size(); + if ( relativePath[0] == '/' ) + ++relativePath; + } else if ( const char* lastSlash = strrchr(relativePath, '/') ) + relativePath = lastSlash+1; + CFStringRef executablePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, relativePath, + kCFStringEncodingASCII, kCFAllocatorNull); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableRelativePath"), executablePath); + CFRelease(executablePath); + } + + // _PrelinkExecutableSize + // This seems to be the file size of __TEXT + __block uint64_t textSegFileSize = 0; + if ( info.ma != nullptr ) { + info.ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + if ( strcmp(segInfo.segName, "__TEXT") == 0 ) + textSegFileSize = segInfo.fileSize; + }); + } + if (textSegFileSize != 0) { + CFNumberRef fileSizeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &textSegFileSize); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableSize"), fileSizeRef); + CFRelease(fileSizeRef); + } + + // _PrelinkExecutableSourceAddr + // Leave a placeholder for this for now just so that we have enough space for it later + CFNumberRef sourceAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableSourceAddr"), sourceAddrRef); + CFRelease(sourceAddrRef); + + // _PrelinkKmodInfo + // Leave a placeholder for this for now just so that we have enough space for it later + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + if ( (info.ma != nullptr) ) { + // Check for a global first + __block bool found = false; + found = info.ma->findExportedSymbol(_diagnostics, "_kmod_info", true, foundInfo, nullptr); + if ( !found ) { + // And fall back to a local if we need to + info.ma->forEachLocalSymbol(_diagnostics, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( strcmp(aSymbolName, "_kmod_info") == 0 ) { + found = true; + stop = true; + } + }); + } + + if ( found ) { + CFNumberRef kmodInfoAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress); + CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkKmodInfo"), kmodInfoAddrRef); + CFRelease(kmodInfoAddrRef); + } + } + + CFArrayAppendValue(bundlesArrayRef, dictCopyRef); + // Release the temporary dictionary now that its in the array + CFRelease(dictCopyRef); + } + + prelinkInfoDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // First add any data from addPrelinkInfo() + if ( extraPrelinkInfo != nullptr ) { + CFDictionaryApplierFunction applier = [](const void *key, const void *value, void *context) { + CFMutableDictionaryRef parentDict = (CFMutableDictionaryRef)context; + CFDictionaryAddValue(parentDict, key, value); + }; + CFDictionaryApplyFunction(extraPrelinkInfo, applier, (void*)prelinkInfoDict); + } + + if ( bundlesArrayRef != nullptr ) { + CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PrelinkInfoDictionary"), bundlesArrayRef); + CFRelease(bundlesArrayRef); + } + + // Add a placeholder for the collection UUID + { + uuid_t uuid = {}; + CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid)); + CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PrelinkKCID"), dataRef); + CFRelease(dataRef); + } + + // The pageable/aux KCs should embed the UUID of the base kernel collection + if ( existingKernelCollection != nullptr ) { + uuid_t uuid = {}; + bool foundUUID = existingKernelCollection->getUuid(uuid); + if ( !foundUUID ) { + _diagnostics.error("Could not find UUID in base kernel collection"); + return; + } + CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid)); + CFDictionaryAddValue(prelinkInfoDict, CFSTR("_BootKCID"), dataRef); + CFRelease(dataRef); + } + + // The aux KC should embed the UUID of the pageable kernel collection if we have one + if ( pageableKernelCollection != nullptr ) { + uuid_t uuid = {}; + bool foundUUID = pageableKernelCollection->getUuid(uuid); + if ( !foundUUID ) { + _diagnostics.error("Could not find UUID in pageable kernel collection"); + return; + } + CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid)); + CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PageableKCID"), dataRef); + CFRelease(dataRef); + } + + CFErrorRef errorRef = nullptr; + CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict, + kCFPropertyListXMLFormat_v1_0, 0, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + _diagnostics.error("Could not serialise plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(xmlData); + CFRelease(errorRef); + return; + } else { + CFIndex xmlDataLength = CFDataGetLength(xmlData); + CFRelease(xmlData); + + // align region end + prelinkInfoRegion.bufferSize = align(xmlDataLength, 14); + prelinkInfoRegion.sizeInUse = prelinkInfoRegion.bufferSize; + prelinkInfoRegion.initProt = VM_PROT_READ | VM_PROT_WRITE; + prelinkInfoRegion.maxProt = VM_PROT_READ | VM_PROT_WRITE; + prelinkInfoRegion.name = "__PRELINK_INFO"; + } + } + + // Do all __LINKINFO regardless of split seg + _nonLinkEditReadOnlySize = 0; + __block uint64_t offsetInRegion = 0; + 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(segInfo.segName, "__LINKINFO") != 0 ) + return; + // Keep segments 4K or more aligned + offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12)); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12); + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &_readOnlyRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + }); + } + + // Align the end of the __LINKINFO + offsetInRegion = align(offsetInRegion, 14); + _nonLinkEditReadOnlySize = offsetInRegion; + + // Do all __LINKEDIT, regardless of split seg + 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(segInfo.segName, "__LINKEDIT") != 0 ) + return; + // Keep segments 4K or more aligned + offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12)); + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections); + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = nullptr; + loc.dstCacheUnslidAddress = offsetInRegion; // This will be updated later once we've assigned addresses + loc.dstCacheFileOffset = (uint32_t)offsetInRegion; + loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12); + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + loc.parentRegion = &_readOnlyRegion; + dylib.cacheLocation[segInfo.segIndex] = loc; + offsetInRegion += loc.dstCacheSegmentSize; + }); + } + + // align r/o region end + _readOnlyRegion.bufferSize = align(offsetInRegion, 14); + _readOnlyRegion.sizeInUse = _readOnlyRegion.bufferSize; + _readOnlyRegion.initProt = VM_PROT_READ; + _readOnlyRegion.maxProt = VM_PROT_READ; + _readOnlyRegion.name = "__LINKEDIT"; + + // Add space in __LINKEDIT for chained fixups and classic relocs + { + + // The pageableKC (and sometimes auxKC) has 1 LC_DYLD_CHAINED_FIXUPS per kext + // while other KCs have 1 for the whole KC. + // It also tracks each segment in each kext for chained fixups, not the segments on the KC itself + __block uint64_t numSegmentsForChainedFixups = 0; + uint64_t numChainedFixupHeaders = 0; + if ( fixupsArePerKext() ) { + for (DylibInfo& dylib : sortedDylibs) { + dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + ++numSegmentsForChainedFixups; + }); + } + numChainedFixupHeaders = sortedDylibs.size(); + + // Branch stubs need fixups on the GOTs region. So add in a top-level chained fixup entry + // and for now all the regions as we don't know what segment index the branch GOTs will be + numSegmentsForChainedFixups += numRegions(); + numChainedFixupHeaders++; + } else { + numSegmentsForChainedFixups = numRegions(); + numChainedFixupHeaders = 1; + } + + uint64_t numBytesForPageStarts = 0; + if ( dataConstRegion.sizeInUse != 0 ) + numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(dataConstRegion.bufferSize)); + if ( branchGOTsRegion.bufferSize != 0 ) + numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(branchGOTsRegion.bufferSize)); + if ( readWriteRegion.sizeInUse != 0 ) + numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(readWriteRegion.bufferSize)); + if ( hibernateRegion.sizeInUse != 0 ) + numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(hibernateRegion.bufferSize)); + for (const Region& region : nonSplitSegRegions) { + // Assume writable regions have fixups to emit + // Note, third party kext's have __TEXT fixups, so assume all of these have fixups + // LINKEDIT is already elsewhere + numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(region.bufferSize)); + } + + uint64_t numBytesForChainedFixups = 0; + if ( numBytesForPageStarts != 0 ) { + numBytesForChainedFixups = numBytesForPageStarts; + numBytesForChainedFixups += sizeof(dyld_chained_fixups_header) * numChainedFixupHeaders; + numBytesForChainedFixups += sizeof(dyld_chained_starts_in_image) * numChainedFixupHeaders; + numBytesForChainedFixups += sizeof(uint32_t) * numSegmentsForChainedFixups; + } + + __block uint64_t numBytesForClassicRelocs = 0; + if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) { + if ( const DylibInfo* dylib = getKernelStaticExecutableInputFile() ) { + if ( dylib->input->mappedFile.mh->usesClassicRelocationsInKernelCollection() ) { + dylib->input->mappedFile.mh->forEachRebase(_diagnostics, false, ^(uint64_t runtimeOffset, bool &stop) { + numBytesForClassicRelocs += sizeof(relocation_info); + }); + } + } + } + + // align fixups region end + if ( (numBytesForChainedFixups != 0) || (numBytesForClassicRelocs != 0) ) { + uint64_t numBytes = align(numBytesForChainedFixups, 3) + align(numBytesForClassicRelocs, 3); + fixupsSubRegion.bufferSize = align(numBytes, 14); + fixupsSubRegion.sizeInUse = fixupsSubRegion.bufferSize; + fixupsSubRegion.initProt = VM_PROT_READ; + fixupsSubRegion.maxProt = VM_PROT_READ; + fixupsSubRegion.name = "__FIXUPS"; + } + } +} + +void AppCacheBuilder::assignSegmentAddresses() { + // Segments already have offsets in to their regions. Now assign the regions their addresses + // in the full allocated buffer, and then assign all segments in those regions + for (DylibInfo& dylib : sortedDylibs) { + for (SegmentMappingInfo& loc : dylib.cacheLocation) { + loc.dstSegment = loc.parentRegion->buffer + loc.dstCacheFileOffset; + loc.dstCacheUnslidAddress = loc.parentRegion->unslidLoadAddress + loc.dstCacheFileOffset; + loc.dstCacheFileOffset = (uint32_t)loc.parentRegion->cacheFileOffset + loc.dstCacheFileOffset; + } + } +} + +void AppCacheBuilder::copyRawSegments() { + const bool log = false; + + // Call the base class to copy segment data + CacheBuilder::copyRawSegments(); + + // The copy any custom sections + for (const CustomSegment& segment : customSegments) { + for (const CustomSegment::CustomSection& section : segment.sections) { + uint8_t* dstBuffer = segment.parentRegion->buffer + section.offsetInRegion; + uint64_t dstVMAddr = segment.parentRegion->unslidLoadAddress + section.offsetInRegion; + if (log) fprintf(stderr, "copy %s segment %s %s (0x%08lX bytes) from %p to %p (logical addr 0x%llX)\n", + _options.archs->name(), segment.segmentName.c_str(), section.sectionName.c_str(), + section.data.size(), section.data.data(), dstBuffer, dstVMAddr); + ::memcpy(dstBuffer, section.data.data(), section.data.size()); + } + } +} + +static uint8_t getFixupLevel(AppCacheBuilder::Options::AppCacheKind kind) { + uint8_t currentLevel = (uint8_t)~0U; + switch (kind) { + case AppCacheBuilder::Options::AppCacheKind::none: + assert(0 && "Cache kind should have been set"); + break; + case AppCacheBuilder::Options::AppCacheKind::kernel: + currentLevel = 0; + break; + case AppCacheBuilder::Options::AppCacheKind::pageableKC: + // The pageableKC sits right above the baseKC which is level 0 + currentLevel = 1; + break; + case AppCacheBuilder::Options::AppCacheKind::kernelCollectionLevel2: + assert(0 && "Unimplemented"); + break; + case AppCacheBuilder::Options::AppCacheKind::auxKC: + currentLevel = 3; + break; + } + return currentLevel; +} + +uint32_t AppCacheBuilder::getCurrentFixupLevel() const { + return getFixupLevel(appCacheOptions.cacheKind); +} + +struct VTableBindSymbol { + std::string_view binaryID; + std::string symbolName; +}; + +// For every dylib, lets make a map from its exports to its defs +struct DylibSymbols { + // Define a bunch of constructors so that we know we are getting move constructors not copies + DylibSymbols() = default; + DylibSymbols(const DylibSymbols&) = delete; + DylibSymbols(DylibSymbols&&) = default; + DylibSymbols(std::map&& globals, + std::map&& locals, + std::unique_ptr> kpiSymbols, + uint32_t dylibLevel, const std::string& dylibName) + : globals(std::move(globals)), locals(std::move(locals)), kpiSymbols(std::move(kpiSymbols)), + dylibLevel(dylibLevel), dylibName(dylibName) { } + + DylibSymbols& operator=(const DylibSymbols& other) = delete; + DylibSymbols& operator=(DylibSymbols&& other) = default; + + std::map globals; + + // We also need to track locals as vtable patching supports patching with these too + std::map locals; + + // KPI (ie, a symbol set embedded in this binary) + std::unique_ptr> kpiSymbols; + + // Kernel collections can reference each other in levels. This is the level + // of the exported dylib. Eg, the base KC is 0, and the aux KC is 3 + uint32_t dylibLevel = 0; + + // Store the name of the dylib for fast lookups + std::string dylibName; + + // Keep track of the binds in this dylib as these tell us if a vtable slot is to a local + // or external definition of a function + std::unordered_map resolvedBindLocations; +}; + +class VTablePatcher { +public: + + VTablePatcher(uint32_t numFixupLevels); + + bool hasError() const; + + void addKernelCollection(const dyld3::MachOAppCache* cacheMA, AppCacheBuilder::Options::AppCacheKind kind, + const uint8_t* basePointer, uint64_t baseAddress); + void addDylib(Diagnostics& diags, const dyld3::MachOAnalyzer* ma, const std::string& dylibID, + const std::vector& dependencies, uint8_t cacheLevel); + + void findMetaclassDefinitions(std::map& dylibsToSymbols, + const std::string& kernelID, const dyld3::MachOAnalyzer* kernelMA, + AppCacheBuilder::Options::AppCacheKind cacheKind); + void findExistingFixups(Diagnostics& diags, + const dyld3::MachOAppCache* existingKernelCollection, + const dyld3::MachOAppCache* pageableKernelCollection); + void findBaseKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection, + std::map& dylibsToSymbols); + void findPageableKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection, + std::map& dylibsToSymbols); + void findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA, + std::map& dylibsToSymbols, + const AppCacheBuilder::ASLR_Tracker& aslrTracker, + const std::map& missingBindLocations); + void calculateSymbols(); + void patchVTables(Diagnostics& diags, + std::map& missingBindLocations, + AppCacheBuilder::ASLR_Tracker& aslrTracker, + uint8_t currentLevel); + +private: + + void logFunc(const char* format, ...) { + if ( logPatching ) { + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + } + }; + + void logFuncVerbose(const char* format, ...) { + if ( logPatchingVerbose ) { + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + } + }; + + // Extract a substring by dropping optional prefix/suffix + std::string_view extractString(std::string_view str, std::string_view prefix, std::string_view suffix) { + if ( !prefix.empty() ) { + // Make sure we have the prefix we are looking for + if ( str.find(prefix) != 0 ) { + return std::string_view(); + } + str.remove_prefix(prefix.size()); + } + if ( !suffix.empty() ) { + // Make sure we have the prefix we are looking for + size_t pos = str.rfind(suffix); + if ( pos != (str.size() - suffix.size()) ) { + return std::string_view(); + } + str.remove_suffix(suffix.size()); + } + return str; + }; + + struct VTable { + struct Entry { + const uint8_t* location = nullptr; + uint64_t targetVMAddr = ~0ULL; + uint32_t targetCacheLevel = ~0; + // Pointer auth + uint16_t diversity = 0; + bool hasAddrDiv = false; + uint8_t key = 0; + bool hasPointerAuth = false; + }; + + const dyld3::MachOAnalyzer* ma = nullptr; + const uint8_t* superVTable = nullptr; + const DylibSymbols* dylib = nullptr; + bool fromParentCollection = false; + bool patched = false; + std::string name = ""; + std::vector entries; + }; + + struct SymbolLocation { + uint64_t vmAddr = 0; + bool foundSymbol = 0; + + bool found() const { + return foundSymbol; + } + }; + + struct Fixup { + uint64_t targetVMAddr = 0; + uint8_t cacheLevel = 0; + // Pointer auth + uint16_t diversity = 0; + bool hasAddrDiv = false; + uint8_t key = 0; + bool hasPointerAuth = false; + }; + + struct VTableDylib { + Diagnostics* diags = nullptr; + const dyld3::MachOAnalyzer* ma = nullptr; + std::string dylibID = ""; + std::vector dependencies; + uint32_t cacheLevel = ~0U; + }; + + struct KernelCollection { + const dyld3::MachOAppCache* ma = nullptr; + + // We need the base pointers to the buffers for every level + // These are the base of the allocated memory, which corresponds to pointing to the lowest + // vmAddr for the buffer. These do *not* necessarily point to a mach_header + const uint8_t* basePointer = nullptr; + + // We also need the base vm addresses to the buffers for every level + uint64_t baseAddress = ~0ULL; + + std::unordered_map symbolNames; + std::map metaclassDefinitions; + }; + + SymbolLocation findVTablePatchingSymbol(std::string_view symbolName, const DylibSymbols& dylibSymbols); + + std::vector dylibs; + std::map vtables; + std::vector collections; + const uint8_t* baseMetaClassVTableLoc = nullptr; + + // Record all the fixup locations in the base/pageable KCs as we need to use them instead of the ASLR tracker + std::map existingCollectionFixupLocations; + + const uint32_t pointerSize = 8; + const bool logPatching = false; + const bool logPatchingVerbose = false; + + // Magic constants for vtable patching + //const char* cxxPrefix = "__Z"; + const char* vtablePrefix = "__ZTV"; + const char* osObjPrefix = "__ZN"; + // const char* vtableReservedToken = "_RESERVED"; + const char* metaclassToken = "10gMetaClassE"; + const char* superMetaclassPointerToken = "10superClassE"; + const char* metaclassVTablePrefix = "__ZTVN"; + const char* metaclassVTableSuffix = "9MetaClassE"; +}; + +VTablePatcher::VTablePatcher(uint32_t numFixupLevels) { + collections.resize(numFixupLevels); +} + +bool VTablePatcher::hasError() const { + for (const VTableDylib& dylib : dylibs) { + if ( dylib.diags->hasError() ) + return true; + } + return false; +} + +void VTablePatcher::addKernelCollection(const dyld3::MachOAppCache* cacheMA, AppCacheBuilder::Options::AppCacheKind kind, + const uint8_t* basePointer, uint64_t baseAddress) { + uint8_t cacheLevel = getFixupLevel(kind); + + assert(cacheLevel < collections.size()); + assert(collections[cacheLevel].ma == nullptr); + + collections[cacheLevel].ma = cacheMA; + collections[cacheLevel].basePointer = basePointer; + collections[cacheLevel].baseAddress = baseAddress; +} + +void VTablePatcher::addDylib(Diagnostics &diags, const dyld3::MachOAnalyzer *ma, + const std::string& dylibID, const std::vector& dependencies, + uint8_t cacheLevel) { + dylibs.push_back((VTableDylib){ &diags, ma, dylibID, dependencies, cacheLevel }); +} + +VTablePatcher::SymbolLocation VTablePatcher::findVTablePatchingSymbol(std::string_view symbolName, + const DylibSymbols& dylibSymbols) { + // First look in the globals + auto globalsIt = dylibSymbols.globals.find(symbolName); + if ( globalsIt != dylibSymbols.globals.end() ) { + return { globalsIt->second, true }; + } + + // Then again in the locals + auto localsIt = dylibSymbols.locals.find(symbolName); + if ( localsIt != dylibSymbols.locals.end() ) { + return { localsIt->second, true }; + } + + return { ~0ULL, false }; +}; + +void VTablePatcher::findMetaclassDefinitions(std::map& dylibsToSymbols, + const std::string& kernelID, const dyld3::MachOAnalyzer* kernelMA, + AppCacheBuilder::Options::AppCacheKind cacheKind) { + for (VTableDylib& dylib : dylibs) { + auto& metaclassDefinitions = collections[dylib.cacheLevel].metaclassDefinitions; + dylib.ma->forEachGlobalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, + uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + if ( strstr(symbolName, metaclassToken) != nullptr ) + metaclassDefinitions[n_value] = symbolName; + }); + dylib.ma->forEachLocalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, + uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + if ( strstr(symbolName, metaclassToken) != nullptr ) + metaclassDefinitions[n_value] = symbolName; + }); + } + + // Keep track of the root OSMetaClass from which all other metaclasses inherit + DylibSymbols& kernelDylibSymbols = dylibsToSymbols[kernelID]; + SymbolLocation symbolLocation = findVTablePatchingSymbol("__ZTV11OSMetaClass", kernelDylibSymbols); + if ( symbolLocation.found() ) { + baseMetaClassVTableLoc = (uint8_t*)kernelMA + (symbolLocation.vmAddr - kernelMA->preferredLoadAddress()); + + VTable& vtable = vtables[baseMetaClassVTableLoc]; + vtable.ma = kernelMA; + vtable.dylib = &kernelDylibSymbols; + vtable.fromParentCollection = (cacheKind != AppCacheBuilder::Options::AppCacheKind::kernel); + vtable.patched = true; + vtable.name = "__ZTV11OSMetaClass"; + } +} + +void VTablePatcher::findExistingFixups(Diagnostics& diags, + const dyld3::MachOAppCache* existingKernelCollection, + const dyld3::MachOAppCache* pageableKernelCollection) { + + const bool is64 = pointerSize == 8; + + if ( existingKernelCollection != nullptr ) { + uint8_t kernelLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::kernel); + uint64_t kernelBaseAddress = collections[kernelLevel].baseAddress; + const uint8_t* kernelBasePointer = collections[kernelLevel].basePointer; + + // We may have both chained and classic fixups. First add chained + if ( existingKernelCollection->hasChainedFixupsLoadCommand() ) { + existingKernelCollection->withChainStarts(diags, 0, ^(const dyld_chained_starts_in_image* starts) { + existingKernelCollection->forEachFixupInAllChains(diags, starts, false, + ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + uint64_t vmOffset = 0; + bool isRebase = fixupLoc->isRebase(segInfo->pointer_format, kernelBaseAddress, vmOffset); + assert(isRebase); + uint64_t targetVMAddr = kernelBaseAddress + vmOffset; + uint16_t diversity = fixupLoc->kernel64.diversity; + bool hasAddrDiv = fixupLoc->kernel64.addrDiv; + uint8_t key = fixupLoc->kernel64.key; + bool hasPointerAuth = fixupLoc->kernel64.isAuth; + existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, kernelLevel, diversity, hasAddrDiv, key, hasPointerAuth }; + }); + }); + } + + // And add classic if we have them + existingKernelCollection->forEachRebase(diags, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo, + const dyld3::MachOAnalyzer::SegmentInfo *segments, + bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, + uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) { + uint64_t rebaseVmAddr = segments[segmentIndex].vmAddr + segmentOffset; + uint64_t runtimeOffset = rebaseVmAddr - kernelBaseAddress; + const uint8_t* fixupLoc = kernelBasePointer + runtimeOffset; + uint64_t targetVMAddr = 0; + if ( is64 ) { + targetVMAddr = *(uint64_t*)fixupLoc; + } else { + targetVMAddr = *(uint32_t*)fixupLoc; + } + // Classic relocs have no pointer auth + uint16_t diversity = 0; + bool hasAddrDiv = false; + uint8_t key = 0; + bool hasPointerAuth = false; + existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, kernelLevel, diversity, hasAddrDiv, key, hasPointerAuth }; + }); + } + + // Add pageable fixup locations if we have it + if ( pageableKernelCollection != nullptr ) { + // We only have chained fixups here to add, but they are on each kext, not on the KC itself + pageableKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + // Skip kexts without fixups + if ( !ma->hasChainedFixupsLoadCommand() ) + return; + ma->withChainStarts(diags, 0, ^(const dyld_chained_starts_in_image* starts) { + ma->forEachFixupInAllChains(diags, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + uint64_t vmOffset = 0; + bool isRebase = fixupLoc->isRebase(DYLD_CHAINED_PTR_64_KERNEL_CACHE, 0, vmOffset); + assert(isRebase); + uint8_t targetFixupLevel = fixupLoc->kernel64.cacheLevel; + uint64_t targetVMAddr = collections[targetFixupLevel].baseAddress + vmOffset; + uint16_t diversity = fixupLoc->kernel64.diversity; + bool hasAddrDiv = fixupLoc->kernel64.addrDiv; + uint8_t key = fixupLoc->kernel64.key; + bool hasPointerAuth = fixupLoc->kernel64.isAuth; + existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, targetFixupLevel, diversity, hasAddrDiv, key, hasPointerAuth }; + }); + }); + }); + } +} + +void VTablePatcher::findBaseKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection, + std::map& dylibsToSymbols) +{ + const bool is64 = pointerSize == 8; + + uint8_t kernelLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::kernel); + uint64_t kernelBaseAddress = collections[kernelLevel].baseAddress; + const uint8_t* kernelBasePointer = collections[kernelLevel].basePointer; + uint16_t chainedPointerFormat = 0; + + if ( existingKernelCollection->hasChainedFixupsLoadCommand() ) + chainedPointerFormat = existingKernelCollection->chainedPointerFormat(); + + // Map from dylibID to list of dependencies + std::map*> kextDependencies; + for (VTableDylib& dylib : dylibs) { + if ( dylib.cacheLevel != kernelLevel ) + continue; + kextDependencies[dylib.dylibID] = &dylib.dependencies; + } + + bool kernelUsesClassicRelocs = existingKernelCollection->usesClassicRelocationsInKernelCollection(); + existingKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) { + uint64_t loadAddress = ma->preferredLoadAddress(); + + auto visitBaseKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) { + if ( strstr(symbolName, superMetaclassPointerToken) == nullptr ) + return; + uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress); + logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID, fixupLoc); + + // 2 - Derive the name of the class from the super MetaClass pointer. + std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( className.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Class name: '%s'\n", std::string(className).c_str()); + + // 3 - Derive the name of the class's vtable from the name of the class + // We support namespaces too which means adding an N before the class name and E after + std::string classVTableName = std::string(vtablePrefix) + std::string(className); + logFunc("Class vtable name: '%s'\n", classVTableName.c_str()); + + uint64_t classVTableVMAddr = 0; + const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID]; + { + std::string namespacedVTableName; + SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols); + if ( !symbolLocation.found() ) { + // If we didn't find a name then try again with namespaces + namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E"; + logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str()); + symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols); + } + if ( symbolLocation.found() ) { + classVTableVMAddr = symbolLocation.vmAddr; + } else { + diags.error("Class vtables '%s' or '%s' is not exported from '%s'", + classVTableName.c_str(), namespacedVTableName.c_str(), dylibID); + stop = true; + return; + } + } + + logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr); + const uint8_t* classVTableLoc = kernelBasePointer + (classVTableVMAddr - kernelBaseAddress); + + // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol + uint64_t superMetaclassSymbolAddress = 0; + auto existingKernelCollectionFixupLocIt = existingCollectionFixupLocations.find(fixupLoc); + if ( existingKernelCollectionFixupLocIt != existingCollectionFixupLocations.end() ) { + if ( ma->isKextBundle() || !kernelUsesClassicRelocs ) { + auto* chainedFixupLoc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc; + uint64_t vmOffset = 0; + bool isRebase = chainedFixupLoc->isRebase(chainedPointerFormat, kernelBaseAddress, vmOffset); + assert(isRebase); + superMetaclassSymbolAddress = kernelBaseAddress + vmOffset; + } else { + // The classic reloc is already the vmAddr so nothing special to do here. + assert(is64); + superMetaclassSymbolAddress = *(uint64_t*)fixupLoc; + } + } + + logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress); + + if ( superMetaclassSymbolAddress == 0 ) { + if ( classVTableName == "__ZTV8OSObject" ) { + // This is the base class of all objects, so it doesn't have a super class + // We add it as a placeholder and set it to 'true' to show its already been processed + VTable& vtable = vtables[classVTableLoc]; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = true; + vtable.patched = true; + vtable.name = classVTableName; + return; + } + } + + // 5 - Look up the super MetaClass symbol by address + // FIXME: VTable patching the auxKC with the superclass in the baseKC + uint8_t superclassFixupLevel = kernelLevel; + + auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions; + auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress); + if ( metaclassIt == metaclassDefinitions.end() ) { + diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'", + symbolName, dylibID); + stop = true; + return; + } + + // 6 - Derive the super class's name from the super MetaClass name + std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( superClassName.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str()); + + // 7 - Derive the super class's vtable from the super class's name + std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + + // We support namespaces, so first try the superclass without the namespace, then again with it + const uint8_t* superclassVTableLoc = nullptr; + for (unsigned i = 0; i != 2; ++i) { + if ( i == 1 ) { + superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E"; + } + logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str()); + + if ( ma->isKextBundle() ) { + // First check if the superclass vtable comes from a dependent kext + auto it = kextDependencies.find(dylibID); + assert(it != kextDependencies.end()); + const std::vector& dependencies = *it->second; + for (const std::string& dependencyID : dependencies) { + auto depIt = dylibsToSymbols.find(dependencyID); + if (depIt == dylibsToSymbols.end()) { + diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id", + symbolName, dylibID, dependencyID.c_str()); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( !symbolLocation.found() ) + continue; + + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr); + superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress); + break; + } + } + if ( superclassVTableLoc == nullptr ) { + auto depIt = dylibsToSymbols.find(dylibID); + if (depIt == dylibsToSymbols.end()) { + diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id", + symbolName, dylibID, dylibID); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( symbolLocation.found() ) { + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr); + superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress); + } + } + + if ( superclassVTableLoc != nullptr ) + break; + } + + if ( superclassVTableLoc == nullptr ) { + superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies", + superclassVTableName.c_str(), dylibID); + stop = true; + return; + } + + // Add an entry for this vtable + VTable& vtable = vtables[classVTableLoc]; + vtable.superVTable = superclassVTableLoc; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = true; + vtable.patched = true; + vtable.name = classVTableName; + + // And an entry for the superclass vtable + VTable& supervtable = vtables[superclassVTableLoc]; + supervtable.fromParentCollection = true; + supervtable.patched = true; + supervtable.name = superclassVTableName; + }; + + ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitBaseKernelCollectionSymbols(symbolName, n_value); + }); + + if ( diags.hasError() ) { + stop = true; + return; + } + + ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitBaseKernelCollectionSymbols(symbolName, n_value); + }); + + if ( diags.hasError() ) { + stop = true; + return; + } + }); +} + +void VTablePatcher::findPageableKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* pageableKernelCollection, + std::map& dylibsToSymbols) +{ + uint8_t collectionLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::pageableKC); + uint64_t collectionBaseAddress = collections[collectionLevel].baseAddress; + const uint8_t* collectionBasePointer = collections[collectionLevel].basePointer; + + // Map from dylibID to list of dependencies + std::map*> kextDependencies; + for (VTableDylib& dylib : dylibs) { + if ( dylib.cacheLevel != collectionLevel ) + continue; + kextDependencies[dylib.dylibID] = &dylib.dependencies; + } + + pageableKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) { + uint64_t loadAddress = ma->preferredLoadAddress(); + auto visitPageableKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) { + if ( strstr(symbolName, superMetaclassPointerToken) == nullptr ) + return; + uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress); + logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID, fixupLoc); + + // 2 - Derive the name of the class from the super MetaClass pointer. + std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( className.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Class name: '%s'\n", std::string(className).c_str()); + + // 3 - Derive the name of the class's vtable from the name of the class + // We support namespaces too which means adding an N before the class name and E after + std::string classVTableName = std::string(vtablePrefix) + std::string(className); + logFunc("Class vtable name: '%s'\n", classVTableName.c_str()); + + uint64_t classVTableVMAddr = 0; + const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID]; + { + std::string namespacedVTableName; + SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols); + if ( !symbolLocation.found() ) { + // If we didn't find a name then try again with namespaces + namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E"; + logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str()); + symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols); + } + if ( symbolLocation.found() ) { + classVTableVMAddr = symbolLocation.vmAddr; + } else { + diags.error("Class vtables '%s' or '%s' is not exported from '%s'", + classVTableName.c_str(), namespacedVTableName.c_str(), dylibID); + stop = true; + return; + } + } + + logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr); + const uint8_t* classVTableLoc = collectionBasePointer + (classVTableVMAddr - collectionBaseAddress); + + // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol + uint8_t superclassFixupLevel = (uint8_t)~0U; + uint64_t superMetaclassSymbolAddress = 0; + auto existingKernelCollectionFixupLocIt = existingCollectionFixupLocations.find(fixupLoc); + if ( existingKernelCollectionFixupLocIt != existingCollectionFixupLocations.end() ) { + auto* chainedFixupLoc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc; + uint64_t vmOffset = 0; + bool isRebase = chainedFixupLoc->isRebase(DYLD_CHAINED_PTR_64_KERNEL_CACHE, 0, vmOffset); + assert(isRebase); + // The superclass could be in the baseKC, while we are analysing the pageableKC, so we need to get the correct level + // from the fixup + superclassFixupLevel = chainedFixupLoc->kernel64.cacheLevel; + superMetaclassSymbolAddress = collections[superclassFixupLevel].baseAddress + vmOffset; + } + + logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress); + + if ( superMetaclassSymbolAddress == 0 ) { + if ( classVTableName == "__ZTV8OSObject" ) { + // This is the base class of all objects, so it doesn't have a super class + // We add it as a placeholder and set it to 'true' to show its already been processed + VTable& vtable = vtables[classVTableLoc]; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = true; + vtable.patched = true; + vtable.name = classVTableName; + return; + } + } + + // 5 - Look up the super MetaClass symbol by address + auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions; + auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress); + if ( metaclassIt == metaclassDefinitions.end() ) { + diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'", + symbolName, dylibID); + stop = true; + return; + } + + // 6 - Derive the super class's name from the super MetaClass name + std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( superClassName.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str()); + + // 7 - Derive the super class's vtable from the super class's name + std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + + // We support namespaces, so first try the superclass without the namespace, then again with it + const uint8_t* superclassVTableLoc = nullptr; + for (unsigned i = 0; i != 2; ++i) { + if ( i == 1 ) { + superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E"; + } + logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str()); + + if ( ma->isKextBundle() ) { + // First check if the superclass vtable comes from a dependent kext + auto it = kextDependencies.find(dylibID); + assert(it != kextDependencies.end()); + const std::vector& dependencies = *it->second; + for (const std::string& dependencyID : dependencies) { + auto depIt = dylibsToSymbols.find(dependencyID); + if (depIt == dylibsToSymbols.end()) { + diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id", + symbolName, dylibID, dependencyID.c_str()); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( !symbolLocation.found() ) + continue; + + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr); + superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress); + break; + } + } + if ( superclassVTableLoc == nullptr ) { + auto depIt = dylibsToSymbols.find(dylibID); + if (depIt == dylibsToSymbols.end()) { + diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id", + symbolName, dylibID, dylibID); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( symbolLocation.found() ) { + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr); + superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress); + } + } + + if ( superclassVTableLoc != nullptr ) + break; + } + + if ( superclassVTableLoc == nullptr ) { + superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies", + superclassVTableName.c_str(), dylibID); + stop = true; + return; + } + + // Add an entry for this vtable + VTable& vtable = vtables[classVTableLoc]; + vtable.superVTable = superclassVTableLoc; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = true; + vtable.patched = true; + vtable.name = classVTableName; + + // And an entry for the superclass vtable + VTable& supervtable = vtables[superclassVTableLoc]; + supervtable.fromParentCollection = true; + supervtable.patched = true; + supervtable.name = superclassVTableName; + }; + + ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitPageableKernelCollectionSymbols(symbolName, n_value); + }); + + if ( diags.hasError() ) { + stop = true; + return; + } + + ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitPageableKernelCollectionSymbols(symbolName, n_value); + }); + + if ( diags.hasError() ) { + stop = true; + return; + } + }); +} + +void VTablePatcher::findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA, + std::map& dylibsToSymbols, + const AppCacheBuilder::ASLR_Tracker& aslrTracker, + const std::map& missingBindLocations) +{ + const bool is64 = pointerSize == 8; + + uint64_t collectionBaseAddress = collections[currentLevel].baseAddress; + const uint8_t* collectionBasePointer = collections[currentLevel].basePointer; + + // VTable patching algorithm (for each symbol...): + // - To find the address of a class vtable: + // - Take symbols with '10superClassE' in their name, eg, __ZN10IOMachPort10superClassE + // - Work out the name of the class from that symbol name, eg, 10IOMachPort + // - Work out the name of the VTable from that class name, eg, __ZTV10IOMachPort + // - Find the address for the export with that vtable name + // - To find the superclass for a given class + // - Take the symbol with '10superClassE' in their name, eg, __ZN10IOMachPort10superClassE + // - Take its address and dereference it as "__ZN10IOMachPort10superClassE = &__ZN8OSObject10gMetaClassE" + // - Find the name of the symbol at this address, eg, work out we have a symbol called __ZN8OSObject10gMetaClassE + // - Get the superclassic from that symbol name, eg, 8OSObject + // - Get the VTable name from that symbol, eg, __ZTV8OSObject + // - Find the superclass vtable address from that name by searching the image and dependents for __ZTV8OSObject + for (VTableDylib& dylib : dylibs) { + // Only process dylibs in the level we are building + // Existing collections were handled elsewhere + if ( dylib.cacheLevel != currentLevel ) + continue; + + const dyld3::MachOAnalyzer* ma = dylib.ma; + const std::string& dylibID = dylib.dylibID; + Diagnostics& dylibDiags = *dylib.diags; + const std::vector& dependencies = dylib.dependencies; + + uint64_t loadAddress = ma->preferredLoadAddress(); + bool alreadyPatched = (ma == kernelMA); + auto visitSymbols = ^(const char *symbolName, uint64_t n_value) { + if ( strstr(symbolName, superMetaclassPointerToken) == nullptr ) + return; + + uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress); + logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID.c_str(), fixupLoc); + + // 2 - Derive the name of the class from the super MetaClass pointer. + std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( className.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Class name: '%s'\n", std::string(className).c_str()); + + // 3 - Derive the name of the class's vtable from the name of the class + // We support namespaces too which means adding an N before the class name and E after + std::string classVTableName = std::string(vtablePrefix) + std::string(className); + logFunc("Class vtable name: '%s'\n", classVTableName.c_str()); + + uint64_t classVTableVMAddr = 0; + const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID]; + { + std::string namespacedVTableName; + SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols); + if ( !symbolLocation.found() ) { + // If we didn't find a name then try again with namespaces + namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E"; + logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str()); + symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols); + } + if ( symbolLocation.found() ) { + classVTableVMAddr = symbolLocation.vmAddr; + } else { + dylibDiags.error("Class vtables '%s' or '%s' is not an exported symbol", + classVTableName.c_str(), namespacedVTableName.c_str()); + return; + } + } + + logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr); + const uint8_t* classVTableLoc = (uint8_t*)ma + (classVTableVMAddr - loadAddress); + + // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol + uint64_t superMetaclassSymbolAddress = 0; + { + uint32_t vmAddr32 = 0; + uint64_t vmAddr64 = 0; + if ( aslrTracker.hasRebaseTarget32(fixupLoc, &vmAddr32) ) { + superMetaclassSymbolAddress = vmAddr32; + } else if ( aslrTracker.hasRebaseTarget64(fixupLoc, &vmAddr64) ) { + superMetaclassSymbolAddress = vmAddr64; + } else { + assert(is64); + superMetaclassSymbolAddress = *(uint64_t*)fixupLoc; + } + uint8_t highByte = 0; + if ( aslrTracker.hasHigh8(fixupLoc, &highByte) ) { + uint64_t tbi = (uint64_t)highByte << 56; + superMetaclassSymbolAddress |= tbi; + } + } + logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress); + + if ( superMetaclassSymbolAddress == 0 ) { + if ( classVTableName == "__ZTV8OSObject" ) { + // This is the base class of all objects, so it doesn't have a super class + // We add it as a placeholder and set it to 'true' to show its already been processed + VTable& vtable = vtables[classVTableLoc]; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = false; + vtable.patched = true; + vtable.name = classVTableName; + return; + } + } + + // 5 - Look up the super MetaClass symbol by address + // FIXME: VTable patching the auxKC with the superclass in the baseKC + uint8_t superclassFixupLevel = currentLevel; + aslrTracker.has(fixupLoc, &superclassFixupLevel); + + auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions; + auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress); + if ( metaclassIt == metaclassDefinitions.end() ) { + auto bindIt = missingBindLocations.find(fixupLoc); + if ( bindIt != missingBindLocations.end() ) { + dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'. " + "Expected symbol '%s' to be defined in another kext", + symbolName, bindIt->second.symbolName.c_str()); + } else { + dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'", + symbolName); + } + return; + } + + // 6 - Derive the super class's name from the super MetaClass name + std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken); + // If the string isn't prefixed/suffixed appropriately, then give up on this one + if ( superClassName.empty() ) { + logFunc("Unsupported vtable superclass name\n"); + return; + } + logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str()); + + // 7 - Derive the super class's vtable from the super class's name + std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + + // We support namespaces, so first try the superclass without the namespace, then again with it + const uint8_t* superclassVTableLoc = nullptr; + bool superVTableIsInParentCollection = false; + for (unsigned i = 0; i != 2; ++i) { + if ( i == 1 ) { + superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E"; + } + logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str()); + + { + // First check if the superclass vtable comes from a dependent kext + for (const std::string& dependencyID : dependencies) { + auto depIt = dylibsToSymbols.find(dependencyID); + if (depIt == dylibsToSymbols.end()) { + dylibDiags.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id", + symbolName, dependencyID.c_str()); + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( !symbolLocation.found() ) + continue; + + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr); + superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress); + superVTableIsInParentCollection = dylibSymbols.dylibLevel != currentLevel; + break; + } + + if ( superclassVTableLoc == nullptr ) { + SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols); + if ( symbolLocation.found() ) { + uint64_t superclassVTableVMAddr = symbolLocation.vmAddr; + superclassVTableLoc = (uint8_t*)collectionBasePointer + (superclassVTableVMAddr - collectionBaseAddress); + superVTableIsInParentCollection = false; + } + } + } + + if ( superclassVTableLoc != nullptr ) + break; + } + + if ( superclassVTableLoc == nullptr ) { + superclassVTableName = std::string(vtablePrefix) + std::string(superClassName); + dylibDiags.error("Superclass vtable '%s' is not exported from kext or its dependencies", + superclassVTableName.c_str()); + return; + } + + // Add an entry for this vtable + VTable& vtable = vtables[classVTableLoc]; + vtable.superVTable = superclassVTableLoc; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = false; + vtable.patched |= alreadyPatched; + vtable.name = classVTableName; + + // And an entry for the superclass vtable + VTable& supervtable = vtables[superclassVTableLoc]; + supervtable.fromParentCollection = superVTableIsInParentCollection; + supervtable.patched |= alreadyPatched; + supervtable.name = superclassVTableName; + + // Also calculate the metaclass vtable name so that we can patch it + std::string metaclassVTableName = std::string(metaclassVTablePrefix) + std::string(className) + metaclassVTableSuffix; + logFunc("Metaclass vtable name: '%s'\n", metaclassVTableName.c_str()); + + { + // Note its safe to just ignore missing metaclass symbols if we can't find them + // If the binary links then kxld would have let it run + SymbolLocation symbolLocation = findVTablePatchingSymbol(metaclassVTableName, dylibSymbols); + if ( symbolLocation.found() ) { + uint64_t metaclassVTableVMAddr = symbolLocation.vmAddr; + + logFunc("Metaclass vtable vmAddr: '0x%llx'\n", metaclassVTableVMAddr); + uint8_t* metaclassVTableLoc = (uint8_t*)ma + (metaclassVTableVMAddr - loadAddress); + + // Add an entry for this vtable + VTable& vtable = vtables[metaclassVTableLoc]; + vtable.superVTable = baseMetaClassVTableLoc; + vtable.ma = ma; + vtable.dylib = &dylibSymbols; + vtable.fromParentCollection = false; + vtable.patched |= alreadyPatched; + vtable.name = metaclassVTableName; + } + } + }; + + ma->forEachGlobalSymbol(dylibDiags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitSymbols(symbolName, n_value); + }); + + ma->forEachLocalSymbol(dylibDiags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + visitSymbols(symbolName, n_value); + }); + } +} + +void VTablePatcher::calculateSymbols() { + for (VTableDylib& dylib : dylibs) { + auto& symbolNames = collections[dylib.cacheLevel].symbolNames; + dylib.ma->forEachGlobalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + symbolNames[n_value] = symbolName; + }); + dylib.ma->forEachLocalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool &stop) { + symbolNames[n_value] = symbolName; + }); + } +} + +void VTablePatcher::patchVTables(Diagnostics& diags, + std::map& missingBindLocations, + AppCacheBuilder::ASLR_Tracker& aslrTracker, + uint8_t currentLevel) +{ + const bool is64 = pointerSize == 8; + + // If we have vtables to patch, then make sure we found the OSMetaClass symbol to patch against + if ( (baseMetaClassVTableLoc == nullptr) && !vtables.empty() ) { + diags.error("Could not find OSMetaClass vtable in kernel binary"); + return; + } + + calculateSymbols(); + + auto calculateVTableEntries = ^(const uint8_t* vtableLoc, VTable& vtable) { + assert(vtable.patched); + logFunc("Calculating vtable: '%s'\n", vtable.name.c_str()); + + // The first entry we want to patch is 2 pointers from the start of the vtable + const uint8_t* relocLoc = vtableLoc + (2 * pointerSize); + + if ( vtable.fromParentCollection ) { + auto it = existingCollectionFixupLocations.find(relocLoc); + while ( it != existingCollectionFixupLocations.end() ) { + const Fixup& fixup = it->second; + uint64_t targetVMAddr = fixup.targetVMAddr; + uint16_t diversity = fixup.diversity; + bool hasAddrDiv = fixup.hasAddrDiv; + uint8_t key = fixup.key; + bool hasPointerAuth = fixup.hasPointerAuth; + uint32_t cacheLevel = fixup.cacheLevel; + vtable.entries.push_back({ relocLoc, targetVMAddr, cacheLevel, diversity, hasAddrDiv, key, hasPointerAuth }); + relocLoc += pointerSize; + it = existingCollectionFixupLocations.find(relocLoc); + } + } else { + while ( aslrTracker.has((void*)relocLoc) || + (missingBindLocations.find(relocLoc) != missingBindLocations.end()) ) { + + uint16_t diversity = 0; + bool hasAddrDiv = false; + uint8_t key = 0; + bool hasPointerAuth = false; + uint8_t cacheLevel = currentLevel; + + if ( aslrTracker.has((void*)relocLoc, &cacheLevel) ) { + hasPointerAuth = aslrTracker.hasAuthData((void*)relocLoc, &diversity, &hasAddrDiv, &key); + } + + uint64_t targetVMAddr = 0; + { + uint32_t vmAddr32 = 0; + uint64_t vmAddr64 = 0; + if ( aslrTracker.hasRebaseTarget32((void*)relocLoc, &vmAddr32) ) { + targetVMAddr = vmAddr32; + } else if ( aslrTracker.hasRebaseTarget64((void*)relocLoc, &vmAddr64) ) { + targetVMAddr = vmAddr64; + } else { + assert(is64); + targetVMAddr = *(uint64_t*)relocLoc; + } + uint8_t highByte = 0; + if ( aslrTracker.hasHigh8((void*)relocLoc, &highByte) ) { + uint64_t tbi = (uint64_t)highByte << 56; + targetVMAddr |= tbi; + } + } + + vtable.entries.push_back({ relocLoc, targetVMAddr, cacheLevel, diversity, hasAddrDiv, key, hasPointerAuth }); + relocLoc += pointerSize; + } + } + + logFunc("Found %d vtable items: '%s'\n", vtable.entries.size(), vtable.name.c_str()); + }; + + // Map from MachO to diagnostics to emit for that file + std::unordered_map diagsMap; + for (VTableDylib& dylib : dylibs) + diagsMap[dylib.ma] = dylib.diags; + + uint32_t numPatchedVTables = 0; + for (auto& vtableEntry : vtables) { + if ( vtableEntry.second.patched ) { + calculateVTableEntries(vtableEntry.first, vtableEntry.second); + ++numPatchedVTables; + } + } + while ( numPatchedVTables != vtables.size() ) { + typedef std::pair VTableEntry; + std::vector toBePatched; + for (auto& vtableEntry : vtables) { + if ( vtableEntry.second.patched ) + continue; + auto superIt = vtables.find(vtableEntry.second.superVTable); + assert(superIt != vtables.end()); + if ( !superIt->second.patched ) + continue; + logFunc("Found unpatched vtable: '%s' with patched superclass '%s'\n", + vtableEntry.second.name.c_str(), superIt->second.name.c_str()); + toBePatched.push_back({ vtableEntry.first, &vtableEntry.second }); + } + + if ( toBePatched.empty() ) { + // If we can't find anything to patch, then print out what we have left + for (const auto& vtableEntry : vtables) { + if ( vtableEntry.second.patched ) + continue; + auto superIt = vtables.find(vtableEntry.second.superVTable); + assert(superIt != vtables.end()); + diags.error("Found unpatched vtable: '%s' with unpatched superclass '%s'\n", + vtableEntry.second.name.c_str(), superIt->second.name.c_str()); + } + break; + } + + for (VTableEntry& vtableEntry : toBePatched) { + VTable& vtable = *vtableEntry.second; + + // We can immediately mark this as patched as then calculateVTableEntries can make + // sure we never ask for vtables which aren't ready yet + vtable.patched = true; + ++numPatchedVTables; + + auto superIt = vtables.find(vtable.superVTable); + logFunc("Processing unpatched vtable: '%s' with patched superclass '%s'\n", + vtable.name.c_str(), superIt->second.name.c_str()); + + calculateVTableEntries(vtableEntry.first, vtable); + + const VTable& supervtable = superIt->second; + if ( vtable.entries.size() < supervtable.entries.size() ) { + // Try emit the error to a per dylib diagnostic object if we can find one + auto diagIt = diagsMap.find(vtable.ma); + Diagnostics* diag = (diagIt != diagsMap.end()) ? diagIt->second : &diags; + diag->error("Malformed vtable. Super class '%s' has %lu entries vs subclass '%s' with %lu entries", + supervtable.name.c_str(), supervtable.entries.size(), + vtable.name.c_str(), vtable.entries.size()); + return; + } + + const std::unordered_map& resolvedBindLocations = vtable.dylib->resolvedBindLocations; + for (uint64_t entryIndex = 0; entryIndex != supervtable.entries.size(); ++entryIndex) { + logFuncVerbose("Processing entry %lld: super[0x%llx] vs subclass[0x%llx]\n", entryIndex, + *(uint64_t*)supervtable.entries[entryIndex].location, + *(uint64_t*)vtable.entries[entryIndex].location); + + VTable::Entry& vtableEntry = vtable.entries[entryIndex]; + const VTable::Entry& superVTableEntry = supervtable.entries[entryIndex]; + + const uint8_t* patchLoc = vtableEntry.location; + uint64_t targetVMAddr = superVTableEntry.targetVMAddr; + + // 1) If the symbol is defined locally, do not patch + // This corresponds to a rebase not a bind, so if we have a match in our bind set + // we were bound to another image, and should see if that bind should be overridden by a + // better vtable patch. + auto resolvedBindIt = resolvedBindLocations.find(patchLoc); + auto unresolvedBindIt = missingBindLocations.find(patchLoc); + if ( (resolvedBindIt == resolvedBindLocations.end()) && (unresolvedBindIt == missingBindLocations.end()) ) + continue; + + // Find the child and parent symbols, if any + const char* childSymbolName = nullptr; + const char* parentSymbolName = nullptr; + + if ( resolvedBindIt != resolvedBindLocations.end() ) { + childSymbolName = resolvedBindIt->second.symbolName.c_str(); + } else { + assert(unresolvedBindIt != missingBindLocations.end()); + childSymbolName = unresolvedBindIt->second.symbolName.c_str(); + } + + auto& symbolNames = collections[superVTableEntry.targetCacheLevel].symbolNames; + auto parentNameIt = symbolNames.find(superVTableEntry.targetVMAddr); + if ( parentNameIt != symbolNames.end() ) + parentSymbolName = parentNameIt->second; + + // The child entry can be NULL when a locally-defined, non-external + // symbol is stripped. We wouldn't patch this entry anyway, so we just skip it. + if ( childSymbolName == nullptr ) { + continue; + } + + // It's possible for the patched parent entry not to have a symbol + // (e.g. when the definition is inlined). We can't patch this entry no + // matter what, so we'll just skip it and die later if it's a problem + // (which is not likely). + if ( parentSymbolName == nullptr ) { + continue; + } + + logFuncVerbose("Processing entry %lld: super[%s] vs subclass[%s]\n", entryIndex, + parentSymbolName, childSymbolName); + + // 2) If the child is a pure virtual function, do not patch. + // In general, we want to proceed with patching when the symbol is + // externally defined because pad slots fall into this category. + // The pure virtual function symbol is special case, as the pure + // virtual property itself overrides the parent's implementation. + if ( !strcmp(childSymbolName, "___cxa_pure_virtual") ) { + continue; + } + + // 3) If the symbols are the same, do not patch + // Note that if the symbol was a missing bind, then we'll still patch + // This is the case where the vtable entry itself was a local symbol + // so we had originally failed to bind to it as it wasn't exported, but it + // has the same name as the parent name + if ( !strcmp(childSymbolName, parentSymbolName) && (unresolvedBindIt == missingBindLocations.end()) ) { + continue; + } + +#if 0 + // FIXME: Implement this + + // 4) If the parent vtable entry is a pad slot, and the child does not + // match it, then the child was built against a newer version of the + // libraries, so it is binary-incompatible. + require_action(!kxld_sym_name_is_padslot(parent_entry->patched.name), + finish, rval = KERN_FAILURE; + kxld_log(kKxldLogPatching, kKxldLogErr, + kKxldLogParentOutOfDate, + kxld_demangle(super_vtable->name, &demangled_name1, + &demangled_length1), + kxld_demangle(vtable->name, &demangled_name2, + &demangled_length2))); +#endif + + logFunc("Patching entry '%s' in '%s' to point to '%s' in superclass '%s'\n", + childSymbolName, vtable.name.c_str(), parentSymbolName, supervtable.name.c_str()); + + if ( is64 ) { + *((uint64_t*)patchLoc) = targetVMAddr; + } else { + *((uint32_t*)patchLoc) = (uint32_t)targetVMAddr; + } + + // FIXME: When we support a baseKC, pageableKC, and auxKC, the supervtable cache level + // may no longer be correct here as we may be: + // - patching a vtable in auxKC + // - where the supervtable is in pageableKC + // - but the entry slot points to baseKC + aslrTracker.add((void*)patchLoc, superVTableEntry.targetCacheLevel); + + // Add pointer auth if the super vtable had it + if ( superVTableEntry.hasPointerAuth ) + aslrTracker.setAuthData((void*)patchLoc, superVTableEntry.diversity, + superVTableEntry.hasAddrDiv, superVTableEntry.key); + + // Update this vtable entry in case there are any subclasses which then need to use it + // to be patched themselves + vtableEntry.targetVMAddr = superVTableEntry.targetVMAddr; + vtableEntry.targetCacheLevel = superVTableEntry.targetCacheLevel; + vtableEntry.diversity = superVTableEntry.diversity; + vtableEntry.hasAddrDiv = superVTableEntry.hasAddrDiv; + vtableEntry.key = superVTableEntry.key; + vtableEntry.hasPointerAuth = superVTableEntry.hasPointerAuth; + + missingBindLocations.erase(patchLoc); + } + } + } +} + +typedef std::pair CacheOffset; + +struct DylibSymbolLocation { + const DylibSymbols* dylibSymbols; + uint64_t symbolVMAddr; + bool isKPI; +}; + +struct DylibFixups { + void processFixups(const std::map& dylibsToSymbols, + const std::unordered_map>& symbolMap, + const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker); + + // Inputs + const dyld3::MachOAnalyzer* ma = nullptr; + DylibSymbols& dylibSymbols; + Diagnostics& dylibDiag; + const std::vector& dependencies; + + // Outputs + struct AuthData { + uint16_t diversity; + bool addrDiv; + uint8_t key; + }; + struct BranchStubData { + CacheOffset targetCacheOffset; + const void* fixupLoc; + uint64_t fixupVMOffset; + }; + std::unordered_map missingBindLocations; + std::unordered_map fixupLocs; + std::unordered_map fixupHigh8s; + std::unordered_map fixupAuths; + std::vector branchStubs; +}; + +void DylibFixups::processFixups(const std::map& dylibsToSymbols, + const std::unordered_map>& symbolMap, + const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker) { + auto& resolvedBindLocations = dylibSymbols.resolvedBindLocations; + const std::string& dylibID = dylibSymbols.dylibName; + + const bool _is64 = true; + const bool isThirdPartyKext = (dylibID.find("com.apple") != 0); + + // The magic symbol for missing weak imports + const char* missingWeakImportSymbolName = "_gOSKextUnresolved"; + + struct SymbolDefinition { + uint64_t symbolVMAddr; + uint32_t kernelCollectionLevel; + }; + auto findDependencyWithSymbol = [&symbolMap, &isThirdPartyKext](const char* symbolName, + const std::vector& dependencies) { + auto symbolMapIt = symbolMap.find(symbolName); + if ( symbolMapIt == symbolMap.end() ) + return (SymbolDefinition){ ~0ULL, 0 }; + // Find the first dependency in the list + const std::vector& dylibSymbols = symbolMapIt->second; + // The massively common case is 1 or 2 definitions of a given symbol, so a basic searhc should be + // fine + for (const std::string& dependency : dependencies) { + for (const DylibSymbolLocation& dylibSymbol : dylibSymbols) { + if ( dependency == dylibSymbol.dylibSymbols->dylibName ) { + // If the Apple kext we are linking has a symbol set, and the user is a third-party kext, + // then only allow the third party kext to see symbols in the kext export list, if it has one + const bool isAppleKext = (dependency.find("com.apple") == 0); + if ( isThirdPartyKext && isAppleKext && !dylibSymbol.isKPI ) + continue; + return (SymbolDefinition){ dylibSymbol.symbolVMAddr, dylibSymbol.dylibSymbols->dylibLevel }; + } + } + } + return (SymbolDefinition){ ~0ULL, 0 }; + }; + + if (ma->hasChainedFixups()) { + // build array of targets + struct BindTarget { + const VTableBindSymbol bindSymbol; + uint64_t vmAddr; + uint32_t dylibLevel; + bool isMissingWeakImport; + bool isMissingSymbol; + }; + __block std::vector bindTargets; + __block bool foundMissingWeakImport = false; + ma->forEachChainedFixupTarget(dylibDiag, ^(int libOrdinal, const char* symbolName, uint64_t addend, + bool weakImport, bool& stop) { + if ( (libOrdinal != BIND_SPECIAL_DYLIB_FLAT_LOOKUP) && (libOrdinal != BIND_SPECIAL_DYLIB_WEAK_LOOKUP) ) { + dylibDiag.error("All chained binds should be flat namespace or weak lookups"); + stop = true; + return; + } + + if ( addend != 0 ) { + dylibDiag.error("Chained bind addends are not supported right now"); + stop = true; + return; + } + + VTableBindSymbol bindSymbol = { dylibID, symbolName }; + bool isMissingSymbol = false; + + for (const std::string& dependencyID : dependencies) { + auto depIt = dylibsToSymbols.find(dependencyID); + if (depIt == dylibsToSymbols.end()) { + dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id", + symbolName, dependencyID.c_str()); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + auto exportIt = dylibSymbols.globals.find(symbolName); + if ( exportIt == dylibSymbols.globals.end() ) + continue; + + isMissingSymbol = false; + bindTargets.push_back({ bindSymbol, exportIt->second, dylibSymbols.dylibLevel, false, isMissingSymbol }); + return; + } + + // If the symbol is weak, and we didn't find it in our listed + // dependencies, then use our own definition + if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { + auto dylibIt = dylibsToSymbols.find(dylibID); + if (dylibIt == dylibsToSymbols.end()) { + dylibDiag.error("Failed to bind weak '%s' as could not find a define in self", + symbolName); + stop = true; + return; + } + const DylibSymbols& dylibSymbols = dylibIt->second; + auto exportIt = dylibSymbols.globals.find(symbolName); + if ( exportIt != dylibSymbols.globals.end() ) { + isMissingSymbol = false; + bindTargets.push_back({ bindSymbol, exportIt->second, dylibSymbols.dylibLevel, false, isMissingSymbol }); + return; + } + } + + if ( weakImport ) { + // Find _gOSKextUnresolved in the kernel + // Weak imports are not compared against null, but instead against the address of that symbol + auto kernelSymbolsIt = dylibsToSymbols.find(kernelID); + assert(kernelSymbolsIt != dylibsToSymbols.end()); + const DylibSymbols& kernelSymbols = kernelSymbolsIt->second; + auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName); + if (exportIt != kernelSymbols.globals.end()) { + foundMissingWeakImport = true; + isMissingSymbol = false; + bindTargets.push_back({ bindSymbol, exportIt->second, kernelSymbols.dylibLevel, true, isMissingSymbol }); + return; + } + dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName); + return; + } + + // Store missing binds for later. They may be fixed by vtable patching + isMissingSymbol = true; + bindTargets.push_back({ bindSymbol, 0, 0, false, isMissingSymbol }); + }); + if ( dylibDiag.hasError() ) + return; + + if( foundMissingWeakImport ) { + // If we found a missing weak import, then we need to check that the user did + // something like "if ( &foo == &gOSKextUnresolved )" + // If they didn't use gOSKextUnresolved at all, then there's no way they could be doing that check + auto kernelSymbolsIt = dylibsToSymbols.find(kernelID); + assert(kernelSymbolsIt != dylibsToSymbols.end()); + const DylibSymbols& kernelSymbols = kernelSymbolsIt->second; + auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName); + assert(exportIt != kernelSymbols.globals.end()); + bool foundUseOfMagicSymbol = false; + for (const BindTarget& bindTarget : bindTargets) { + // Skip the missing weak imports + if ( bindTarget.isMissingWeakImport || bindTarget.isMissingSymbol ) + continue; + // Skip anything which isn't the symbol we are looking for + if ( (bindTarget.dylibLevel != 0) && (bindTarget.vmAddr != exportIt->second) ) + continue; + foundUseOfMagicSymbol = true; + break; + } + + if ( !foundUseOfMagicSymbol ) { + dylibDiag.error("Has weak references but does not test for them. " + "Test for weak references with OSKextSymbolIsResolved()."); + return; + } + } + + // uint64_t baseAddress = ma->preferredLoadAddress(); + + ma->withChainStarts(dylibDiag, 0, ^(const dyld_chained_starts_in_image* starts) { + ma->forEachFixupInAllChains(dylibDiag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + switch (segInfo->pointer_format) { + case DYLD_CHAINED_PTR_64_OFFSET: + if ( fixupLoc->generic64.bind.bind ) { + uint64_t bindOrdinal = fixupLoc->generic64.bind.ordinal; + if ( bindOrdinal >= bindTargets.size() ) { + dylibDiag.error("Bind ordinal %lld out of range %lu", bindOrdinal, bindTargets.size()); + stop = true; + return; + } + + const BindTarget& bindTarget = bindTargets[bindOrdinal]; + if ( bindTarget.isMissingSymbol ) { + // Track this missing bind for later + // For now we bind it to null and don't slide it. + fixupLoc->raw64 = 0; + missingBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol; + } else { + fixupLoc->raw64 = bindTarget.vmAddr; + fixupLocs[fixupLoc] = bindTarget.dylibLevel; + resolvedBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol; + } + } + else { + // convert rebase chain entry to raw pointer to target vmaddr + uint64_t targetVMAddr = fixupLoc->generic64.rebase.target; + uint64_t sideTableAddr = 0; + if ( aslrTracker.hasRebaseTarget64(fixupLoc, &sideTableAddr) ) + targetVMAddr = sideTableAddr; + // store high8 in side table + if ( fixupLoc->generic64.rebase.high8 ) + fixupHigh8s[fixupLoc] = fixupLoc->generic64.rebase.high8; + fixupLoc->raw64 = targetVMAddr; + } + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + if ( fixupLoc->arm64e.bind.bind ) { + uint64_t bindOrdinal = fixupLoc->arm64e.bind.ordinal; + if ( bindOrdinal >= bindTargets.size() ) { + dylibDiag.error("Bind ordinal %lld out of range %lu", bindOrdinal, bindTargets.size()); + stop = true; + return; + } + + const BindTarget& bindTarget = bindTargets[bindOrdinal]; + uint64_t targetVMAddr = bindTarget.vmAddr; + + if ( fixupLoc->arm64e.authBind.auth ) { + // store auth data in side table + fixupAuths[fixupLoc] = { + (uint16_t)fixupLoc->arm64e.authBind.diversity, + (bool)fixupLoc->arm64e.authBind.addrDiv, + (uint8_t)fixupLoc->arm64e.authBind.key + }; + } + else { + // plain binds can have addend in chain + targetVMAddr += fixupLoc->arm64e.bind.addend; + } + // change location from a chain ptr into a raw pointer to the target vmaddr + if ( bindTarget.isMissingSymbol ) { + // Track this missing bind for later + // For now we bind it to null and don't slide it. + fixupLoc->raw64 = 0; + missingBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol; + } else { + fixupLoc->raw64 = targetVMAddr; + fixupLocs[fixupLoc] = bindTarget.dylibLevel; + resolvedBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol; + } + } + else { + // convert rebase chain entry to raw pointer to target vmaddr + if ( fixupLoc->arm64e.rebase.auth ) { + // store auth data in side table + fixupAuths[fixupLoc] = { + (uint16_t)fixupLoc->arm64e.authRebase.diversity, + (bool)fixupLoc->arm64e.authRebase.addrDiv, + (uint8_t)fixupLoc->arm64e.authRebase.key + }; + uint64_t targetVMAddr = fixupLoc->arm64e.authRebase.target; + fixupLoc->raw64 = targetVMAddr; + } + else { + uint64_t targetVMAddr = fixupLoc->arm64e.rebase.target; + uint64_t sideTableAddr; + if ( aslrTracker.hasRebaseTarget64(fixupLoc, &sideTableAddr) ) + targetVMAddr = sideTableAddr; + // store high8 in side table + if ( fixupLoc->arm64e.rebase.high8 ) + fixupHigh8s[fixupLoc] = fixupLoc->arm64e.rebase.high8; + fixupLoc->raw64 = targetVMAddr; + } + } + break; + default: + fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format); + break; + } + }); + }); + return; + } + + // If we have any missing imports, then they should check for the kernel symbol + // Grab a hold of that now if it exists so we can check it later + __block bool foundUseOfMagicSymbol = false; + __block bool foundMissingWeakImport = false; + + const uint64_t loadAddress = ma->preferredLoadAddress(); + ma->forEachBind(dylibDiag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t bindType, + const char *symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool &stop) { + // printf("Bind at 0x%llx to '%s'\n", runtimeOffset, symbolName); + // Kext binds are a flat namespace so walk until we find the symbol we need + bool foundSymbol = false; + VTableBindSymbol bindSymbol = { dylibID, symbolName }; + if (SymbolDefinition symbolDef = findDependencyWithSymbol(symbolName, dependencies); symbolDef.symbolVMAddr != ~0ULL) { + // Set the bind to the target address since we found it + uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset; + if ( bindType == BIND_TYPE_POINTER ) { + if ( _is64 ) + *((uint64_t*)fixupLoc) = symbolDef.symbolVMAddr; + else + *((uint32_t*)fixupLoc) = (uint32_t)symbolDef.symbolVMAddr; + + // Only track regular fixups for ASLR, not branch fixups + fixupLocs[fixupLoc] = symbolDef.kernelCollectionLevel; + resolvedBindLocations[(const uint8_t*)fixupLoc] = bindSymbol; + } else if ( bindType == BIND_TYPE_TEXT_PCREL32 ) { + // The value to store is the difference between the bind target + // and the value of the PC after this instruction + uint64_t targetAddress = 0; + if ( dylibSymbols.dylibLevel != symbolDef.kernelCollectionLevel ) { + // Record this for later as we want to create stubs serially + CacheOffset targetCacheOffset = { symbolDef.kernelCollectionLevel, symbolDef.symbolVMAddr }; + branchStubs.emplace_back((BranchStubData){ + .targetCacheOffset = targetCacheOffset, + .fixupLoc = fixupLoc, + .fixupVMOffset = runtimeOffset + }); + } else { + targetAddress = symbolDef.symbolVMAddr; + uint64_t diffValue = targetAddress - (loadAddress + runtimeOffset + 4); + *((uint32_t*)fixupLoc) = (uint32_t)diffValue; + } + } else { + dylibDiag.error("Unexpected bind type: %d", bindType); + stop = true; + return; + } + + foundSymbol = true; + } + + if ( foundSymbol && !foundUseOfMagicSymbol ) { + foundUseOfMagicSymbol = (strcmp(symbolName, missingWeakImportSymbolName) == 0); + } + + if (!foundSymbol) { + for (const std::string& dependencyID : dependencies) { + auto depIt = dylibsToSymbols.find(dependencyID); + if (depIt == dylibsToSymbols.end()) { + dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id", + symbolName, dependencyID.c_str()); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = depIt->second; + auto exportIt = dylibSymbols.globals.find(symbolName); + if ( exportIt == dylibSymbols.globals.end() ) + continue; + findDependencyWithSymbol(symbolName, dependencies); + break; + } + } + + // If the symbol is weak, and we didn't find it in our listed + // dependencies, then use our own definition + if ( !foundSymbol && (libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP) ) { + auto dylibIt = dylibsToSymbols.find(dylibID); + if (dylibIt == dylibsToSymbols.end()) { + dylibDiag.error("Failed to bind weak '%s' as could not find a define in self", + symbolName); + stop = true; + return; + } + + const DylibSymbols& dylibSymbols = dylibIt->second; + auto exportIt = dylibSymbols.globals.find(symbolName); + if ( exportIt != dylibSymbols.globals.end() ) { + // Set the bind to the target address since we found it + uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset; + if ( bindType == BIND_TYPE_POINTER ) { + if ( _is64 ) + *((uint64_t*)fixupLoc) = exportIt->second; + else + *((uint32_t*)fixupLoc) = (uint32_t)exportIt->second; + + // Only track regular fixups for ASLR, not branch fixups + fixupLocs[fixupLoc] = dylibSymbols.dylibLevel; + resolvedBindLocations[(const uint8_t*)fixupLoc] = bindSymbol; + } else if ( bindType == BIND_TYPE_TEXT_PCREL32 ) { + // We should never have a branch to a weak bind as we should have had a GOT for these + dylibDiag.error("Unexpected weak bind type: %d", bindType); + stop = true; + return; + } else { + dylibDiag.error("Unexpected bind type: %d", bindType); + stop = true; + return; + } + + foundSymbol = true; + } + } + + if ( !foundSymbol && weakImport ) { + if ( bindType != BIND_TYPE_POINTER ) { + dylibDiag.error("Unexpected bind type: %d", bindType); + stop = true; + return; + } + // Find _gOSKextUnresolved in the kernel + // Weak imports are not compared against null, but instead against the address of that symbol + auto kernelSymbolsIt = dylibsToSymbols.find(kernelID); + assert(kernelSymbolsIt != dylibsToSymbols.end()); + const DylibSymbols& kernelSymbols = kernelSymbolsIt->second; + auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName); + if (exportIt != kernelSymbols.globals.end()) { + foundMissingWeakImport = true; + + uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset; + if ( _is64 ) + *((uint64_t*)fixupLoc) = exportIt->second; + else + *((uint32_t*)fixupLoc) = (uint32_t)exportIt->second; + + // Only track regular fixups for ASLR, not branch fixups + fixupLocs[fixupLoc] = kernelSymbols.dylibLevel; + return; + } + dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName); + return; + } + + if ( !foundSymbol ) { + // Store missing binds for later. They may be fixed by vtable patching + const uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset; + missingBindLocations[fixupLoc] = bindSymbol; + } + }, ^(const char *symbolName) { + dylibDiag.error("Strong binds are not supported right now"); + }); + + if ( foundMissingWeakImport && !foundUseOfMagicSymbol ) { + dylibDiag.error("Has weak references but does not test for them. " + "Test for weak references with OSKextSymbolIsResolved()."); + return; + } + + ma->forEachRebase(dylibDiag, false, ^(uint64_t runtimeOffset, bool &stop) { + uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset; + fixupLocs[fixupLoc] = (uint8_t)~0U; + }); +} + +// A helper to automatically call CFRelease when we go out of scope +struct AutoReleaseTypeRef { + AutoReleaseTypeRef() = default; + ~AutoReleaseTypeRef() { + if ( ref != nullptr ) { + CFRelease(ref); + } + } + void setRef(CFTypeRef typeRef) { + assert(ref == nullptr); + ref = typeRef; + } + + CFTypeRef ref = nullptr; +}; + +static std::unique_ptr> getKPI(Diagnostics& diags, const dyld3::MachOAnalyzer* ma, + std::string_view dylibID) { + bool isAppleKext = (dylibID.find("com.apple") == 0); + if ( !isAppleKext ) + return {}; + + __block std::list nonASCIIStrings; + auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) { + const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8); + if ( symbolName != nullptr ) + return symbolName; + + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); + char buffer[len + 1]; + if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { + diags.error("Could not convert string to ASCII"); + return (const char*)nullptr; + } + buffer[len] = '\0'; + nonASCIIStrings.push_back(buffer); + return nonASCIIStrings.back().c_str(); + }; + + uint64_t symbolSetsSize = 0; + const void* symbolSetsContent = ma->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize); + if ( symbolSetsContent == nullptr ) + return {}; + + AutoReleaseTypeRef dataRefReleaser; + AutoReleaseTypeRef plistRefReleaser; + + std::unordered_set symbols; + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull); + if ( dataRef == nullptr ) { + diags.error("Could not create data ref for kpi"); + return {}; + } + dataRefReleaser.setRef(dataRef); + + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + diags.error("Could not load plist because :%s", CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(errorRef); + return {}; + } + if ( plistRef == nullptr ) { + diags.error("Could not create plist ref for kpi"); + return {}; + } + plistRefReleaser.setRef(plistRef); + + if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) { + diags.error("kpi plist should be a dictionary"); + return {}; + } + + CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef; + + // CFBundleIdentifier + CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("CFBundleIdentifier")); + if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) { + diags.error("kpi bundle ID should be a string"); + return {}; + } + + const char* bundleID = getString(diags, bundleIDRef); + if ( bundleID == nullptr ) + return {}; + + if ( dylibID != bundleID ) { + diags.error("kpi bundle ID doesn't match kext"); + return {}; + } + + CFArrayRef symbolsArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("Symbols")); + if ( symbolsArrayRef != nullptr ) { + if ( CFGetTypeID(symbolsArrayRef) != CFArrayGetTypeID() ) { + diags.error("Symbols value should be an array"); + return {}; + } + for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolsArrayRef); ++symbolSetIndex) { + CFStringRef symbolNameRef = (CFStringRef)CFArrayGetValueAtIndex(symbolsArrayRef, symbolSetIndex); + if ( (symbolNameRef == nullptr) || (CFGetTypeID(symbolNameRef) != CFStringGetTypeID()) ) { + diags.error("Symbol name should be a string"); + return {}; + } + + const char* symbolName = getString(diags, symbolNameRef); + if ( symbolName == nullptr ) + return {}; + symbols.insert(symbolName); + } + } + + return std::make_unique>(std::move(symbols)); +} + +void AppCacheBuilder::processFixups() +{ + auto dylibsToSymbolsOwner = std::make_unique>(); + std::map& dylibsToSymbols = *dylibsToSymbolsOwner.get(); + + auto vtablePatcherOwner = std::make_unique(numFixupLevels); + VTablePatcher& vtablePatcher = *vtablePatcherOwner.get(); + + const uint32_t kernelLevel = 0; + uint8_t currentLevel = getCurrentFixupLevel(); + + // Keep track of missing binds until later. They may be "resolved" by vtable patching + std::map missingBindLocations; + + __block std::string kernelID; + __block const dyld3::MachOAnalyzer* kernelMA = nullptr; + if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) { + kernelMA = getKernelStaticExecutableFromCache(); + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector &dependencies, + Diagnostics& dylibDiag, + bool &stop) { + if ( ma == kernelMA ) { + kernelID = dylibID; + stop = true; + } + }); + assert(!kernelID.empty()); + } else { + assert(existingKernelCollection != nullptr); + existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + if ( ma->isStaticExecutable() ) { + kernelMA = ma; + kernelID = name; + } + }); + if ( kernelMA == nullptr ) { + _diagnostics.error("Could not find kernel in kernel collection"); + return; + } + } + + auto getGlobals = [](Diagnostics& diags, const dyld3::MachOAnalyzer *ma) -> std::map { + // Note we don't put __block on the variable directly as then it gets copied in to the return value + std::map exports; + __block std::map& exportsRef = exports; + ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value, + uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + exportsRef[symbolName] = n_value; + }); + return exports; + }; + + auto getLocals = [](Diagnostics& diags, const dyld3::MachOAnalyzer *ma) -> std::map { + // Note we don't put __block on the variable directly as then it gets copied in to the return value + std::map exports; + __block std::map& exportsRef = exports; + ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value, + uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + exportsRef[symbolName] = n_value; + }); + return exports; + }; + + dylibsToSymbols[kernelID] = { + getGlobals(_diagnostics, kernelMA), + getLocals(_diagnostics, kernelMA), + nullptr, + kernelLevel, + std::string(kernelID) + }; + + // Add all the codeless kext's as kext's can list them as dependencies + // Note we add placeholders here which can be legitimately replaced by symbol sets + for (const InputDylib& dylib : codelessKexts) { + dylibsToSymbols[dylib.dylibID] = { }; + } + + // Similarly, add placeholders for codeless kexts in the baseKC + if ( existingKernelCollection != nullptr ) { + existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics, + ^(const char *bundleName, const char* relativePath, + const std::vector &deps) { + dylibsToSymbols[bundleName] = { }; + }); + } + + // And placeholders for codeless kexts in the pageableKC + if ( pageableKernelCollection != nullptr ) { + pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics, + ^(const char *bundleName, const char* relativePath, + const std::vector &deps) { + dylibsToSymbols[bundleName] = { }; + }); + } + + // Get the symbol sets + AutoReleaseTypeRef dataRefReleaser; + AutoReleaseTypeRef plistRefReleaser; + + __block std::list nonASCIIStrings; + auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) { + const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8); + if ( symbolName != nullptr ) + return symbolName; + + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); + char buffer[len + 1]; + if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { + diags.error("Could not convert string to ASCII"); + return (const char*)nullptr; + } + buffer[len] = '\0'; + nonASCIIStrings.push_back(buffer); + return nonASCIIStrings.back().c_str(); + }; + + uint64_t symbolSetsSize = 0; + const void* symbolSetsContent = kernelMA->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize); + if ( symbolSetsContent != nullptr ) { + const DylibSymbols& kernelSymbols = dylibsToSymbols[kernelID]; + + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull); + if ( dataRef == nullptr ) { + _diagnostics.error("Could not create data ref for symbol sets"); + return; + } + dataRefReleaser.setRef(dataRef); + + CFErrorRef errorRef = nullptr; + CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + _diagnostics.error("Could not load plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(errorRef); + return; + } + if ( plistRef == nullptr ) { + _diagnostics.error("Could not create plist ref for symbol sets"); + return; + } + plistRefReleaser.setRef(plistRef); + + if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) { + _diagnostics.error("Symbol set plist should be a dictionary"); + return; + } + CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef; + CFArrayRef symbolSetArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("SymbolsSets")); + if ( symbolSetArrayRef != nullptr ) { + if ( CFGetTypeID(symbolSetArrayRef) != CFArrayGetTypeID() ) { + _diagnostics.error("SymbolsSets value should be an array"); + return; + } + for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolSetArrayRef); ++symbolSetIndex) { + CFDictionaryRef symbolSetDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolSetArrayRef, symbolSetIndex); + if ( CFGetTypeID(symbolSetDictRef) != CFDictionaryGetTypeID() ) { + _diagnostics.error("Symbol set element should be a dictionary"); + return; + } + + // CFBundleIdentifier + CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("CFBundleIdentifier")); + if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) { + _diagnostics.error("Symbol set bundle ID should be a string"); + return; + } + + // Symbols + CFArrayRef symbolsArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("Symbols")); + if ( (symbolsArrayRef == nullptr) || (CFGetTypeID(symbolsArrayRef) != CFArrayGetTypeID()) ) { + _diagnostics.error("Symbol set symbols should be an array"); + return; + } + + std::map symbolSetGlobals; + std::map symbolSetLocals; + for (CFIndex symbolIndex = 0; symbolIndex != CFArrayGetCount(symbolsArrayRef); ++symbolIndex) { + CFDictionaryRef symbolDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolsArrayRef, symbolIndex); + if ( CFGetTypeID(symbolDictRef) != CFDictionaryGetTypeID() ) { + _diagnostics.error("Symbols array element should be a dictionary"); + return; + } + + // SymbolPrefix + CFStringRef symbolPrefixRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("SymbolPrefix")); + if ( symbolPrefixRef != nullptr ) { + if ( CFGetTypeID(symbolPrefixRef) != CFStringGetTypeID() ) { + _diagnostics.error("Symbol prefix should be a string"); + return; + } + + const char* symbolPrefix = getString(_diagnostics, symbolPrefixRef); + if ( symbolPrefix == nullptr ) + return; + size_t symbolPrefixLen = strlen(symbolPrefix); + + // FIXME: Brute force might not be the best thing here + for (std::pair kernelGlobal : kernelSymbols.globals) { + if ( strncmp(kernelGlobal.first.data(), symbolPrefix, symbolPrefixLen) == 0 ) { + symbolSetGlobals[kernelGlobal.first] = kernelGlobal.second; + } + } + for (std::pair kernelLocal : kernelSymbols.locals) { + if ( strncmp(kernelLocal.first.data(), symbolPrefix, symbolPrefixLen) == 0 ) { + symbolSetLocals[kernelLocal.first] = kernelLocal.second; + } + } + continue; + } + + // SymbolName + CFStringRef symbolNameRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("SymbolName")); + if ( (symbolNameRef == nullptr) || (CFGetTypeID(symbolNameRef) != CFStringGetTypeID()) ) { + _diagnostics.error("Symbol name should be a string"); + return; + } + + // AliasTarget [Optional] + CFStringRef aliasTargetRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("AliasTarget")); + if ( aliasTargetRef == nullptr ) { + // No alias + const char* symbolName = getString(_diagnostics, symbolNameRef); + if ( symbolName == nullptr ) + return; + + // Find the symbol in xnu + auto globalIt = kernelSymbols.globals.find(symbolName); + if (globalIt != kernelSymbols.globals.end()) { + symbolSetGlobals[symbolName] = globalIt->second; + } + + auto localIt = kernelSymbols.locals.find(symbolName); + if (localIt != kernelSymbols.locals.end()) { + symbolSetLocals[symbolName] = localIt->second; + } + } else { + // We have an alias + if ( CFGetTypeID(aliasTargetRef) != CFStringGetTypeID() ) { + _diagnostics.error("Alias should be a string"); + return; + } + + const char* symbolName = getString(_diagnostics, symbolNameRef); + if ( symbolName == nullptr ) + return; + const char* aliasTargetName = getString(_diagnostics, aliasTargetRef); + if ( aliasTargetName == nullptr ) + return; + + // Find the alias symbol in xnu + auto globalIt = kernelSymbols.globals.find(aliasTargetName); + if (globalIt != kernelSymbols.globals.end()) { + symbolSetGlobals[symbolName] = globalIt->second; + } else { + _diagnostics.error("Alias '%s' not found in kernel", aliasTargetName); + return; + } + + auto localIt = kernelSymbols.locals.find(aliasTargetName); + if (localIt != kernelSymbols.locals.end()) { + symbolSetLocals[symbolName] = localIt->second; + } else { + // This is not an error, as aliases from symbol sets from the kernel + // are only for vtable patching, not general binding + } + } + } + const char* dylibID = getString(_diagnostics, bundleIDRef); + if ( dylibID == nullptr ) + return; + + // HACK: kxld aliases __ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev to __ZN15OSMetaClassBase8DispatchE5IORPC + auto metaclassHackIt = symbolSetGlobals.find("__ZN15OSMetaClassBase8DispatchE5IORPC"); + if ( metaclassHackIt != symbolSetGlobals.end() ) + symbolSetGlobals["__ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev"] = metaclassHackIt->second; + dylibsToSymbols[dylibID] = { + std::move(symbolSetGlobals), + std::move(symbolSetLocals), + nullptr, + kernelLevel, + dylibID + }; + } + } + } + + auto processBinary = ^(Diagnostics& dylibDiags, const dyld3::MachOAnalyzer *ma, + const std::string& dylibID, uint32_t dylibLevel) { + // We dont support export trie's for now + uint32_t unusedExportTrieOffset = 0; + uint32_t unusedExportTrieSize = 0; + if (ma->hasExportTrie(unusedExportTrieOffset, unusedExportTrieSize)) + assert(false); + + // Already done the kernel before. + if ( ma == kernelMA ) + return; + + // Regular kext. + dylibsToSymbols[dylibID] = { + getGlobals(dylibDiags, ma), + getLocals(dylibDiags, ma), + getKPI(dylibDiags, ma, dylibID), + dylibLevel, + dylibID }; + }; + + // Process binary symbols in parallel + { + struct DylibData { + const dyld3::MachOAnalyzer* ma = nullptr; + Diagnostics& dylibDiag; + const std::string& dylibID; + }; + + __block std::vector dylibDatas; + dylibDatas.reserve(sortedDylibs.size()); + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode, + const std::vector &dependencies, Diagnostics &dylibDiag, bool &stop) { + // Already done the kernel before. + if ( ma == kernelMA ) + return; + + // Make space for all the map entries so that we know they are there when we write their values later + dylibsToSymbols[dylibID] = { }; + dylibDatas.emplace_back((DylibData){ ma, dylibDiag, dylibID }); + }); + + dispatch_apply(dylibDatas.size(), DISPATCH_APPLY_AUTO, ^(size_t index) { + DylibData& dylibData = dylibDatas[index]; + processBinary(dylibData.dylibDiag, dylibData.ma, dylibData.dylibID, currentLevel); + }); + } + + // Add exports from the kernel collection if we have it + if ( existingKernelCollection != nullptr ) { + uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel); + existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + processBinary(_diagnostics, ma, name, fixupLevel); + }); + } + + // Add exports from the pageable collection if we have it + if ( pageableKernelCollection != nullptr ) { + uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC); + pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + processBinary(_diagnostics, ma, name, fixupLevel); + }); + } + + // Map from an offset in to a KC to a synthesized stub which branches to that offset + struct CacheOffsetHash + { + size_t operator() (const CacheOffset& cacheOffset) const + { + return std::hash{}(cacheOffset.first) ^ std::hash{}(cacheOffset.second); + } + }; + std::unordered_map branchStubs; + + // Clear the branch regions sizes so that we fill them up to their buffer sizes as we go + branchStubsRegion.sizeInUse = 0; + branchGOTsRegion.sizeInUse = 0; + + { + // Map from each symbol to the list of dylibs which export it + auto symbolMapOwner = std::make_unique>>(); + __block auto& symbolMap = *symbolMapOwner.get(); + for (const auto& dylibNameAndSymbols : dylibsToSymbols) { + const DylibSymbols& dylibSymbols = dylibNameAndSymbols.second; + for (const auto& symbolNameAndAddress : dylibSymbols.globals) { + // By default, everything i KPI, ie, can be linked by third parties. + // If a symbol is is provided, even an empty one, then it can override this + bool isKPI = true; + if ( dylibSymbols.dylibName == "com.apple.kpi.private" ) { + // com.apple.kpi.private is always hidden from third parties. They shouldn't even list it as a dependency + isKPI = false; + } else if ( dylibSymbols.kpiSymbols ) { + const std::unordered_set* kpiSymbols = dylibSymbols.kpiSymbols.get(); + if ( kpiSymbols->count(symbolNameAndAddress.first.data()) == 0 ) + isKPI = false; + } + symbolMap[symbolNameAndAddress.first].push_back({ &dylibSymbols, symbolNameAndAddress.second, isKPI }); + } + } + + auto dylibFixupsOwner = std::make_unique>(); + __block auto& dylibFixups = *dylibFixupsOwner.get(); + dylibFixups.reserve(sortedDylibs.size()); + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode, + const std::vector &dependencies, Diagnostics &dylibDiag, bool &stop) { + + auto dylibSymbolsIt = dylibsToSymbols.find(dylibID); + assert(dylibSymbolsIt != dylibsToSymbols.end()); + + dylibFixups.emplace_back((DylibFixups){ + .ma = ma, + .dylibSymbols = dylibSymbolsIt->second, + .dylibDiag = dylibDiag, + .dependencies = dependencies + }); + }); + + dispatch_apply(dylibFixups.size(), DISPATCH_APPLY_AUTO, ^(size_t index) { + DylibFixups& dylibFixup = dylibFixups[index]; + dylibFixup.processFixups(dylibsToSymbols, symbolMap, kernelID, _aslrTracker); + }); + + // Merge all the dylib results in serial + for (DylibFixups& dylibFixup : dylibFixups) { + // Skip bad dylibs + if ( dylibFixup.dylibDiag.hasError() ) { + if ( !_diagnostics.hasError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); + } + return; + } + + if ( !dylibFixup.missingBindLocations.empty() ) { + missingBindLocations.insert(dylibFixup.missingBindLocations.begin(), + dylibFixup.missingBindLocations.end()); + } + + if ( !dylibFixup.fixupLocs.empty() ) { + for (auto fixupLocAndLevel : dylibFixup.fixupLocs) { + _aslrTracker.add(fixupLocAndLevel.first, fixupLocAndLevel.second); + } + } + + if ( !dylibFixup.fixupHigh8s.empty() ) { + for (auto fixupLocAndHigh8 : dylibFixup.fixupHigh8s) { + _aslrTracker.setHigh8(fixupLocAndHigh8.first, fixupLocAndHigh8.second); + } + } + + if ( !dylibFixup.fixupAuths.empty() ) { + for (auto fixupLocAndAuth : dylibFixup.fixupAuths) { + _aslrTracker.setAuthData(fixupLocAndAuth.first, fixupLocAndAuth.second.diversity, + fixupLocAndAuth.second.addrDiv, fixupLocAndAuth.second.key); + } + } + + // Emit branch stubs + const uint64_t loadAddress = dylibFixup.ma->preferredLoadAddress(); + for (const DylibFixups::BranchStubData& branchData : dylibFixup.branchStubs) { + // Branching from the auxKC to baseKC. ld64 doesn't emit a stub in x86_64 kexts + // so we need to synthesize one now + uint64_t targetAddress = 0; + const CacheOffset& targetCacheOffset = branchData.targetCacheOffset; + auto itAndInserted = branchStubs.insert({ targetCacheOffset, 0 }); + if ( itAndInserted.second ) { + // We inserted the branch location, so we need to create new stubs and GOTs + if ( branchStubsRegion.sizeInUse == branchStubsRegion.bufferSize ) { + _diagnostics.error("Overflow in branch stubs region"); + return; + } + if ( branchGOTsRegion.sizeInUse == branchGOTsRegion.bufferSize ) { + _diagnostics.error("Overflow in branch GOTs region"); + return; + } + uint64_t stubAddress = branchStubsRegion.unslidLoadAddress + branchStubsRegion.sizeInUse; + uint8_t* stubBuffer = branchStubsRegion.buffer + branchStubsRegion.sizeInUse; + uint64_t gotAddress = branchGOTsRegion.unslidLoadAddress + branchGOTsRegion.sizeInUse; + uint8_t* gotBuffer = branchGOTsRegion.buffer + branchGOTsRegion.sizeInUse; + + // Write the stub + // ff 25 aa bb cc dd jmpq *0xddccbbaa(%rip) + uint64_t diffValue = gotAddress - (stubAddress + 6); + stubBuffer[0] = 0xFF; + stubBuffer[1] = 0x25; + memcpy(&stubBuffer[2], &diffValue, sizeof(uint32_t)); + + // And write the GOT + uint8_t symbolCacheLevel = targetCacheOffset.first; + uint64_t symbolVMAddr = targetCacheOffset.second; + if ( _is64 ) + *((uint64_t*)gotBuffer) = symbolVMAddr; + else + *((uint32_t*)gotBuffer) = (uint32_t)symbolVMAddr; + _aslrTracker.add(gotBuffer, symbolCacheLevel); + + branchStubsRegion.sizeInUse += 6; + branchGOTsRegion.sizeInUse += 8; + targetAddress = stubAddress; + itAndInserted.first->second = targetAddress; + } else { + // The stub already existed, so use it + targetAddress = itAndInserted.first->second; + } + uint64_t diffValue = targetAddress - (loadAddress + branchData.fixupVMOffset + 4); + *((uint32_t*)branchData.fixupLoc) = (uint32_t)diffValue; + } + } + + // FIXME: We could move symbolOwner and dylibFixupsOwner to a worker thread to be destroyed + } + + // Now that we've processes all rebases/binds, patch all the vtables + + // Add all the collections to the vtable patcher + if ( existingKernelCollection != nullptr ) { + // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest + // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that. + // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT + __block uint64_t baseAddress = ~0ULL; + existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + + // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that + // Offset those here + uint64_t basePointerOffset = existingKernelCollection->preferredLoadAddress() - baseAddress; + const uint8_t* basePointer = (uint8_t*)existingKernelCollection - basePointerOffset; + + vtablePatcher.addKernelCollection(existingKernelCollection, Options::AppCacheKind::kernel, + basePointer, baseAddress); + } + + if ( pageableKernelCollection != nullptr ) { + // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest + // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that. + // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT + __block uint64_t baseAddress = ~0ULL; + pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + + // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that + // Offset those here + uint64_t basePointerOffset = pageableKernelCollection->preferredLoadAddress() - baseAddress; + const uint8_t* basePointer = (uint8_t*)pageableKernelCollection - basePointerOffset; + + vtablePatcher.addKernelCollection(pageableKernelCollection, Options::AppCacheKind::pageableKC, + basePointer, baseAddress); + } + + // Also add our KC + vtablePatcher.addKernelCollection((const dyld3::MachOAppCache*)cacheHeader.header, appCacheOptions.cacheKind, + (const uint8_t*)_fullAllocatedBuffer, cacheBaseAddress); + + // Add all the dylibs to the patcher + { + if ( existingKernelCollection != nullptr ) { + uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel); + + __block std::map> kextDependencies; + kextDependencies[kernelID] = {}; + existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics, + ^(const char *bundleName, const char* relativePath, + const std::vector &deps) { + std::vector& dependencies = kextDependencies[bundleName]; + dependencies.insert(dependencies.end(), deps.begin(), deps.end()); + }); + + existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) { + auto depsIt = kextDependencies.find(dylibID); + assert(depsIt != kextDependencies.end()); + vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel); + }); + } + + if ( pageableKernelCollection != nullptr ) { + uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC); + + __block std::map> kextDependencies; + pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics, + ^(const char *bundleName, const char* relativePath, + const std::vector &deps) { + std::vector& dependencies = kextDependencies[bundleName]; + dependencies.insert(dependencies.end(), deps.begin(), deps.end()); + }); + + pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) { + auto depsIt = kextDependencies.find(dylibID); + assert(depsIt != kextDependencies.end()); + vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel); + }); + } + + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode, + const std::vector &dependencies, Diagnostics& dylibDiag, bool &stop) { + vtablePatcher.addDylib(dylibDiag, ma, dylibID, dependencies, currentLevel); + }); + } + + vtablePatcher.findMetaclassDefinitions(dylibsToSymbols, kernelID, kernelMA, appCacheOptions.cacheKind); + vtablePatcher.findExistingFixups(_diagnostics, existingKernelCollection, pageableKernelCollection); + if ( _diagnostics.hasError() ) + return; + + // Add vtables from the base KC if we have one + if ( existingKernelCollection != nullptr ) { + vtablePatcher.findBaseKernelVTables(_diagnostics, existingKernelCollection, dylibsToSymbols); + if ( _diagnostics.hasError() ) + return; + } + + // Add vtables from the pageable KC if we have one + if ( pageableKernelCollection != nullptr ) { + vtablePatcher.findPageableKernelVTables(_diagnostics, pageableKernelCollection, dylibsToSymbols); + if ( _diagnostics.hasError() ) + return; + } + + // Add vables from our level + vtablePatcher.findVTables(currentLevel, kernelMA, dylibsToSymbols, _aslrTracker, missingBindLocations); + + // Don't run the patcher if we have a failure finding the vtables + if ( vtablePatcher.hasError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); + return; + } + + // Now patch all of the vtables. + vtablePatcher.patchVTables(_diagnostics, missingBindLocations, _aslrTracker, currentLevel); + if ( _diagnostics.hasError() ) + return; + + if ( vtablePatcher.hasError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); + return; + } + + // FIXME: We could move vtablePatcherOwner to a worker thread to be destroyed + vtablePatcherOwner.reset(); + + // Also error out if we have an error on any of the dylib diagnostic objects + + // Log any binds which are still missing + for (const auto& missingLocationAndBind : missingBindLocations) { + const uint8_t* missingBindLoc = missingLocationAndBind.first; + const VTableBindSymbol& missingBind = missingLocationAndBind.second; + + // Work out which segment and section this missing bind was in + __block bool reportedError = false; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode, + const std::vector &dependencies, Diagnostics& dylibDiag, bool &stopDylib) { + intptr_t slide = ma->getSlide(); + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, + bool malformedSectionRange, bool &stopSection) { + const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide); + const uint8_t* start = (uint8_t*)content; + const uint8_t* end = start + sectInfo.sectSize; + if ( (missingBindLoc >= start) && (missingBindLoc < end) ) { + std::string segmentName = sectInfo.segInfo.segName; + std::string sectionName = sectInfo.sectName; + uint64_t sectionOffset = (missingBindLoc - start); + + dylibDiag.error("Failed to bind '%s' in '%s' (at offset 0x%llx in %s, %s) as " + "could not find a kext which exports this symbol", + missingBind.symbolName.c_str(), missingBind.binaryID.data(), + sectionOffset, segmentName.c_str(), sectionName.c_str()); + + reportedError = true; + stopSection = true; + stopDylib = true; + } + }); + }); + + if ( !reportedError ) { + _diagnostics.error("Failed to bind '%s' in '%s' as could not find a kext which exports this symbol", + missingBind.symbolName.c_str(), missingBind.binaryID.data()); + } + } + + // If we had missing binds and reported no other errors, then generate an error to give the diagnostics something to track + if ( !missingBindLocations.empty() && _diagnostics.noError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); + } + + // FIXME: We could move dylibsToSymbolsOwner to a worker thread to be destroyed +} + +namespace { + +class ByteBuffer { +public: + ByteBuffer(uint8_t* storage, uintptr_t allocCount) { + buffer.setInitialStorage(storage, allocCount); + } + + uint8_t* makeSpace(size_t bytesNeeded) { + // Make space in the buffer + for (size_t i = 0; i != bytesNeeded; ++i) + buffer.default_constuct_back(); + + // Grab a pointer to our position in the buffer + uint8_t* data = buffer.begin(); + + // Move the buffer to start after our data + dyld3::Array newBuffer(buffer.end(), buffer.freeCount(), 0); + buffer = newBuffer; + + return data; + }; + + const uint8_t* begin() const { + return buffer.begin(); + } + + const uint8_t* end() const { + return buffer.end(); + } + +private: + dyld3::Array buffer; +}; + +} + +void AppCacheBuilder::writeFixups() +{ + if ( fixupsSubRegion.sizeInUse == 0 ) + return; + + __block ByteBuffer byteBuffer(fixupsSubRegion.buffer, fixupsSubRegion.bufferSize); + + // Keep track of where we put the fixups + const uint8_t* classicRelocsBufferStart = nullptr; + const uint8_t* classicRelocsBufferEnd = nullptr; + + // If the kernel needs classic relocs, emit those first + CacheHeader64& header = cacheHeader; + if ( header.dynSymbolTable != nullptr ) { + classicRelocsBufferStart = byteBuffer.begin(); + + dyld3::MachOAnalyzer* cacheMA = (dyld3::MachOAnalyzer*)header.header; + __block uint64_t localRelocBaseAddress = 0; + cacheMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + if ( info.protections & VM_PROT_WRITE ) { + localRelocBaseAddress = info.vmAddr; + stop = true; + } + }); + + const std::vector allRebaseTargets = _aslrTracker.getRebaseTargets(); + + const dyld3::MachOAnalyzer* kernelMA = getKernelStaticExecutableFromCache(); + kernelMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + std::vector segmentRebaseTargets; + uint64_t segmentVMOffset = info.vmAddr - cacheBaseAddress; + const uint8_t* segmentStartAddr = (const uint8_t*)(_fullAllocatedBuffer + segmentVMOffset); + const uint8_t* segmentEndAddr = (const uint8_t*)(segmentStartAddr + info.vmSize); + for (void* target : allRebaseTargets) { + if ( (target >= segmentStartAddr) && (target < segmentEndAddr) ) { + segmentRebaseTargets.push_back(target); + } + } + std::sort(segmentRebaseTargets.begin(), segmentRebaseTargets.end()); + + for (void* target : segmentRebaseTargets) { + uint64_t targetSegmentOffset = (uint64_t)target - (uint64_t)segmentStartAddr; + //printf("Target: %s + 0x%llx: %p\n", info.segName, targetSegmentOffset, target); + + uint64_t offsetFromBaseAddress = (info.vmAddr + targetSegmentOffset) - localRelocBaseAddress; + relocation_info* reloc = (relocation_info*)byteBuffer.makeSpace(sizeof(relocation_info)); + reloc->r_address = (uint32_t)offsetFromBaseAddress; + reloc->r_symbolnum = 0; + reloc->r_pcrel = false; + reloc->r_length = 0; + reloc->r_extern = 0; + reloc->r_type = 0; + + uint32_t vmAddr32 = 0; + uint64_t vmAddr64 = 0; + if ( _aslrTracker.hasRebaseTarget32(target, &vmAddr32) ) { + reloc->r_length = 2; + *(uint32_t*)target = vmAddr32; + } else if ( _aslrTracker.hasRebaseTarget64(target, &vmAddr64) ) { + reloc->r_length = 3; + *(uint64_t*)target = vmAddr64; + } + } + + // Remove these fixups so that we don't also emit chained fixups for them + for (void* target : segmentRebaseTargets) + _aslrTracker.remove(target); + }); + + classicRelocsBufferEnd = byteBuffer.begin(); + } + + // TODO: 32-bit pointer format + assert(_is64); + const uint8_t currentLevel = getCurrentFixupLevel(); + + // We can have up to 4 levels in the fixup format. These are the base addresses from + // which each level starts + BLOCK_ACCCESSIBLE_ARRAY(uint64_t, levelBaseAddresses, 4); + for (unsigned i = 0; i != numFixupLevels; ++i) + levelBaseAddresses[i] = 0; + + levelBaseAddresses[currentLevel] = cacheBaseAddress; + if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel ) { + assert(existingKernelCollection != nullptr); + // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest + __block uint64_t baseAddress = ~0ULL; + existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + levelBaseAddresses[0] = baseAddress; + } + + if ( pageableKernelCollection != nullptr ) { + // We may have __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest + __block uint64_t baseAddress = ~0ULL; + pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + }); + uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC); + levelBaseAddresses[fixupLevel] = baseAddress; + } + + // We have a dyld_chained_starts_in_segment plus an offset for each page + struct SegmentFixups { + //const Region* region = nullptr; + uint8_t* segmentBuffer = nullptr; + uint64_t segmentIndex = 0; + uint64_t unslidLoadAddress = 0; + uint64_t sizeInUse = 0; + dyld_chained_starts_in_segment* starts = nullptr; + uint64_t startsByteSize = 0; + uint64_t numPagesToFixup = 0; + }; + + auto buildChainedFixups = ^(uint64_t baseAddress, uint64_t segmentCount, std::vector& startsInSegments) { + + const uint8_t* chainedFixupsBufferStart = nullptr; + const uint8_t* chainedFixupsBufferEnd = nullptr; + + chainedFixupsBufferStart = byteBuffer.begin(); + + // Start with dyld_chained_fixups_header which is fixed size + dyld_chained_fixups_header* fixupsHeader = (dyld_chained_fixups_header*)byteBuffer.makeSpace(sizeof(dyld_chained_fixups_header)); + + // We have a dyld_chained_starts_in_image plus an offset for each segment + dyld_chained_starts_in_image* startsInImage = (dyld_chained_starts_in_image*)byteBuffer.makeSpace(sizeof(dyld_chained_starts_in_image) + (segmentCount * sizeof(uint32_t))); + + const uint8_t* endOfStarts = nullptr; + for (SegmentFixups& segmentFixups : startsInSegments) { + uint64_t startsInSegmentByteSize = sizeof(dyld_chained_starts_in_segment) + (segmentFixups.numPagesToFixup * sizeof(uint16_t)); + dyld_chained_starts_in_segment* startsInSegment = (dyld_chained_starts_in_segment*)byteBuffer.makeSpace(startsInSegmentByteSize); + endOfStarts = (const uint8_t*)startsInSegment + startsInSegmentByteSize; + + segmentFixups.starts = startsInSegment; + segmentFixups.startsByteSize = startsInSegmentByteSize; + } + + // Starts in image + startsInImage->seg_count = (uint32_t)segmentCount; + for (uint32_t segmentIndex = 0; segmentIndex != segmentCount; ++segmentIndex) { + startsInImage->seg_info_offset[segmentIndex] = 0; + } + for (const SegmentFixups& segmentFixups : startsInSegments) { + dyld_chained_starts_in_segment* startsInSegment = segmentFixups.starts; + uint64_t segmentIndex = segmentFixups.segmentIndex; + assert(segmentIndex < segmentCount); + assert(startsInImage->seg_info_offset[segmentIndex] == 0); + startsInImage->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)startsInSegment - (uint8_t*)startsInImage); + } + + const unsigned chainedPointerStride = dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk::strideSize(chainedPointerFormat); + + // Starts in segment + for (const SegmentFixups& segmentFixups : startsInSegments) { + dyld_chained_starts_in_segment* startsInSegment = segmentFixups.starts; + startsInSegment->size = (uint32_t)segmentFixups.startsByteSize; + startsInSegment->page_size = fixupsPageSize(); + startsInSegment->pointer_format = chainedPointerFormat; + startsInSegment->segment_offset = segmentFixups.unslidLoadAddress - baseAddress; + startsInSegment->max_valid_pointer = 0; // FIXME: Needed in 32-bit only + startsInSegment->page_count = (segmentFixups.sizeInUse + startsInSegment->page_size - 1) / startsInSegment->page_size; + for (uint64_t pageIndex = 0; pageIndex != startsInSegment->page_count; ++pageIndex) { + startsInSegment->page_start[pageIndex] = DYLD_CHAINED_PTR_START_NONE; + uint8_t* lastLoc = nullptr; + // Note we always walk in 1-byte at a time as x86_64 has unaligned fixups + for (uint64_t pageOffset = 0; pageOffset != startsInSegment->page_size; pageOffset += 1) { + uint8_t* fixupLoc = segmentFixups.segmentBuffer + (pageIndex * startsInSegment->page_size) + pageOffset; + uint8_t fixupLevel = currentLevel; + if ( !_aslrTracker.has(fixupLoc, &fixupLevel) ) + continue; + assert((pageOffset % chainedPointerStride) == 0); + if ( lastLoc ) { + // Patch last loc to point here + assert(_is64); + dyld_chained_ptr_64_kernel_cache_rebase* lastLocBits = (dyld_chained_ptr_64_kernel_cache_rebase*)lastLoc; + assert(lastLocBits->next == 0); + uint64_t next = (fixupLoc - lastLoc) / chainedPointerStride; + lastLocBits->next = next; + assert(lastLocBits->next == next && "next location truncated"); + } else { + // First fixup on this page + startsInSegment->page_start[pageIndex] = pageOffset; + } + lastLoc = fixupLoc; + + uint64_t targetVMAddr = *(uint64_t*)fixupLoc; + + uint8_t highByte = 0; + if ( _aslrTracker.hasHigh8(fixupLoc, &highByte) ) { + uint64_t tbi = (uint64_t)highByte << 56; + targetVMAddr |= tbi; + } + + assert(fixupLevel < numFixupLevels); + uint64_t targetVMOffset = targetVMAddr - levelBaseAddresses[fixupLevel]; + + // Pack the vmAddr on this location in to the fixup format + dyld_chained_ptr_64_kernel_cache_rebase* locBits = (dyld_chained_ptr_64_kernel_cache_rebase*)fixupLoc; + + uint16_t diversity; + bool hasAddrDiv; + uint8_t key; + if ( _aslrTracker.hasAuthData(fixupLoc, &diversity, &hasAddrDiv, &key) ) { + locBits->target = targetVMOffset; + locBits->cacheLevel = fixupLevel; + locBits->diversity = diversity; + locBits->addrDiv = hasAddrDiv; + locBits->key = key; + locBits->next = 0; + locBits->isAuth = 1; + assert(locBits->target == targetVMOffset && "target truncated"); + } + else { + locBits->target = targetVMOffset; + locBits->cacheLevel = fixupLevel; + locBits->diversity = 0; + locBits->addrDiv = 0; + locBits->key = 0; + locBits->next = 0; + locBits->isAuth = 0; + assert(locBits->target == targetVMOffset && "target truncated"); + } + } + } + } + + chainedFixupsBufferEnd = byteBuffer.begin(); + + // Header + fixupsHeader->fixups_version = 0; + fixupsHeader->starts_offset = (uint32_t)((uint8_t*)startsInImage - (uint8_t*)fixupsHeader); + fixupsHeader->imports_offset = (uint32_t)((uint8_t*)chainedFixupsBufferEnd - (uint8_t*)fixupsHeader); + fixupsHeader->symbols_offset = fixupsHeader->imports_offset; + fixupsHeader->imports_count = 0; + fixupsHeader->imports_format = DYLD_CHAINED_IMPORT; // The validate code wants a value here + fixupsHeader->symbols_format = 0; + + return std::make_pair(chainedFixupsBufferStart, chainedFixupsBufferEnd); + }; + + if ( fixupsArePerKext() ) { + // The pageableKC (and sometimes auxKC) has one LC_DYLD_CHAINED_FIXUPS per kext, not 1 total + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector &dependencies, + Diagnostics& dylibDiag, bool &stop) { + uint64_t loadAddress = ma->preferredLoadAddress(); + + __block uint64_t numSegments = 0; + __block std::vector segmentFixups; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + // Third party kexts have writable __TEXT, so we need to add starts for all segments + // other than LINKEDIT + bool segmentCanHaveFixups = false; + if ( appCacheOptions.cacheKind == Options::AppCacheKind::pageableKC ) { + segmentCanHaveFixups = (info.protections & VM_PROT_WRITE) != 0; + } else { + // auxKC + segmentCanHaveFixups = (strcmp(info.segName, "__LINKEDIT") != 0); + } + + if ( segmentCanHaveFixups) { + SegmentFixups segmentToFixup; + segmentToFixup.segmentBuffer = (uint8_t*)ma + (info.vmAddr - loadAddress); + segmentToFixup.segmentIndex = info.segIndex; + segmentToFixup.unslidLoadAddress = info.vmAddr; + segmentToFixup.sizeInUse = info.vmSize; + segmentToFixup.starts = nullptr; + segmentToFixup.startsByteSize = 0; + segmentToFixup.numPagesToFixup = numWritablePagesToFixup(info.vmSize); + segmentFixups.push_back(segmentToFixup); + } + + ++numSegments; + }); + + + std::pair chainedFixupsRange = buildChainedFixups(loadAddress, + numSegments, segmentFixups); + const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first; + const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second; + + if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) { + // Add the load command to our file + + uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer; + uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart; + + // 64-bit + assert(_is64); + typedef Pointer64 P; + + uint32_t freeSpace = ma->loadCommandsFreeSpace(); + assert(freeSpace >= sizeof(macho_linkedit_data_command

)); + uint8_t* endOfLoadCommands = (uint8_t*)ma + sizeof(macho_header

) + ma->sizeofcmds; + + // update mach_header to account for new load commands + macho_header

* mh = (macho_header

*)ma; + mh->set_sizeofcmds(mh->sizeofcmds() + sizeof(macho_linkedit_data_command

)); + mh->set_ncmds(mh->ncmds() + 1); + + // Add the new load command + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)endOfLoadCommands; + cmd->set_cmd(LC_DYLD_CHAINED_FIXUPS); + cmd->set_cmdsize(sizeof(linkedit_data_command)); + cmd->set_dataoff((uint32_t)(_readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse + fixupsOffset)); + cmd->set_datasize((uint32_t)fixupsSize); + } + }); + + // Also build chained fixups on the top level for the branch stub GOTs + // FIXME: We don't need numRegions() here, but instead just up to an including the RW region + uint64_t segmentCount = numRegions(); + __block std::vector segmentFixups; + + if ( branchGOTsRegion.sizeInUse != 0 ) { + SegmentFixups segmentToFixup; + segmentToFixup.segmentBuffer = branchGOTsRegion.buffer; + segmentToFixup.segmentIndex = branchGOTsRegion.index; + segmentToFixup.unslidLoadAddress = branchGOTsRegion.unslidLoadAddress; + segmentToFixup.sizeInUse = branchGOTsRegion.sizeInUse; + segmentToFixup.starts = nullptr; + segmentToFixup.startsByteSize = 0; + segmentToFixup.numPagesToFixup = numWritablePagesToFixup(branchGOTsRegion.bufferSize); + segmentFixups.push_back(segmentToFixup); + } + + std::pair chainedFixupsRange = buildChainedFixups(cacheHeaderRegion.unslidLoadAddress, + segmentCount, segmentFixups); + const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first; + const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second; + + if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) { + uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer; + uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart; + header.chainedFixups->dataoff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset;; + header.chainedFixups->datasize = (uint32_t)fixupsSize; + } + } else { + // Build the chained fixups for just the kernel collection itself + // FIXME: We don't need numRegions() here, but instead just up to an including the RW region + uint64_t segmentCount = numRegions(); + __block std::vector segmentFixups; + + auto addSegmentStarts = ^(const Region& region) { + SegmentFixups segmentToFixup; + segmentToFixup.segmentBuffer = region.buffer; + segmentToFixup.segmentIndex = region.index; + segmentToFixup.unslidLoadAddress = region.unslidLoadAddress; + segmentToFixup.sizeInUse = region.sizeInUse; + segmentToFixup.starts = nullptr; + segmentToFixup.startsByteSize = 0; + segmentToFixup.numPagesToFixup = numWritablePagesToFixup(region.bufferSize); + segmentFixups.push_back(segmentToFixup); + }; + + if ( dataConstRegion.sizeInUse != 0 ) + addSegmentStarts(dataConstRegion); + if ( branchGOTsRegion.sizeInUse != 0 ) + addSegmentStarts(branchGOTsRegion); + if ( readWriteRegion.sizeInUse != 0 ) + addSegmentStarts(readWriteRegion); + if ( hibernateRegion.sizeInUse != 0 ) + addSegmentStarts(hibernateRegion); + for (const Region& region : nonSplitSegRegions) { + // Assume writable regions have fixups to emit + // Note, third party kext's have __TEXT fixups, so assume all of these have fixups + // LINKEDIT is already elsewhere + addSegmentStarts(region); + } + + std::pair chainedFixupsRange = buildChainedFixups(cacheHeaderRegion.unslidLoadAddress, + segmentCount, segmentFixups); + const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first; + const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second; + + if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) { + uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer; + uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart; + header.chainedFixups->dataoff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset;; + header.chainedFixups->datasize = (uint32_t)fixupsSize; + } + } + + // Move the fixups to the end of __LINKEDIT + if ( classicRelocsBufferStart != classicRelocsBufferEnd ) { + uint64_t fixupsOffset = (uint64_t)classicRelocsBufferStart - (uint64_t)fixupsSubRegion.buffer; + uint64_t fixupsSize = (uint64_t)classicRelocsBufferEnd - (uint64_t)classicRelocsBufferStart; + header.dynSymbolTable->locreloff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset; + header.dynSymbolTable->nlocrel = (uint32_t)fixupsSize / sizeof(fixupsSize); + } + + uint64_t fixupsSpace = (uint64_t)byteBuffer.end() - (uint64_t)fixupsSubRegion.buffer; + + uint8_t* linkeditEnd = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse; + memcpy(linkeditEnd, fixupsSubRegion.buffer, fixupsSpace); + uint8_t* fixupsEnd = linkeditEnd + fixupsSpace; + + _readOnlyRegion.sizeInUse += align(fixupsSpace, _is64 ? 3 : 2); + _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14); + _readOnlyRegion.bufferSize = _readOnlyRegion.sizeInUse; + + // Zero the alignment gap, just in case there's any unoptimized LINKEDIT in there + uint8_t* alignedBufferEnd = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse; + if ( fixupsEnd != alignedBufferEnd ){ + memset(fixupsEnd, 0, alignedBufferEnd - fixupsEnd); + } + +#if 0 + dyld3::MachOAnalyzer* cacheMA = (dyld3::MachOAnalyzer*)header.header; + uint64_t cachePreferredLoadAddress = cacheMA->preferredLoadAddress(); + cacheMA->forEachRebase(_diagnostics, false, ^(uint64_t runtimeOffset, bool &stop) { + printf("Rebase: 0x%llx = 0x%llx\n", runtimeOffset, runtimeOffset + cachePreferredLoadAddress); + }); +#endif +} + +void AppCacheBuilder::allocateBuffer() +{ + // Whether to order the regions __TEXT, __DATA, __LINKEDIT or __DATA, __TEXT, __LINKEDIT in VM address order + bool dataRegionFirstInVMOrder = false; + bool hibernateRegionFirstInVMOrder = false; + switch (appCacheOptions.cacheKind) { + case Options::AppCacheKind::none: + assert(0 && "Cache kind should have been set"); + break; + case Options::AppCacheKind::kernel: + if ( hibernateAddress != 0 ) + hibernateRegionFirstInVMOrder = true; + break; + case Options::AppCacheKind::pageableKC: + // There's no interesting ordering for the pageableKC + break; + case Options::AppCacheKind::kernelCollectionLevel2: + assert(0 && "Unimplemented"); + break; + case Options::AppCacheKind::auxKC: + dataRegionFirstInVMOrder = true; + break; + } + + // Count how many bytes we need from all our regions + __block uint64_t numRegionFileBytes = 0; + __block uint64_t numRegionVMBytes = 0; + + std::vector> regions; + std::vector> regionsVMOrder; + std::map sectionsToAddToRegions; + + if ( hibernateRegionFirstInVMOrder ) { + regionsVMOrder.push_back({ &hibernateRegion, numRegionVMBytes }); + // Pad out the VM offset so that the cache header starts where the base address + // really should be + uint64_t paddedSize = cacheBaseAddress - hibernateAddress; + if ( hibernateRegion.bufferSize > paddedSize ) { + _diagnostics.error("Could not lay out __HIB segment"); + return; + } + numRegionVMBytes = paddedSize; + // Set the base address to the hibernate address so that we actually put the + // hibernate segment there + cacheBaseAddress = hibernateAddress; + + // Add a section too + sectionsToAddToRegions[&hibernateRegion] = 1; + } else if ( dataRegionFirstInVMOrder ) { + if ( prelinkInfoDict != nullptr ) { + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes }); + numRegionVMBytes += prelinkInfoRegion.bufferSize; + } + if ( readWriteRegion.sizeInUse != 0 ) { + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes }); + numRegionVMBytes += readWriteRegion.bufferSize; + } + } + + // Cache header + numRegionVMBytes = align(numRegionVMBytes, 14); + regions.push_back({ &cacheHeaderRegion, 0 }); + regionsVMOrder.push_back({ &cacheHeaderRegion, numRegionVMBytes }); + + // Split seg __TEXT + { + // File offset + readOnlyTextRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += readOnlyTextRegion.bufferSize; + regions.push_back({ &readOnlyTextRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &readOnlyTextRegion, numRegionVMBytes }); + numRegionVMBytes += readOnlyTextRegion.bufferSize; + + // Add a section too + sectionsToAddToRegions[&readOnlyTextRegion] = 1; + } + + // Split seg __TEXT_EXEC + if ( readExecuteRegion.sizeInUse != 0 ) { + // File offset + readExecuteRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += readExecuteRegion.bufferSize; + regions.push_back({ &readExecuteRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &readExecuteRegion, numRegionVMBytes }); + numRegionVMBytes += readExecuteRegion.bufferSize; + } + + // __BRANCH_STUBS + if ( branchStubsRegion.bufferSize != 0 ) { + // File offset + branchStubsRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += branchStubsRegion.bufferSize; + regions.push_back({ &branchStubsRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &branchStubsRegion, numRegionVMBytes }); + numRegionVMBytes += branchStubsRegion.bufferSize; + } + + // __DATA_CONST + if ( dataConstRegion.sizeInUse != 0 ) { + // File offset + dataConstRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += dataConstRegion.bufferSize; + regions.push_back({ &dataConstRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &dataConstRegion, numRegionVMBytes }); + numRegionVMBytes += dataConstRegion.bufferSize; + } + + // __BRANCH_GOTS + if ( branchGOTsRegion.bufferSize != 0 ) { + // File offset + branchGOTsRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += branchGOTsRegion.bufferSize; + regions.push_back({ &branchGOTsRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &branchGOTsRegion, numRegionVMBytes }); + numRegionVMBytes += branchGOTsRegion.bufferSize; + } + + // -sectcreate + // Align to 16k before we lay out all contiguous regions + numRegionFileBytes = align(numRegionFileBytes, 14); + for (CustomSegment& customSegment : customSegments) { + Region& region = *customSegment.parentRegion; + + region.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += region.bufferSize; + regions.push_back({ ®ion, 0 }); + // VM offset + // Note we can't align the vm offset in here + assert( (numRegionVMBytes % 4096) == 0); + regionsVMOrder.push_back({ ®ion, numRegionVMBytes }); + numRegionVMBytes += region.bufferSize; + + // Maybe add sections too + uint32_t sectionsToAdd = 0; + if ( customSegment.sections.size() > 1 ) { + // More than one section, so they all need names + sectionsToAdd = (uint32_t)customSegment.sections.size(); + } else if ( !customSegment.sections.front().sectionName.empty() ) { + // Only one section, but it has a name + sectionsToAdd = 1; + } + sectionsToAddToRegions[®ion] = sectionsToAdd; + } + numRegionVMBytes = align(numRegionVMBytes, 14); + + // __PRELINK_INFO + // Align to 16k + numRegionFileBytes = align(numRegionFileBytes, 14); + if ( prelinkInfoDict != nullptr ) + { + // File offset + prelinkInfoRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += prelinkInfoRegion.bufferSize; + regions.push_back({ &prelinkInfoRegion, 0 }); + + if ( !dataRegionFirstInVMOrder ) { + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes }); + numRegionVMBytes += prelinkInfoRegion.bufferSize; + } + + // Add a section too + sectionsToAddToRegions[&prelinkInfoRegion] = 1; + } + + // Split seg __DATA + // Align to 16k + numRegionFileBytes = align(numRegionFileBytes, 14); + if ( readWriteRegion.sizeInUse != 0 ) { + // File offset + readWriteRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += readWriteRegion.bufferSize; + regions.push_back({ &readWriteRegion, 0 }); + + if ( !dataRegionFirstInVMOrder ) { + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes }); + numRegionVMBytes += readWriteRegion.bufferSize; + } + } + + // Split seg __HIB + // Align to 16k + numRegionFileBytes = align(numRegionFileBytes, 14); + if ( hibernateRegion.sizeInUse != 0 ) { + // File offset + hibernateRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += hibernateRegion.bufferSize; + regions.push_back({ &hibernateRegion, 0 }); + + // VM offset was already handled earlier + } + + // Non split seg regions + // Align to 16k before we lay out all contiguous regions + numRegionFileBytes = align(numRegionFileBytes, 14); + for (Region& region : nonSplitSegRegions) { + region.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += region.bufferSize; + regions.push_back({ ®ion, 0 }); + // VM offset + // Note we can't align the vm offset in here + assert( (numRegionVMBytes % 4096) == 0); + regionsVMOrder.push_back({ ®ion, numRegionVMBytes }); + numRegionVMBytes += region.bufferSize; + } + numRegionVMBytes = align(numRegionVMBytes, 14); + + // __LINKEDIT + // Align to 16k + // File offset + numRegionFileBytes = align(numRegionFileBytes, 14); + _readOnlyRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += _readOnlyRegion.bufferSize; + regions.push_back({ &_readOnlyRegion, 0 }); + // VM offset + numRegionVMBytes = align(numRegionVMBytes, 14); + regionsVMOrder.push_back({ &_readOnlyRegion, numRegionVMBytes }); + numRegionVMBytes += _readOnlyRegion.bufferSize; + + // __LINKEDIT fixups sub region + // Align to 16k + numRegionFileBytes = align(numRegionFileBytes, 14); + if ( fixupsSubRegion.sizeInUse != 0 ) { + fixupsSubRegion.cacheFileOffset = numRegionFileBytes; + numRegionFileBytes += fixupsSubRegion.bufferSize; + //regions.push_back({ &fixupsSubRegion, 0 }); + + // VM offset + regionsVMOrder.push_back({ &fixupsSubRegion, numRegionVMBytes }); + numRegionVMBytes += fixupsSubRegion.bufferSize; + } + + const thread_command* unixThread = nullptr; + if (const DylibInfo* dylib = getKernelStaticExecutableInputFile()) { + unixThread = dylib->input->mappedFile.mh->unixThreadLoadCommand(); + } + + if (_is64) { + + const uint64_t cacheHeaderSize = sizeof(mach_header_64); + uint64_t cacheLoadCommandsSize = 0; + uint64_t cacheNumLoadCommands = 0; + + // UUID + ++cacheNumLoadCommands; + uint64_t uuidOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(uuid_command); + + // BUILD VERSION + ++cacheNumLoadCommands; + uint64_t buildVersionOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(build_version_command); + + // UNIX THREAD + uint64_t unixThreadOffset = 0; + if ( unixThread != nullptr ) { + ++cacheNumLoadCommands; + unixThreadOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += unixThread->cmdsize; + } + + // SYMTAB and DYSYMTAB + uint64_t symbolTableOffset = 0; + uint64_t dynSymbolTableOffset = 0; + if (const DylibInfo* dylib = getKernelStaticExecutableInputFile()) { + if ( dylib->input->mappedFile.mh->usesClassicRelocationsInKernelCollection() ) { + // SYMTAB + ++cacheNumLoadCommands; + symbolTableOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(symtab_command); + + // DYSYMTAB + ++cacheNumLoadCommands; + dynSymbolTableOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(dysymtab_command); + } + } + + // LC_DYLD_CHAINED_FIXUPS + // The pageableKC has one LC_DYLD_CHAINED_FIXUPS per kext, and 1 more on the top-level + // for the branch GOTs + uint64_t chainedFixupsOffset = 0; + if ( fixupsSubRegion.bufferSize != 0 ) { + ++cacheNumLoadCommands; + chainedFixupsOffset = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(linkedit_data_command); + } + + // Add an LC_SEGMENT_64 for each region + for (auto& regionAndOffset : regions) { + ++cacheNumLoadCommands; + regionAndOffset.second = cacheHeaderSize + cacheLoadCommandsSize; + cacheLoadCommandsSize += sizeof(segment_command_64); + + // Add space for any sections too + auto sectionIt = sectionsToAddToRegions.find(regionAndOffset.first); + if ( sectionIt != sectionsToAddToRegions.end() ) { + uint32_t numSections = sectionIt->second; + cacheLoadCommandsSize += sizeof(section_64) * numSections; + } + } + + // Add an LC_FILESET_ENTRY for each dylib + std::vector> dylibs; + for (const auto& dylib : sortedDylibs) { + ++cacheNumLoadCommands; + const char* dylibID = dylib.dylibID.c_str(); + dylibs.push_back({ &dylib, cacheHeaderSize + cacheLoadCommandsSize }); + uint64_t size = align(sizeof(fileset_entry_command) + strlen(dylibID) + 1, 3); + cacheLoadCommandsSize += size; + } + + uint64_t cacheHeaderRegionSize = cacheHeaderSize + cacheLoadCommandsSize; + + // Align the app cache header before the rest of the bytes + cacheHeaderRegionSize = align(cacheHeaderRegionSize, 14); + + assert(numRegionFileBytes <= numRegionVMBytes); + + _allocatedBufferSize = cacheHeaderRegionSize + numRegionVMBytes; + + // The fixup format cannot handle a KC over 1GB (64MB for arm64e auxKC). Error out if we exceed that + uint64_t cacheLimit = 1 << 30; + if ( (appCacheOptions.cacheKind == Options::AppCacheKind::auxKC) && (_options.archs == &dyld3::GradedArchs::arm64e) ) + cacheLimit = 64 * (1 << 20); + if ( _allocatedBufferSize >= cacheLimit ) { + _diagnostics.error("kernel collection size exceeds maximum size of %lld vs actual size of %lld", + cacheLimit, _allocatedBufferSize); + return; + } + + if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) { + _diagnostics.error("could not allocate buffer"); + return; + } + + // Assign region vm and buffer addresses now that we know the size of + // the cache header + { + // All vm offsets prior to the cache header are already correct + // All those after the cache header need to be shifted by the cache + // header size + bool seenCacheHeader = false; + for (const auto& regionAndVMOffset : regionsVMOrder) { + Region* region = regionAndVMOffset.first; + uint64_t vmOffset = regionAndVMOffset.second; + region->unslidLoadAddress = cacheBaseAddress + vmOffset; + if ( seenCacheHeader ) { + // Shift by the cache header size + region->unslidLoadAddress += cacheHeaderRegionSize; + } else { + // The offset is correct but add in the base address + seenCacheHeader = (region == &cacheHeaderRegion); + } + region->buffer = (uint8_t*)_fullAllocatedBuffer + (region->unslidLoadAddress - cacheBaseAddress); + } + } + + // Cache header + cacheHeaderRegion.bufferSize = cacheHeaderRegionSize; + cacheHeaderRegion.sizeInUse = cacheHeaderRegion.bufferSize; + cacheHeaderRegion.cacheFileOffset = 0; + cacheHeaderRegion.initProt = VM_PROT_READ; + cacheHeaderRegion.maxProt = VM_PROT_READ; + cacheHeaderRegion.name = "__TEXT"; + +#if 0 + for (const auto& regionAndVMOffset : regionsVMOrder) { + printf("0x%llx : %s\n", regionAndVMOffset.first->unslidLoadAddress, regionAndVMOffset.first->name.c_str()); + } +#endif + + CacheHeader64& header = cacheHeader; + header.header = (mach_header_64*)cacheHeaderRegion.buffer; + header.numLoadCommands = cacheNumLoadCommands; + header.loadCommandsSize = cacheLoadCommandsSize; + header.uuid = (uuid_command*)(cacheHeaderRegion.buffer + uuidOffset); + header.buildVersion = (build_version_command*)(cacheHeaderRegion.buffer + buildVersionOffset); + if ( unixThread != nullptr ) { + header.unixThread = (thread_command*)(cacheHeaderRegion.buffer + unixThreadOffset); + // Copy the contents here while we have the source pointer available + memcpy(header.unixThread, unixThread, unixThread->cmdsize); + } + + if ( symbolTableOffset != 0 ) { + header.symbolTable = (symtab_command*)(cacheHeaderRegion.buffer + symbolTableOffset); + } + + if ( dynSymbolTableOffset != 0 ) { + header.dynSymbolTable = (dysymtab_command*)(cacheHeaderRegion.buffer + dynSymbolTableOffset); + } + + if ( chainedFixupsOffset != 0 ) { + header.chainedFixups = (linkedit_data_command*)(cacheHeaderRegion.buffer + chainedFixupsOffset); + } + + for (auto& regionAndOffset : regions) { + assert(regionAndOffset.first->initProt != 0); + assert(regionAndOffset.first->maxProt != 0); + segment_command_64* loadCommand = (segment_command_64*)(cacheHeaderRegion.buffer + regionAndOffset.second); + header.segments.push_back({ loadCommand, regionAndOffset.first }); + } + for (const auto& dylibAndOffset : dylibs) { + fileset_entry_command* loadCommand = (fileset_entry_command*)(cacheHeaderRegion.buffer + dylibAndOffset.second); + header.dylibs.push_back({ loadCommand, dylibAndOffset.first }); + } + + // Move the offsets of all the other regions + // Split seg __TEXT + readOnlyTextRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // Split seg __TEXT_EXEC + readExecuteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // __BRANCH_STUBS + branchStubsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // Split seg __DATA_CONST + dataConstRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // __BRANCH_GOTS + branchGOTsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // Split seg __DATA + readWriteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // Split seg __HIB + hibernateRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // -sectcreate + for (Region& region : customDataRegions) { + region.cacheFileOffset += cacheHeaderRegion.sizeInUse; + } + + // Non split seg regions + for (Region& region : nonSplitSegRegions) { + region.cacheFileOffset += cacheHeaderRegion.sizeInUse; + } + + // __PRELINK_INFO + prelinkInfoRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // __LINKEDIT + _readOnlyRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + + // __LINKEDIT fixups sub region + fixupsSubRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse; + } else { + assert(false); + } +} + +void AppCacheBuilder::generateCacheHeader() { + if ( !_is64 ) + assert(0 && "Unimplemented"); + + { + // 64-bit + typedef Pointer64 P; + CacheHeader64& header = cacheHeader; + + // Write the header + macho_header

* mh = (macho_header

*)header.header; + mh->set_magic(MH_MAGIC_64); + mh->set_cputype(_options.archs->_orderedCpuTypes[0].type); + mh->set_cpusubtype(_options.archs->_orderedCpuTypes[0].subtype); + mh->set_filetype(MH_FILESET); + mh->set_ncmds((uint32_t)header.numLoadCommands); + mh->set_sizeofcmds((uint32_t)header.loadCommandsSize); + mh->set_flags(0); + mh->set_reserved(0); + + // FIXME: Move this to writeAppCacheHeader + { + macho_uuid_command

* cmd = (macho_uuid_command

*)header.uuid; + cmd->set_cmd(LC_UUID); + cmd->set_cmdsize(sizeof(uuid_command)); + cmd->set_uuid((uuid_t){}); + } + + // FIXME: Move this to writeAppCacheHeader + { + macho_build_version_command

* cmd = (macho_build_version_command

*)header.buildVersion; + cmd->set_cmd(LC_BUILD_VERSION); + cmd->set_cmdsize(sizeof(build_version_command)); + cmd->set_platform((uint32_t)_options.platform); + cmd->set_minos(0); + cmd->set_sdk(0); + cmd->set_ntools(0); + } + + // FIXME: Move this to writeAppCacheHeader + // LC_UNIXTHREAD was already memcpy()'ed from the source dylib when we allocated space for it + // We still need to slide its PC value here before we lose the information about the slide + if ( header.unixThread != nullptr ) { + const DylibInfo* dylib = getKernelStaticExecutableInputFile(); + const dyld3::MachOAnalyzer* ma = dylib->input->mappedFile.mh; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + uint64_t startAddress = dylib->input->mappedFile.mh->entryAddrFromThreadCmd(header.unixThread); + if ( (startAddress < info.vmAddr) || (startAddress >= (info.vmAddr + info.vmSize)) ) + return; + + uint64_t segSlide = dylib->cacheLocation[info.segIndex].dstCacheUnslidAddress - info.vmAddr; + startAddress += segSlide; + + macho_thread_command

* cmd = (macho_thread_command

*)header.unixThread; + cmd->set_thread_register(ma->entryAddrRegisterIndexForThreadCmd(), startAddress); + + stop = true; + }); + } + + if ( header.symbolTable != nullptr ) { + macho_symtab_command

* cmd = (macho_symtab_command

*)header.symbolTable; + cmd->set_cmd(LC_SYMTAB); + cmd->set_cmdsize(sizeof(symtab_command)); + cmd->set_symoff(0); + cmd->set_nsyms(0); + cmd->set_stroff(0); + cmd->set_strsize(0); + } + + if ( header.dynSymbolTable != nullptr ) { + macho_dysymtab_command

* cmd = (macho_dysymtab_command

*)header.dynSymbolTable; + cmd->set_cmd(LC_DYSYMTAB); + cmd->set_cmdsize(sizeof(dysymtab_command)); + cmd->set_ilocalsym(0); + cmd->set_nlocalsym(0); + cmd->set_iextdefsym(0); + cmd->set_nextdefsym(0); + cmd->set_iundefsym(0); + cmd->set_nundefsym(0); + cmd->set_tocoff(0); + cmd->set_ntoc(0); + cmd->set_modtaboff(0); + cmd->set_nmodtab(0); + cmd->set_extrefsymoff(0); + cmd->set_nextrefsyms(0); + cmd->set_indirectsymoff(0); + cmd->set_nindirectsyms(0); + cmd->set_extreloff(0); + cmd->set_nextrel(0); + cmd->set_locreloff(0); + cmd->set_nlocrel(0); + } + + if ( header.chainedFixups != nullptr ) { + macho_linkedit_data_command

* cmd = (macho_linkedit_data_command

*)header.chainedFixups; + cmd->set_cmd(LC_DYLD_CHAINED_FIXUPS); + cmd->set_cmdsize(sizeof(linkedit_data_command)); + cmd->set_dataoff(0); + cmd->set_datasize(0); + } + + // FIXME: Move this to writeAppCacheHeader + uint64_t segmentIndex = 0; + for (CacheHeader64::SegmentCommandAndRegion& cmdAndInfo : header.segments) { + macho_segment_command

* cmd = (macho_segment_command

*)cmdAndInfo.first; + Region* region = cmdAndInfo.second; + region->index = segmentIndex; + ++segmentIndex; + + assert(region->initProt != 0); + assert(region->maxProt != 0); + + const char* name = region->name.c_str(); + + cmd->set_cmd(LC_SEGMENT_64); + cmd->set_cmdsize(sizeof(segment_command_64)); + cmd->set_segname(name); + cmd->set_vmaddr(region->unslidLoadAddress); + cmd->set_vmsize(region->sizeInUse); + cmd->set_fileoff(region->cacheFileOffset); + cmd->set_filesize(region->sizeInUse); + cmd->set_maxprot(region->maxProt); + cmd->set_initprot(region->initProt); + cmd->set_nsects(0); + cmd->set_flags(0); + + if ( region == &readOnlyTextRegion ) { + // __PRELINK_TEXT should also get a section + cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64)); + cmd->set_nsects(1); + + macho_section

* section = (macho_section

*)((uint64_t)cmd + sizeof(*cmd)); + section->set_sectname("__text"); + section->set_segname(name); + section->set_addr(region->unslidLoadAddress); + section->set_size(region->sizeInUse); + section->set_offset((uint32_t)region->cacheFileOffset); + section->set_align(0); + section->set_reloff(0); + section->set_nreloc(0); + section->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + section->set_reserved1(0); + section->set_reserved2(0); + } else if ( region == &prelinkInfoRegion ) { + // __PRELINK_INFO should also get a section + cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64)); + cmd->set_nsects(1); + + macho_section

* section = (macho_section

*)((uint64_t)cmd + sizeof(*cmd)); + section->set_sectname("__info"); + section->set_segname(name); + section->set_addr(region->unslidLoadAddress); + section->set_size(region->sizeInUse); + section->set_offset((uint32_t)region->cacheFileOffset); + section->set_align(0); + section->set_reloff(0); + section->set_nreloc(0); + section->set_flags(S_REGULAR); + section->set_reserved1(0); + section->set_reserved2(0); + } else if ( region == &hibernateRegion ) { + // __HIB should also get a section + cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64)); + cmd->set_nsects(1); + + macho_section

* section = (macho_section

*)((uint64_t)cmd + sizeof(*cmd)); + section->set_sectname("__text"); + section->set_segname(name); + section->set_addr(region->unslidLoadAddress); + section->set_size(region->sizeInUse); + section->set_offset((uint32_t)region->cacheFileOffset); + section->set_align(0); + section->set_reloff(0); + section->set_nreloc(0); + section->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS); + section->set_reserved1(0); + section->set_reserved2(0); + } else { + // Custom segments may have sections + for (CustomSegment &customSegment : customSegments) { + if ( region != customSegment.parentRegion ) + continue; + + // Found a segment for this region. Now work out how many sections to emit + // Maybe add sections too + uint32_t sectionsToAdd = 0; + if ( customSegment.sections.size() > 1 ) { + // More than one section, so they all need names + sectionsToAdd = (uint32_t)customSegment.sections.size(); + } else if ( !customSegment.sections.front().sectionName.empty() ) { + // Only one section, but it has a name + sectionsToAdd = 1; + } else { + // Only 1 section, and it has no name, so don't add a section + continue; + } + + cmd->set_cmdsize(cmd->cmdsize() + (sizeof(section_64) * sectionsToAdd)); + cmd->set_nsects(sectionsToAdd); + uint8_t* bufferPos = (uint8_t*)cmd + sizeof(*cmd); + for (const CustomSegment::CustomSection& customSection : customSegment.sections) { + macho_section

* section = (macho_section

*)bufferPos; + section->set_sectname(customSection.sectionName.c_str()); + section->set_segname(name); + section->set_addr(region->unslidLoadAddress + customSection.offsetInRegion); + section->set_size(customSection.data.size()); + section->set_offset((uint32_t)(region->cacheFileOffset + customSection.offsetInRegion)); + section->set_align(0); + section->set_reloff(0); + section->set_nreloc(0); + section->set_flags(S_REGULAR); + section->set_reserved1(0); + section->set_reserved2(0); + + bufferPos += sizeof(section_64); + } + } + } + } + + // Write the dylibs. These are all we need for now to be able to walk the + // app cache + for (CacheHeader64::DylibCommandAndInfo& cmdAndInfo : header.dylibs) { + macho_fileset_entry_command

* cmd = (macho_fileset_entry_command

*)cmdAndInfo.first; + const DylibInfo* dylib = cmdAndInfo.second; + + const char* dylibID = dylib->dylibID.c_str(); + uint64_t size = align(sizeof(fileset_entry_command) + strlen(dylibID) + 1, 3); + + cmd->set_cmd(LC_FILESET_ENTRY); + cmd->set_cmdsize((uint32_t)size); + cmd->set_vmaddr(dylib->cacheLocation[0].dstCacheUnslidAddress); + cmd->set_fileoff(dylib->cacheLocation[0].dstCacheFileOffset); + cmd->set_entry_id(dylibID); + } + } +} + +void AppCacheBuilder::generatePrelinkInfo() { + if ( prelinkInfoDict == nullptr ) { + // The kernel doesn't need a prelink dictionary just for itself + bool needsPrelink = true; + if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) { + if ( sortedDylibs.size() == 1 ) + needsPrelink = false; + } + if ( needsPrelink ) { + _diagnostics.error("Expected prelink info dictionary"); + } + return; + } + + CFMutableArrayRef arrayRef = (CFMutableArrayRef)CFDictionaryGetValue(prelinkInfoDict, + CFSTR("_PrelinkInfoDictionary")); + if ( arrayRef == nullptr ) { + _diagnostics.error("Expected prelink info dictionary array"); + return; + } + + typedef std::pair DylibAndDiag; + __block std::unordered_map dylibs; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector& dependencies, + Diagnostics& dylibDiag, bool& stop) { + dylibs[dylibID] = { ma, &dylibDiag }; + }); + for (const InputDylib& dylib : codelessKexts) { + dylibs[dylib.dylibID] = { nullptr, nullptr }; + } + + __block std::list nonASCIIStrings; + auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) { + const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8); + if ( symbolName != nullptr ) + return symbolName; + + CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8); + char buffer[len + 1]; + if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) { + diags.error("Could not convert string to ASCII"); + return (const char*)nullptr; + } + buffer[len] = '\0'; + nonASCIIStrings.push_back(buffer); + return nonASCIIStrings.back().c_str(); + }; + + bool badKext = false; + CFIndex arrayCount = CFArrayGetCount(arrayRef); + for (CFIndex i = 0; i != arrayCount; ++i) { + CFMutableDictionaryRef dictRef = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(arrayRef, i); + + CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(dictRef, CFSTR("CFBundleIdentifier")); + if ( bundleIDRef == nullptr ) { + _diagnostics.error("Cannot get bundle ID for dylib"); + return; + } + + const char* bundleIDStr = getString(_diagnostics, bundleIDRef); + if ( _diagnostics.hasError() ) + return; + + auto dylibIt = dylibs.find(bundleIDStr); + if ( dylibIt == dylibs.end() ) { + _diagnostics.error("Cannot get dylib for bundle ID %s", bundleIDStr); + return; + } + const dyld3::MachOAnalyzer *ma = dylibIt->second.first; + Diagnostics* dylibDiag = dylibIt->second.second; + // Skip codeless kext's + if ( ma == nullptr ) + continue; + uint64_t loadAddress = ma->preferredLoadAddress(); + + // _PrelinkExecutableLoadAddr + CFNumberRef loadAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &loadAddress); + CFDictionarySetValue(dictRef, CFSTR("_PrelinkExecutableLoadAddr"), loadAddrRef); + CFRelease(loadAddrRef); + + // _PrelinkExecutableSourceAddr + CFNumberRef sourceAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &loadAddress); + CFDictionarySetValue(dictRef, CFSTR("_PrelinkExecutableSourceAddr"), sourceAddrRef); + CFRelease(sourceAddrRef); + + // _PrelinkKmodInfo + __block uint64_t kmodInfoAddress = 0; + + // Check for a global first + __block bool found = false; + { + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + found = ma->findExportedSymbol(_diagnostics, "_kmod_info", true, foundInfo, nullptr); + if ( found ) { + kmodInfoAddress = loadAddress + foundInfo.value; + } + } + // And fall back to a local if we need to + if ( !found ) { + ma->forEachLocalSymbol(_diagnostics, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, + uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( strcmp(aSymbolName, "_kmod_info") == 0 ) { + kmodInfoAddress = n_value; + found = true; + stop = true; + } + }); + } + + if ( found ) { + CFNumberRef kmodInfoAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &kmodInfoAddress); + CFDictionarySetValue(dictRef, CFSTR("_PrelinkKmodInfo"), kmodInfoAddrRef); + CFRelease(kmodInfoAddrRef); + + // Since we have a reference to the kmod info anyway, set its address field to the correct value + assert(_is64); + uint64_t kmodInfoVMOffset = kmodInfoAddress - loadAddress; + dyld3::MachOAppCache::KModInfo64_v1* kmodInfo = (dyld3::MachOAppCache::KModInfo64_v1*)((uint8_t*)ma + kmodInfoVMOffset); + if ( kmodInfo->info_version != 1 ) { + dylibDiag->error("unsupported kmod_info version of %d", kmodInfo->info_version); + badKext = true; + continue; + } + __block uint64_t textSegmnentVMAddr = 0; + __block uint64_t textSegmnentVMSize = 0; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + if ( !strcmp(info.segName, "__TEXT") ) { + textSegmnentVMAddr = info.vmAddr; + textSegmnentVMSize = info.vmSize; + stop = true; + } + }); + kmodInfo->address = textSegmnentVMAddr; + kmodInfo->size = textSegmnentVMSize; + } + } + + CFErrorRef errorRef = nullptr; + CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict, + kCFPropertyListXMLFormat_v1_0, 0, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + _diagnostics.error("Could not serialise plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(xmlData); + CFRelease(errorRef); + return; + } else { + CFIndex xmlDataLength = CFDataGetLength(xmlData); + if ( xmlDataLength > prelinkInfoRegion.bufferSize ) { + _diagnostics.error("Overflow in prelink info segment. 0x%llx vs 0x%llx", + (uint64_t)xmlDataLength, prelinkInfoRegion.bufferSize); + CFRelease(xmlData); + return; + } + + // Write the prelink info in to the buffer + memcpy(prelinkInfoRegion.buffer, CFDataGetBytePtr(xmlData), xmlDataLength); + CFRelease(xmlData); + } + + if ( badKext && _diagnostics.noError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); + } +} + +bool AppCacheBuilder::addCustomSection(const std::string& segmentName, + CustomSegment::CustomSection section) { + for (CustomSegment& segment: customSegments) { + if ( segment.segmentName != segmentName ) + continue; + + // Found a matching segment + // Make sure we don't have a section with this name already + if ( section.sectionName.empty() ) { + // We can't add a segment only section if other sections exist + _diagnostics.error("Cannot add empty section name with segment '%s' as other sections exist on that segment", + segmentName.c_str()); + return false; + } + + for (const CustomSegment::CustomSection& existingSection : segment.sections) { + if ( existingSection.sectionName.empty() ) { + // We can't add a section with a name if an existing section exists with no name + _diagnostics.error("Cannot add section named '%s' with segment '%s' as segment has existing nameless section", + segmentName.c_str(), section.sectionName.c_str()); + return false; + } + if ( existingSection.sectionName == section.sectionName ) { + // We can't add a section with the same name as an existing one + _diagnostics.error("Cannot add section named '%s' with segment '%s' as section already exists", + segmentName.c_str(), section.sectionName.c_str()); + return false; + } + } + segment.sections.push_back(section); + return true; + } + + // Didn't find a segment, so add a new one + CustomSegment segment; + segment.segmentName = segmentName; + segment.sections.push_back(section); + customSegments.push_back(segment); + return true; +} + +void AppCacheBuilder::setExistingKernelCollection(const dyld3::MachOAppCache* appCacheMA) { + existingKernelCollection = appCacheMA; +} + +void AppCacheBuilder::setExistingPageableKernelCollection(const dyld3::MachOAppCache* appCacheMA) { + pageableKernelCollection = appCacheMA; +} + +void AppCacheBuilder::setExtraPrelinkInfo(CFDictionaryRef dictionary) { + extraPrelinkInfo = dictionary; +} + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + +void AppCacheBuilder::buildAppCache(const std::vector& dylibs) +{ + uint64_t t1 = mach_absolute_time(); + + // make copy of dylib list and sort + makeSortedDylibs(dylibs); + + // Set the chained pointer format + // x86_64 uses unaligned fixups + if ( (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h) ) { + chainedPointerFormat = DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE; + } else { + chainedPointerFormat = DYLD_CHAINED_PTR_64_KERNEL_CACHE; + } + + // If we have only codeless kexts, then error out + if ( sortedDylibs.empty() ) { + if ( codelessKexts.empty() ) { + _diagnostics.error("No binaries or codeless kexts were provided"); + } else { + _diagnostics.error("Cannot build collection without binaries as only %lx codeless kexts provided", + codelessKexts.size()); + } + return; + } + + // assign addresses for each segment of each dylib in new cache + assignSegmentRegionsAndOffsets(); + if ( _diagnostics.hasError() ) + return; + + // allocate space used by largest possible cache plus room for LINKEDITS before optimization + allocateBuffer(); + if ( _diagnostics.hasError() ) + return; + + assignSegmentAddresses(); + + generateCacheHeader(); + + // copy all segments into cache + uint64_t t2 = mach_absolute_time(); + copyRawSegments(); + + // rebase all dylibs for new location in cache + uint64_t t3 = mach_absolute_time(); + if ( appCacheOptions.cacheKind == Options::AppCacheKind::auxKC ) { + // We can have text fixups in the auxKC so ASLR should just track the whole buffer + __block const Region* firstDataRegion = nullptr; + __block const Region* lastDataRegion = nullptr; + forEachRegion(^(const Region ®ion) { + if ( (firstDataRegion == nullptr) || (region.buffer < firstDataRegion->buffer) ) + firstDataRegion = ®ion; + if ( (lastDataRegion == nullptr) || (region.buffer > lastDataRegion->buffer) ) + lastDataRegion = ®ion; + }); + + if ( firstDataRegion != nullptr ) { + uint64_t size = (lastDataRegion->buffer - firstDataRegion->buffer) + lastDataRegion->bufferSize; + _aslrTracker.setDataRegion(firstDataRegion->buffer, size); + } + } else { + const Region* firstDataRegion = nullptr; + const Region* lastDataRegion = nullptr; + if ( hibernateRegion.sizeInUse != 0 ) { + firstDataRegion = &hibernateRegion; + lastDataRegion = &hibernateRegion; + } + + if ( dataConstRegion.sizeInUse != 0 ) { + if ( firstDataRegion == nullptr ) + firstDataRegion = &dataConstRegion; + if ( (lastDataRegion == nullptr) || (dataConstRegion.buffer > lastDataRegion->buffer) ) + lastDataRegion = &dataConstRegion; + } + + if ( branchGOTsRegion.bufferSize != 0 ) { + if ( firstDataRegion == nullptr ) + firstDataRegion = &branchGOTsRegion; + if ( (lastDataRegion == nullptr) || (branchGOTsRegion.buffer > lastDataRegion->buffer) ) + lastDataRegion = &branchGOTsRegion; + } + + if ( readWriteRegion.sizeInUse != 0 ) { + // __DATA might be before __DATA_CONST in an auxKC + if ( (firstDataRegion == nullptr) || (readWriteRegion.buffer < firstDataRegion->buffer) ) + firstDataRegion = &readWriteRegion; + if ( (lastDataRegion == nullptr) || (readWriteRegion.buffer > lastDataRegion->buffer) ) + lastDataRegion = &readWriteRegion; + } + + for (const Region& region : nonSplitSegRegions) { + // Assume writable regions have fixups to emit + // Note, third party kext's have __TEXT fixups, so assume all of these have fixups + // LINKEDIT is already elsewhere + if ( readWriteRegion.sizeInUse != 0 ) { + assert(region.buffer >= readWriteRegion.buffer); + } + if ( firstDataRegion == nullptr ) + firstDataRegion = ®ion; + if ( (lastDataRegion == nullptr) || (region.buffer > lastDataRegion->buffer) ) + lastDataRegion = ®ion; + } + + if ( firstDataRegion != nullptr ) { + uint64_t size = (lastDataRegion->buffer - firstDataRegion->buffer) + lastDataRegion->bufferSize; + _aslrTracker.setDataRegion(firstDataRegion->buffer, size); + } + } + adjustAllImagesForNewSegmentLocations(cacheBaseAddress, _aslrTracker, nullptr, nullptr); + if ( _diagnostics.hasError() ) + return; + + // Once we have the final addresses, we can emit the prelink info segment + generatePrelinkInfo(); + if ( _diagnostics.hasError() ) + return; + + // build ImageArray for dyld3, which has side effect of binding all cached dylibs + uint64_t t4 = mach_absolute_time(); + processFixups(); + if ( _diagnostics.hasError() ) + return; + + uint64_t t5 = mach_absolute_time(); + + // optimize away stubs + uint64_t t6 = mach_absolute_time(); + { + __block std::vector> images; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector& dependencies, + Diagnostics& dylibDiag, bool& stop) { + images.push_back({ ma, dylibID.c_str() }); + }); + // FIXME: Should we keep the same never stub eliminate symbols? Eg, for gmalloc. + const char* const neverStubEliminateSymbols[] = { + nullptr + }; + + uint64_t cacheUnslidAddr = cacheBaseAddress; + int64_t cacheSlide = (long)_fullAllocatedBuffer - cacheUnslidAddr; + optimizeAwayStubs(images, cacheSlide, cacheUnslidAddr, + nullptr, neverStubEliminateSymbols); + } + + // 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 + uint64_t t7 = mach_absolute_time(); + { + __block std::vector> images; + __block std::set imagesToStrip; + __block const dyld3::MachOAnalyzer* kernelMA = nullptr; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector& dependencies, + Diagnostics& dylibDiag, bool& stop) { + if ( stripMode == DylibStripMode::stripNone ) { + // If the binary didn't have a strip mode, then use the global mode + switch (appCacheOptions.cacheKind) { + case Options::AppCacheKind::none: + assert("Unhandled kind"); + break; + case Options::AppCacheKind::kernel: + switch (appCacheOptions.stripMode) { + case Options::StripMode::none: + break; + case Options::StripMode::all: + stripMode = CacheBuilder::DylibStripMode::stripAll; + break; + case Options::StripMode::allExceptKernel: + // Strip all binaries which are not the kernel + if ( kernelMA == nullptr ) { + kernelMA = getKernelStaticExecutableFromCache(); + } + if ( ma != kernelMA ) + stripMode = CacheBuilder::DylibStripMode::stripAll; + break; + } + break; + case Options::AppCacheKind::pageableKC: + assert("Unhandled kind"); + break; + case Options::AppCacheKind::kernelCollectionLevel2: + assert("Unhandled kind"); + break; + case Options::AppCacheKind::auxKC: + assert("Unhandled kind"); + break; + } + } + images.push_back({ ma, dylibID.c_str(), stripMode }); + }); + optimizeLinkedit(nullptr, images); + + // update final readOnly region size + if ( !_is64 ) + assert(0 && "Unimplemented"); + + { + // 64-bit + typedef Pointer64 P; + CacheHeader64& header = cacheHeader; + + for (CacheHeader64::SegmentCommandAndRegion& cmdAndRegion : header.segments) { + if (cmdAndRegion.second != &_readOnlyRegion) + continue; + cmdAndRegion.first->vmsize = _readOnlyRegion.sizeInUse; + cmdAndRegion.first->filesize = _readOnlyRegion.sizeInUse; + break; + } + } + } + + uint64_t t8 = mach_absolute_time(); + + uint64_t t9 = mach_absolute_time(); + + // Add fixups to rebase/bind the app cache + writeFixups(); + { + if ( !_is64 ) + assert(0 && "Unimplemented"); + + // update final readOnly region size + { + // 64-bit + typedef Pointer64 P; + CacheHeader64& header = cacheHeader; + + for (CacheHeader64::SegmentCommandAndRegion& cmdAndRegion : header.segments) { + if (cmdAndRegion.second != &_readOnlyRegion) + continue; + cmdAndRegion.first->vmsize = _readOnlyRegion.sizeInUse; + cmdAndRegion.first->filesize = _readOnlyRegion.sizeInUse; + break; + } + } + } + + // FIXME: We could move _aslrTracker to a worker thread to be destroyed as we don't need it + // after this point + + uint64_t t10 = mach_absolute_time(); + + generateUUID(); + if ( _diagnostics.hasError() ) + return; + + uint64_t t11 = mach_absolute_time(); + + if ( _options.verbose ) { + 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 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)); + } +} + +void AppCacheBuilder::fipsSign() +{ + if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel ) + return; + + // find com.apple.kec.corecrypto in collection being built + __block const dyld3::MachOAnalyzer* kextMA = nullptr; + forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, + DylibStripMode stripMode, const std::vector& dependencies, + Diagnostics& dylibDiag, bool& stop) { + if ( dylibID == "com.apple.kec.corecrypto" ) { + kextMA = ma; + stop = true; + } + }); + + if ( kextMA == nullptr ) { + _diagnostics.warning("Could not find com.apple.kec.corecrypto, skipping FIPS sealing"); + return; + } + + // find location in com.apple.kec.corecrypto to store hash of __text section + uint64_t hashStoreSize; + const void* hashStoreLocation = kextMA->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize); + if ( hashStoreLocation == nullptr ) { + _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in com.apple.kec.corecrypto, skipping FIPS sealing"); + return; + } + if ( hashStoreSize != 32 ) { + _diagnostics.warning("__TEXT/__fips_hmacs section in com.apple.kec.corecrypto is not 32 bytes in size, skipping FIPS sealing"); + return; + } + + // compute hmac hash of __text section. It may be in __TEXT_EXEC or __TEXT + uint64_t textSize; + const void* textLocation = kextMA->findSectionContent("__TEXT", "__text", textSize); + if ( textLocation == nullptr ) { + textLocation = kextMA->findSectionContent("__TEXT_EXEC", "__text", textSize); + } + if ( textLocation == nullptr ) { + _diagnostics.warning("Could not find __TEXT/__text section in com.apple.kec.corecrypto, skipping FIPS sealing"); + return; + } + unsigned char hmac_key = 0; + CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation +} + +void AppCacheBuilder::generateUUID() { + uint8_t* uuidLoc = cacheHeader.uuid->uuid; + assert(uuid_is_null(uuidLoc)); + + CCDigestRef digestRef = CCDigestCreate(kCCDigestSHA256); + forEachRegion(^(const Region ®ion) { + if ( _diagnostics.hasError() ) + return; + if ( region.sizeInUse == 0 ) + return; + int result = CCDigestUpdate(digestRef, region.buffer, region.sizeInUse); + if ( result != 0 ) { + _diagnostics.error("Could not generate UUID: %d", result); + return; + } + }); + if ( !_diagnostics.hasError() ) { + uint8_t buffer[CCDigestGetOutputSize(kCCDigestSHA256)]; + int result = CCDigestFinal(digestRef, buffer); + memcpy(cacheHeader.uuid->uuid, buffer, sizeof(cacheHeader.uuid->uuid)); + if ( result != 0 ) { + _diagnostics.error("Could not finalize UUID: %d", result); + } + } + CCDigestDestroy(digestRef); + if ( _diagnostics.hasError() ) + return; + + // Update the prelink info dictionary too + if ( prelinkInfoDict != nullptr ) { + CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, &cacheHeader.uuid->uuid[0], sizeof(cacheHeader.uuid->uuid)); + CFDictionarySetValue(prelinkInfoDict, CFSTR("_PrelinkKCID"), dataRef); + CFRelease(dataRef); + + CFErrorRef errorRef = nullptr; + CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict, + kCFPropertyListXMLFormat_v1_0, 0, &errorRef); + if (errorRef != nullptr) { + CFStringRef errorString = CFErrorCopyDescription(errorRef); + _diagnostics.error("Could not serialise plist because :%s", + CFStringGetCStringPtr(errorString, kCFStringEncodingASCII)); + CFRelease(xmlData); + CFRelease(errorRef); + return; + } else { + CFIndex xmlDataLength = CFDataGetLength(xmlData); + if ( xmlDataLength > prelinkInfoRegion.bufferSize ) { + _diagnostics.error("Overflow in prelink info segment. 0x%llx vs 0x%llx", + (uint64_t)xmlDataLength, prelinkInfoRegion.bufferSize); + CFRelease(xmlData); + return; + } + + // Write the prelink info in to the buffer + memcpy(prelinkInfoRegion.buffer, CFDataGetBytePtr(xmlData), xmlDataLength); + CFRelease(xmlData); + } + } +} + + +void AppCacheBuilder::writeFile(const std::string& path) +{ + std::string pathTemplate = path + "-XXXXXX"; + size_t templateLen = strlen(pathTemplate.c_str())+2; + BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen); + strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen); + int fd = mkstemp(pathTemplateSpace); + if ( fd == -1 ) { + _diagnostics.error("could not open file %s", pathTemplateSpace); + return; + } + uint64_t cacheFileSize = 0; + // FIXME: Do we ever need to avoid allocating space for zero fill? + cacheFileSize = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse; + + // set final cache file size (may help defragment file) + ::ftruncate(fd, cacheFileSize); + + // Write the whole buffer + uint64_t writtenSize = pwrite(fd, (const uint8_t*)_fullAllocatedBuffer, cacheFileSize, 0); + if (writtenSize == cacheFileSize) { + ::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 whole file. %lld bytes out of %lld were written", + writtenSize, cacheFileSize); + return; + } + ::close(fd); + ::unlink(pathTemplateSpace); +} + +void AppCacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) const { + bufferSize = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse; + buffer = (uint8_t*)malloc(bufferSize); + + forEachRegion(^(const Region ®ion) { + if ( region.sizeInUse == 0 ) + return; + memcpy(buffer + region.cacheFileOffset, (const uint8_t*)region.buffer, region.sizeInUse); + }); +} diff --git a/dyld3/shared-cache/AppCacheBuilder.h b/dyld3/shared-cache/AppCacheBuilder.h new file mode 100644 index 0000000..07dba23 --- /dev/null +++ b/dyld3/shared-cache/AppCacheBuilder.h @@ -0,0 +1,204 @@ +/* + * 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 AppCacheBuilder_h +#define AppCacheBuilder_h + +#include "CacheBuilder.h" +#include "MachOFileAbstraction.hpp" +#include "MachOAppCache.h" + +#include + +#include + +class AppCacheBuilder final : public CacheBuilder { +public: + struct Options { + enum class AppCacheKind { + none, + kernel, // The base first party kernel collection with xnu and boot time kexts + pageableKC, // Other first party kexts which are usually only for some HW, eg, different GPUs + kernelCollectionLevel2, // Placeholder until we find a use for this + auxKC, // Third party kexts, or Apple kexts updated out of band with the OS, if any + }; + + enum class StripMode { + none, // Don't strip anything + all, // Strip everything + allExceptKernel // Strip everything other than the static kernel + }; + + AppCacheKind cacheKind = AppCacheKind::none; + StripMode stripMode = StripMode::none; + }; + AppCacheBuilder(const DyldSharedCache::CreateOptions& dyldCacheOptions, const Options& appCacheOptions, + const dyld3::closure::FileSystem& fileSystem); + ~AppCacheBuilder() override final; + + // Wraps up a loaded macho with the other information required by kext's, eg, the list of dependencies + struct InputDylib { + LoadedMachO dylib; + std::string dylibID; + std::vector dylibDeps; + CFDictionaryRef infoPlist = nullptr; + std::string bundlePath; + Diagnostics* errors = nullptr; + CacheBuilder::DylibStripMode stripMode = CacheBuilder::DylibStripMode::stripNone; + }; + + // The payload for -sectcreate + struct CustomSegment { + struct CustomSection { + std::string sectionName; + std::vector data; + uint64_t offsetInRegion = 0; + }; + + std::string segmentName; + std::vector sections; + Region* parentRegion = nullptr; + }; + + bool addCustomSection(const std::string& segmentName, + CustomSegment::CustomSection section); + void setExistingKernelCollection(const dyld3::MachOAppCache* appCacheMA); + void setExistingPageableKernelCollection(const dyld3::MachOAppCache* appCacheMA); + void setExtraPrelinkInfo(CFDictionaryRef dictionary); + void buildAppCache(const std::vector& dylibs); + void writeFile(const std::string& path); + void writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) const; + +private: + + enum { + // The fixup format can't support more than 3 levels right now + numFixupLevels = 4 + }; + + struct AppCacheDylibInfo : CacheBuilder::DylibInfo + { + // From CacheBuilder::DylibInfo + // const LoadedMachO* input; + // std::string dylibID; + // std::vector cacheLocation; + + DylibStripMode stripMode = DylibStripMode::stripNone; + std::vector dependencies; + CFDictionaryRef infoPlist = nullptr; + Diagnostics* errors = nullptr; + std::string bundlePath; + }; + + static_assert(std::is_move_constructible::value); + + void forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) override final; + + void forEachCacheDylib(void (^callback)(const dyld3::MachOAnalyzer* ma, + const std::string& dylibID, + DylibStripMode stripMode, + const std::vector& dependencies, + Diagnostics& dylibDiag, + bool& stop)) const; + + const DylibInfo* getKernelStaticExecutableInputFile() const; + const dyld3::MachOAnalyzer* getKernelStaticExecutableFromCache() const; + + void makeSortedDylibs(const std::vector& dylibs); + void allocateBuffer(); + void assignSegmentRegionsAndOffsets(); + void copyRawSegments(); + void assignSegmentAddresses(); + void generateCacheHeader(); + void generatePrelinkInfo(); + uint32_t getCurrentFixupLevel() const; + void processFixups(); + void writeFixups(); + void fipsSign(); + void generateUUID(); + uint64_t numRegions() const; + uint64_t numBranchRelocationTargets(); + uint64_t fixupsPageSize() const; + uint64_t numWritablePagesToFixup(uint64_t numBytesToFixup) const; + bool fixupsArePerKext() const; + void forEachRegion(void (^callback)(const Region& region)) const; + + Options appCacheOptions; + const dyld3::MachOAppCache* existingKernelCollection = nullptr; + const dyld3::MachOAppCache* pageableKernelCollection = nullptr; + CFDictionaryRef extraPrelinkInfo = nullptr; + + std::vector sortedDylibs; + std::vector codelessKexts; + std::vector customSegments; + Region cacheHeaderRegion; + Region readExecuteRegion; + Region branchStubsRegion; + Region dataConstRegion; + Region branchGOTsRegion; + Region readWriteRegion; + Region hibernateRegion; + Region readOnlyTextRegion; + std::list customDataRegions; // -sectcreate + Region prelinkInfoRegion; + std::list nonSplitSegRegions; + // Region _readOnlyRegion; // This comes from the base class + Region fixupsSubRegion; // This will be in the __LINKEDIT when we write the file + + // This is the base address from the statically linked kernel, or 0 for other caches + uint64_t cacheBaseAddress = 0; + // For x86_64 only, we want to keep the address of the hibernate segment from xnu + // We'll ensure this is mapped lower than the cache base address + uint64_t hibernateAddress = 0; + + uint16_t chainedPointerFormat = 0; + + // The dictionary we ultimately store in the __PRELINK_INFO region + CFMutableDictionaryRef prelinkInfoDict = nullptr; + + // Cache header fields + // FIXME: 32-bit + + struct CacheHeader64 { + typedef std::pair SegmentCommandAndRegion; + typedef std::pair DylibCommandAndInfo; + + mach_header_64* header = nullptr; + + uint64_t numLoadCommands = 0; + uint64_t loadCommandsSize = 0; + + uuid_command* uuid = nullptr; + build_version_command* buildVersion = nullptr; + thread_command* unixThread = nullptr; + symtab_command* symbolTable = nullptr; + dysymtab_command* dynSymbolTable = nullptr; + linkedit_data_command* chainedFixups = nullptr; + std::vector segments; + std::vector dylibs; + }; + + CacheHeader64 cacheHeader; +}; + +#endif /* AppCacheBuilder_h */ diff --git a/launch-cache/Architectures.hpp b/dyld3/shared-cache/Architectures.hpp similarity index 100% rename from launch-cache/Architectures.hpp rename to dyld3/shared-cache/Architectures.hpp diff --git a/dyld3/shared-cache/BuilderUtils.h b/dyld3/shared-cache/BuilderUtils.h deleted file mode 100644 index dc2c5e3..0000000 --- a/dyld3/shared-cache/BuilderUtils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2017 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef BuilderUtils_h -#define BuilderUtils_h - -dispatch_group_t buildGroup(); -void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot); -bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash, - bool emitDevCaches, bool isLocallyBuiltCache); - -#endif /* BuilderUtils_h */ diff --git a/dyld3/shared-cache/BuilderUtils.mm b/dyld3/shared-cache/BuilderUtils.mm deleted file mode 100644 index 7d76766..0000000 --- a/dyld3/shared-cache/BuilderUtils.mm +++ /dev/null @@ -1,317 +0,0 @@ -/* - * 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 -#include -#include -#include -#include // std::setfill, std::setw -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "Manifest.h" -#include "Diagnostics.h" -#include "FileUtils.h" - -#include "BuilderUtils.h" - -static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT); -static dispatch_group_t build_group = dispatch_group_create(); - -dispatch_group_t buildGroup() { - return build_group; -} - -void insertFileInBom(const std::string& path, BOMBom bom) -{ - std::vector components; - std::vector processed_components; - std::stringstream ss(path); - std::string item; - - while (std::getline(ss, item, '/')) { - if (!item.empty()) { - components.push_back(item); - } - } - - std::string partialPath = "."; - std::string lastComponent = components.back(); - components.pop_back(); - BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType); - BOMFSObjectSetFlags(fso, B_PATHONLY); - BOMFSObjectSetPathName(fso, ".", true); - BOMFSObjectSetShortName(fso, ".", true); - (void)BOMBomInsertFSObject(bom, fso, false); - BOMFSObjectFree(fso); - - for (const auto& component : components) { - partialPath = partialPath + "/" + component; - fso = BOMFSObjectNew(BOMDirectoryType); - BOMFSObjectSetFlags(fso, B_PATHONLY); - BOMFSObjectSetPathName(fso, partialPath.c_str(), true); - BOMFSObjectSetShortName(fso, component.c_str(), true); - (void)BOMBomInsertFSObject(bom, fso, false); - BOMFSObjectFree(fso); - } - - partialPath = partialPath + "/" + lastComponent; - fso = BOMFSObjectNew(BOMFileType); - BOMFSObjectSetFlags(fso, B_PATHONLY); - BOMFSObjectSetPathName(fso, partialPath.c_str(), true); - BOMFSObjectSetShortName(fso, lastComponent.c_str(), true); - (void)BOMBomInsertFSObject(bom, fso, false); - BOMFSObjectFree(fso); -} - -void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot) -{ - mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755); - - manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) { - auto config = manifest.configuration(configName); - std::vector prodBomPaths; - std::vector devBomPaths; - - std::string runtimePath = "/System/Library/Caches/com.apple.dyld/"; - if (manifest.platform() == dyld3::Platform::macOS) { - runtimePath = "/private/var/db/dyld/"; - } - - for (auto& arch : config.architectures) { - std::string cachePath = "dyld_shared_cache_" + arch.first; - prodBomPaths.push_back(cachePath); - if (manifest.platform() != dyld3::Platform::macOS) { - cachePath += ".development"; - } - devBomPaths.push_back(cachePath); - char buffer[MAXPATHLEN]; - sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str()); - BOMBom bom = BOMBomNew(buffer); - for (auto& path : prodBomPaths) { - insertFileInBom(runtimePath + path, bom); - } - BOMBomFree(bom); - - sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str()); - bom = BOMBomNew(buffer); - for (auto& path : devBomPaths) { - insertFileInBom(runtimePath + path, bom); - } - BOMBomFree(bom); - - sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str()); - bom = BOMBomNew(buffer); - for (auto& path : prodBomPaths) { - insertFileInBom(runtimePath + path, bom); - } - for (auto& path : devBomPaths) { - insertFileInBom(runtimePath + path, bom); - } - BOMBomFree(bom); - } - }); -} - -bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, - bool skipWrites, bool agileChooseSHA256CdHash, 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); - std::vector> dedupedCacheSets; - if (dedupe) { - manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) { - auto config = manifest.configuration(configName); - bool dupeFound = false; - - for (auto& cacheSet : dedupedCacheSets) { - if (config == manifest.configuration(*cacheSet.begin())) { - cacheSet.insert(configName); - dupeFound = true; - break; - } - } - - if (!dupeFound) { - std::set temp; - temp.insert(configName); - dedupedCacheSets.push_back(temp); - } - }); - } else { - manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) { - std::set temp; - temp.insert(configName); - dedupedCacheSets.push_back(temp); - }); - } - - std::vector buildQueue; - - for (auto& cacheSet : dedupedCacheSets) { - //FIXME we may want to consider moving to hashes of UUID sets - std::string setName; - - for (auto& archName : cacheSet) { - if (!setName.empty()) { - setName += "|"; - } - setName += archName; - } - - std::stringstream fileNameStream; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - std::array digest = { 0 }; - CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]); -#pragma clang diagnostic pop - - fileNameStream << std::hex << std::uppercase << std::setfill('0'); - for (int c : digest) { - fileNameStream << std::setw(2) << c; - } - - std::string fileName(fileNameStream.str()); - - if (dedupe) { - for (auto& config : cacheSet) { - if (!skipWrites) { - int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str()); - if (err) { - diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err); - } - } - } - } - - manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, - &buildQueue, &cacheSet, 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) { - runtimePath = "/private/var/db/dyld/"; - } - if (dedupe) { - configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath; - } else { - 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, - isLocallyBuiltCache, skipWrites, verbose)); - } else { - 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)); - } - }); - } - - __block bool cacheBuildFailure = false; - __block std::set warnings; - __block std::set errors; - - dispatch_sync(warningQueue, ^{ - auto manifestWarnings = diags.warnings(); - //warnings.insert(manifestWarnings.begin(), manifestWarnings.end()); - }); - - bool requuiresConcurrencyLimit = false; - dispatch_semaphore_t concurrencyLimit = NULL; - // Limit cuncurrency to 8 threads for machines with 32GB of RAM and to 1 thread if we have 4GB or less of memory - uint64_t memSize = 0; - size_t sz = sizeof(memSize);; - if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) { - if ( memSize <= 0x100000000ULL ) { - fprintf(stderr, "Detected 4Gb or less of memory, limiting concurrency to 1 thread\n"); - requuiresConcurrencyLimit = true; - concurrencyLimit = dispatch_semaphore_create(1); - } else if ( memSize <= 0x800000000ULL ) { - fprintf(stderr, "Detected 32Gb or less of memory, limiting concurrency to 8 threads\n"); - requuiresConcurrencyLimit = true; - concurrencyLimit = dispatch_semaphore_create(8); - } - } - - dispatch_apply(buildQueue.size(), queue, ^(size_t index) { - auto queueEntry = buildQueue[index]; - pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str()); - - // Horrible hack to limit concurrency in low spec build machines. - if (requuiresConcurrencyLimit) { dispatch_semaphore_wait(concurrencyLimit, DISPATCH_TIME_FOREVER); } - DyldSharedCache::CreateResults results = DyldSharedCache::create(queueEntry.options, queueEntry.fileSystem, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables); - if (requuiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); } - - dispatch_sync(warningQueue, ^{ - warnings.insert(results.warnings.begin(), results.warnings.end()); - bool chooseSecondCdHash = agileChooseSHA256CdHash; - if (agileChooseSHA256CdHash && !results.agileSignature) { - // Ignore this option for caches that are not signed agile (which is the majority). - chooseSecondCdHash = false; - } - for (const auto& configName : queueEntry.configNames) { - auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archs->name()).results; - for (const auto& mh : results.evictions) { - configResults.exclude(mh, "VM overflow, evicting"); - } - configResults.warnings = results.warnings; - if (queueEntry.options.optimizeStubs) { - configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst; - } else { - 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()); - cacheBuildFailure = true; - } else if (skipWrites) { - fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str()); - } - }); - - // print any warnings - for (const std::string& warn : warnings) { - fprintf(stderr, "[WARNING] %s\n", warn.c_str()); - } - - int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT); - if (err) { - fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err)); - } - - return !cacheBuildFailure; -} diff --git a/dyld3/shared-cache/CacheBuilder.cpp b/dyld3/shared-cache/CacheBuilder.cpp index 0666b0c..b625e9f 100644 --- a/dyld3/shared-cache/CacheBuilder.cpp +++ b/dyld3/shared-cache/CacheBuilder.cpp @@ -22,380 +22,24 @@ * @APPLE_LICENSE_HEADER_END@ */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include - -#include -#include -#include -#include #include "MachOFileAbstraction.hpp" -#include "CodeSigningTypes.h" #include "DyldSharedCache.h" #include "CacheBuilder.h" -#include "FileAbstraction.hpp" -#include "Trie.hpp" -#include "FileUtils.h" #include "Diagnostics.h" -#include "ClosureBuilder.h" -#include "Closure.h" -#include "ClosureFileSystemNull.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 0x100000000ULL -#endif - -#ifndef ARM64_32_SHARED_REGION_START - #define ARM64_32_SHARED_REGION_START 0x1A000000ULL - #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL -#endif - -#if ARM_SHARED_REGION_SIZE > 0x26000000ULL - #define ARMV7K_CHAIN_BITS 0xC0000000 - #define ARMV7K_MAX 0x0 -#else - #define ARMV7K_CHAIN_BITS 0xE0000000 - #define ARMV7K_MAX 0x20000000 -#endif - -const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = { - { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K, 12, 2, true, true, true }, - { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x0, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K, 12, 2, true, true, true }, - { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x0, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K, 12, 0, false, false, true }, - { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64", CS_PAGE_SIZE_4K, 14, 2, false, true, false }, -#if SUPPORT_ARCH_arm64e - { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x0, 0x02000000, 0x00FFFF0000000000, "arm64e", CS_PAGE_SIZE_16K, 14, 2, false, true, false }, -#endif -#if SUPPORT_ARCH_arm64_32 - { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true }, -#endif - { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x0, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K, 14, 4, false, false, true }, - { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, ARMV7K_MAX, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", CS_PAGE_SIZE_4K, 14, 4, false, false, true }, - { 0x40000000, 0x40000000, 0x0, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K, 14, 0, false, false, true } -}; - - -// These are dylibs that may be interposed, so stubs calling into them should never be bypassed -const char* const CacheBuilder::_s_neverStubEliminateDylibs[] = { - "/usr/lib/system/libdispatch.dylib", - nullptr -}; - -// These are functions that are interposed by Instruments.app or ASan -const char* const CacheBuilder::_s_neverStubEliminateSymbols[] = { - "___bzero", - "___cxa_atexit", - "___cxa_throw", - "__longjmp", - "__objc_autoreleasePoolPop", - "_accept", - "_access", - "_asctime", - "_asctime_r", - "_asprintf", - "_atoi", - "_atol", - "_atoll", - "_calloc", - "_chmod", - "_chown", - "_close", - "_confstr", - "_ctime", - "_ctime_r", - "_dispatch_after", - "_dispatch_after_f", - "_dispatch_async", - "_dispatch_async_f", - "_dispatch_barrier_async_f", - "_dispatch_group_async", - "_dispatch_group_async_f", - "_dispatch_source_set_cancel_handler", - "_dispatch_source_set_event_handler", - "_dispatch_sync_f", - "_dlclose", - "_dlopen", - "_dup", - "_dup2", - "_endgrent", - "_endpwent", - "_ether_aton", - "_ether_hostton", - "_ether_line", - "_ether_ntoa", - "_ether_ntohost", - "_fchmod", - "_fchown", - "_fclose", - "_fdopen", - "_fflush", - "_fopen", - "_fork", - "_fprintf", - "_free", - "_freopen", - "_frexp", - "_frexpf", - "_frexpl", - "_fscanf", - "_fstat", - "_fstatfs", - "_fstatfs64", - "_fsync", - "_ftime", - "_getaddrinfo", - "_getattrlist", - "_getcwd", - "_getgrent", - "_getgrgid", - "_getgrgid_r", - "_getgrnam", - "_getgrnam_r", - "_getgroups", - "_gethostbyaddr", - "_gethostbyname", - "_gethostbyname2", - "_gethostent", - "_getifaddrs", - "_getitimer", - "_getnameinfo", - "_getpass", - "_getpeername", - "_getpwent", - "_getpwnam", - "_getpwnam_r", - "_getpwuid", - "_getpwuid_r", - "_getsockname", - "_getsockopt", - "_gmtime", - "_gmtime_r", - "_if_indextoname", - "_if_nametoindex", - "_index", - "_inet_aton", - "_inet_ntop", - "_inet_pton", - "_initgroups", - "_ioctl", - "_lchown", - "_lgamma", - "_lgammaf", - "_lgammal", - "_link", - "_listxattr", - "_localtime", - "_localtime_r", - "_longjmp", - "_lseek", - "_lstat", - "_malloc", - "_malloc_create_zone", - "_malloc_default_purgeable_zone", - "_malloc_default_zone", - "_malloc_good_size", - "_malloc_make_nonpurgeable", - "_malloc_make_purgeable", - "_malloc_set_zone_name", - "_mbsnrtowcs", - "_mbsrtowcs", - "_mbstowcs", - "_memchr", - "_memcmp", - "_memcpy", - "_memmove", - "_memset", - "_mktime", - "_mlock", - "_mlockall", - "_modf", - "_modff", - "_modfl", - "_munlock", - "_munlockall", - "_objc_autoreleasePoolPop", - "_objc_setProperty", - "_objc_setProperty_atomic", - "_objc_setProperty_atomic_copy", - "_objc_setProperty_nonatomic", - "_objc_setProperty_nonatomic_copy", - "_objc_storeStrong", - "_open", - "_opendir", - "_poll", - "_posix_memalign", - "_pread", - "_printf", - "_pthread_attr_getdetachstate", - "_pthread_attr_getguardsize", - "_pthread_attr_getinheritsched", - "_pthread_attr_getschedparam", - "_pthread_attr_getschedpolicy", - "_pthread_attr_getscope", - "_pthread_attr_getstack", - "_pthread_attr_getstacksize", - "_pthread_condattr_getpshared", - "_pthread_create", - "_pthread_getschedparam", - "_pthread_join", - "_pthread_mutex_lock", - "_pthread_mutex_unlock", - "_pthread_mutexattr_getprioceiling", - "_pthread_mutexattr_getprotocol", - "_pthread_mutexattr_getpshared", - "_pthread_mutexattr_gettype", - "_pthread_rwlockattr_getpshared", - "_pwrite", - "_rand_r", - "_read", - "_readdir", - "_readdir_r", - "_readv", - "_readv$UNIX2003", - "_realloc", - "_realpath", - "_recv", - "_recvfrom", - "_recvmsg", - "_remquo", - "_remquof", - "_remquol", - "_scanf", - "_send", - "_sendmsg", - "_sendto", - "_setattrlist", - "_setgrent", - "_setitimer", - "_setlocale", - "_setpwent", - "_shm_open", - "_shm_unlink", - "_sigaction", - "_sigemptyset", - "_sigfillset", - "_siglongjmp", - "_signal", - "_sigpending", - "_sigprocmask", - "_sigwait", - "_snprintf", - "_sprintf", - "_sscanf", - "_stat", - "_statfs", - "_statfs64", - "_strcasecmp", - "_strcat", - "_strchr", - "_strcmp", - "_strcpy", - "_strdup", - "_strerror", - "_strerror_r", - "_strlen", - "_strncasecmp", - "_strncat", - "_strncmp", - "_strncpy", - "_strptime", - "_strtoimax", - "_strtol", - "_strtoll", - "_strtoumax", - "_tempnam", - "_time", - "_times", - "_tmpnam", - "_tsearch", - "_unlink", - "_valloc", - "_vasprintf", - "_vfprintf", - "_vfscanf", - "_vprintf", - "_vscanf", - "_vsnprintf", - "_vsprintf", - "_vsscanf", - "_wait", - "_wait$UNIX2003", - "_wait3", - "_wait4", - "_waitid", - "_waitid$UNIX2003", - "_waitpid", - "_waitpid$UNIX2003", - "_wcslen", - "_wcsnrtombs", - "_wcsrtombs", - "_wcstombs", - "_wordexp", - "_write", - "_writev", - "_writev$UNIX2003", - // always use stubs for C++ symbols that can be overridden - "__ZdaPv", - "__ZdlPv", - "__Znam", - "__Znwm", - - nullptr -}; - +#include "IMPCaches.hpp" CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem) : _options(options) , _fileSystem(fileSystem) , _fullAllocatedBuffer(0) , _diagnostics(options.loggingPrefix, options.verbose) - , _archLayout(nullptr) - , _aliasCount(0) - , _slideInfoFileOffset(0) - , _slideInfoBufferSizeAllocated(0) , _allocatedBufferSize(0) - , _selectorStringsFromExecutables(0) { +} - std::string targetArch = options.archs->name(); - if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) ) - targetArch = "sim-x86"; - - for (const ArchLayout& layout : _s_archLayout) { - if ( layout.archName == targetArch ) { - _archLayout = &layout; - break; - } - } - - if (!_archLayout) { - _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str()); - } +CacheBuilder::~CacheBuilder() { } @@ -404,1019 +48,12 @@ std::string CacheBuilder::errorMessage() return _diagnostics.errorMessage(); } -const std::set CacheBuilder::warnings() -{ - return _diagnostics.warnings(); -} - -const std::set CacheBuilder::evictions() -{ - return _evictions; -} - -void CacheBuilder::deleteBuffer() -{ - // Cache buffer - vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize); - _fullAllocatedBuffer = 0; - _allocatedBufferSize = 0; - // Local symbols buffer - if ( _localSymbolsRegion.bufferSize != 0 ) { - vm_deallocate(mach_task_self(), (vm_address_t)_localSymbolsRegion.buffer, _localSymbolsRegion.bufferSize); - _localSymbolsRegion.buffer = 0; - _localSymbolsRegion.bufferSize = 0; - } - // Code singatures - vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize); - _codeSignatureRegion.buffer = 0; - _codeSignatureRegion.bufferSize = 0; -} - - -void CacheBuilder::makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder) -{ - for (const LoadedMachO& dylib : dylibs) { - _sortedDylibs.push_back({ &dylib, dylib.mappedFile.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()); - - // 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; - - // Sort mac before iOSMac - bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0; - bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0; - if (isIOSMacA != isIOSMacB) - return !isIOSMacA; - - // Finally sort by path - return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath; - }); -} - - -inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) -{ - return (uint32_t)(abstime/1000/1000); -} - -struct DylibAndSize -{ - const CacheBuilder::LoadedMachO* input; - const char* installName; - uint64_t size; -}; - -uint64_t CacheBuilder::cacheOverflowAmount() -{ - if ( _archLayout->sharedRegionsAreDiscontiguous ) { - // for macOS x86_64 cache, need to check each region for overflow - 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 if ( _archLayout->textAndDataMaxSize != 0 ) { - // for armv7k, limit is 512MB of TEX+DATA - uint64_t totalTextAndData = _readWriteRegion.unslidLoadAddress + _readWriteRegion.sizeInUse - _readExecuteRegion.unslidLoadAddress; - if ( totalTextAndData < _archLayout->textAndDataMaxSize ) - return 0; - else - return totalTextAndData - _archLayout->textAndDataMaxSize; - } - else { - 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 %37 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; -} - -size_t CacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector& overflowDylibs) -{ - // build a reverse map of all dylib dependencies - __block std::map> references; - std::map>* referencesPtr = &references; - for (const DylibInfo& dylib : _sortedDylibs) { - // Esnure we have an entry (even if it is empty) - if (references.count(dylib.input->mappedFile.mh->installName()) == 0) { - references[dylib.input->mappedFile.mh->installName()] = std::set(); - }; - dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { - references[loadPath].insert(dylib.input->mappedFile.mh->installName()); - }); - } - - // Find the sizes of all the dylibs - std::vector dylibsToSort; - std::vector sortedDylibs; - for (const DylibInfo& dylib : _sortedDylibs) { - const char* installName = dylib.input->mappedFile.mh->installName(); - __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; - }); - dylibsToSort.push_back({ dylib.input, installName, segsSize }); - } - - // Build an ordered list of what to remove. At each step we do following - // 1) Find all dylibs that nothing else depends on - // 2a) If any of those dylibs are not in the order select the largest one of them - // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file - // 3) Remove all entries to the removed file from the reverse dependency map - // 4) Go back to one and repeat until there are no more evictable dylibs - // This results in us always choosing the locally optimal selection, and then taking into account how that impacts - // the dependency graph for subsequent selections - - bool candidateFound = true; - while (candidateFound) { - candidateFound = false; - DylibAndSize candidate; - uint64_t candidateOrder = 0; - for(const auto& dylib : dylibsToSort) { - const auto &i = referencesPtr->find(dylib.installName); - assert(i != referencesPtr->end()); - if (!i->second.empty()) { - continue; - } - const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath); - uint64_t order = 0; - if (j != _options.dylibOrdering.end()) { - order = j->second; - } else { - // Not in the order file, set order sot it goes to the front of the list - order = UINT64_MAX; - } - if (order > candidateOrder || - (order == UINT64_MAX && candidate.size < dylib.size)) { - // The new file is either a lower priority in the order file - // or the same priority as the candidate but larger - candidate = dylib; - candidateOrder = order; - candidateFound = true; - } - } - if (candidateFound) { - sortedDylibs.push_back(candidate); - referencesPtr->erase(candidate.installName); - for (auto& dependent : references) { - (void)dependent.second.erase(candidate.installName); - } - auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) { - return (strcmp(candidate.installName, dylib.installName) == 0); - }); - if (j != dylibsToSort.end()) { - dylibsToSort.erase(j); - } - } - } - - // build set of dylibs that if removed will allow cache to build - for (DylibAndSize& dylib : sortedDylibs) { - 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; - } - - // 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, - const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform) - : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { } - - // Loads and maps any MachOs in the given list of files. - void loadMachOs(std::vector& inputFiles, - std::vector& dylibsToCache, - std::vector& otherDylibs, - std::vector& executables, - std::vector& couldNotLoadFiles) { - - std::map dylibInstallNameMap; - for (CacheBuilder::InputFile& inputFile : inputFiles) { - char realerPath[MAXPATHLEN]; - dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath); - 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. - if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) { - inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg); - }) ) { - fileSystem.unloadFile(loadedFileInfo); - continue; - } - 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? - if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) { - inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg); - }) ) { - fileSystem.unloadFile(loadedFileInfo); - continue; - } - 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); - } - } - } - -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; - case dyld3::Platform::driverKit: - 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; - case dyld3::Platform::driverKit: - return false; - } - } - - const dyld3::closure::FileSystem& fileSystem; - const dyld3::GradedArchs& reqArchs; - dyld3::Platform reqPlatform; -}; - -static void verifySelfContained(std::vector& dylibsToCache, - std::vector& otherDylibs, - std::vector& couldNotLoadFiles) -{ - // build map of dylibs - __block std::map knownDylibs; - __block std::map 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> 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 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); - } - } - }); - } - } - - // 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 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); - }); - } - - // find all dylibs not referenced - 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; - } - } - } - } - - // 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& inputFiles, - std::vector& aliases) { - // First filter down to files which are actually MachO's - CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform); - - std::vector dylibsToCache; - std::vector otherDylibs; - std::vector executables; - std::vector 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"; - } - } - } - 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"; - - } - } - } - if (!errorString.empty()) { - _diagnostics.error("%s", errorString.c_str()); - } - } - - 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& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) { - - std::vector dylibsToCache; - std::vector otherDylibs; - std::vector 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& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases) -{ - // 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; - } - - // assign addresses for each segment of each dylib in new cache - parseCoalescableSegments(); - processSelectorStrings(osExecutables); - assignSegmentAddresses(); - std::vector 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(); - _coalescedText.clear(); - parseCoalescableSegments(); - processSelectorStrings(osExecutables); - 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); - if ( !_options.cacheSupportsASLR ) - _aslrTracker.disable(); - adjustAllImagesForNewSegmentLocations(); - if ( _diagnostics.hasError() ) - return; - - // 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(); - if ( _diagnostics.hasError() ) - return; - - - // optimize away stubs - uint64_t t6 = mach_absolute_time(); - if ( _options.optimizeStubs ) - optimizeAwayStubs(); - - - // 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 - uint64_t t7 = mach_absolute_time(); - optimizeLinkedit(); - - // copy ImageArray to end of read-only region - addImageArray(); - if ( _diagnostics.hasError() ) - return; - uint64_t t8 = mach_absolute_time(); - - // don't add dyld3 closures to simulator cache - if ( !dyld3::MachOFile::isSimulatorPlatform(_options.platform) ) { - // compute and add dlopen closures for all other dylibs - addOtherImageArray(otherOsDylibsInput, overflowDylibs); - if ( _diagnostics.hasError() ) - return; - - // compute and add launch closures to end of read-only region - addClosures(osExecutables); - if ( _diagnostics.hasError() ) - return; - } - - // 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 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 - _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 { - // branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB - if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) ) - dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse; - else - dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse); - } - - 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>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); -#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k - else if ( _archLayout->pointerDeltaMask == 0xC0000000 ) - writeSlideInfoV4>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); -#endif - else - writeSlideInfoV2>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); - } - - uint64_t t10 = mach_absolute_time(); - - // last sanity check on size - if ( cacheOverflowAmount() != 0 ) { - _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse); - return; - } - - // codesignature is part of file, but is not mapped - codeSign(); - if ( _diagnostics.hasError() ) - return; - - uint64_t t11 = mach_absolute_time(); - - if ( _options.verbose ) { - 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() -{ - // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes - std::string magic = "dyld_v1"; - magic.append(15 - magic.length() - strlen(_options.archs->name()), ' '); - magic.append(_options.archs->name()); - assert(magic.length() == 15); - - // fill in header - 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)); - 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 = 0; - dyldCacheHeader->branchPoolsCount = 0; - dyldCacheHeader->imagesTextOffset = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount; - dyldCacheHeader->imagesTextCount = _sortedDylibs.size(); - dyldCacheHeader->patchInfoAddr = 0; - dyldCacheHeader->patchInfoSize = 0; - dyldCacheHeader->otherImageGroupAddrUnused = 0; - dyldCacheHeader->otherImageGroupSizeUnused = 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->builtFromChainedFixups= (strcmp(_options.archs->name(), "arm64e") == 0); // FIXME - 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 image table - 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.input->mappedFile.modTime; - images->inode = dylib.input->mappedFile.inode; - } - 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 -/* - for (auto &dylib : _dylibs) { - if (!dylib->installNameAliases.empty()) { - for (const std::string& alias : dylib->installNameAliases) { - images->set_address(_segmentMap[dylib][0].address); - if (_manifest.platform() == "osx") { - images->modTime = dylib->lastModTime; - images->inode = dylib->inode; - } - else { - images->modTime = 0; - images->inode = pathHash(alias.c_str()); - } - images->pathFileOffset = offset; - //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str()); - ::strcpy((char*)&_buffer[offset], alias.c_str()); - offset += alias.size() + 1; - ++images; - } - } - } -*/ - // calculate start of text image array and trailing string pool - dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_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 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 = 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*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset); - assert(stringOffset <= (firstImage->address - mappings[0].address)); -} - void CacheBuilder::copyRawSegments() { const bool log = false; - dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) { - const DylibInfo& dylib = _sortedDylibs[index]; + const bool logCFConstants = false; + + forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) { 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.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str()); @@ -1424,7 +61,7 @@ void CacheBuilder::copyRawSegments() } }); - // Copy the coalesced sections + // Copy the coalesced __TEXT sections const uint64_t numCoalescedSections = sizeof(CacheCoalescedText::SupportedSections) / sizeof(*CacheCoalescedText::SupportedSections); dispatch_apply(numCoalescedSections, DISPATCH_APPLY_AUTO, ^(size_t index) { const CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(CacheCoalescedText::SupportedSections[index]); @@ -1434,2034 +71,52 @@ void CacheBuilder::copyRawSegments() for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets) ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1); }); + + // Copy the coalesced CF sections + if ( _coalescedText.cfStrings.bufferSize != 0 ) { + uint8_t* dstBuffer = _coalescedText.cfStrings.bufferAddr; + uint64_t dstBufferVMAddr = _coalescedText.cfStrings.bufferVMAddr; + forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) { + const char* segmentName = "__OBJC_CONST"; + const char* sectionName = "__cfstring"; + const DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionData = dylib.textCoalescer.getSectionCoalescer(segmentName, sectionName); + if ( sectionData.empty() ) + return; + + uint64_t sectionContentSize = 0; + const void* sectionContent = dylib.input->mappedFile.mh->findSectionContent(segmentName, sectionName, sectionContentSize); + assert(sectionContent != nullptr); + assert(sectionContentSize != 0); + for (const auto& dylibOffsetAndCacheOffset : sectionData) { + uint64_t dylibOffset = dylibOffsetAndCacheOffset.first; + uint64_t cacheOffset = dylibOffsetAndCacheOffset.second; + if (logCFConstants) fprintf(stderr, "copy %s %s section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n", + _options.archs->name(), segmentName, sectionName, + (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize, dstBuffer + cacheOffset, dstBufferVMAddr + cacheOffset); + ::memcpy(dstBuffer + cacheOffset, (const uint8_t*)sectionContent + dylibOffset, (size_t)DyldSharedCache::ConstantClasses::cfStringAtomSize); + } + }); + } } -void CacheBuilder::adjustAllImagesForNewSegmentLocations() +void CacheBuilder::adjustAllImagesForNewSegmentLocations(uint64_t cacheBaseAddress, + ASLR_Tracker& aslrTracker, LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText) { - __block std::vector diags; - diags.resize(_sortedDylibs.size()); - - 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]); - }); - } 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]); - } - } - - for (const Diagnostics& diag : diags) { - if ( diag.hasError() ) { - _diagnostics.error("%s", diag.errorMessage().c_str()); - break; - } - } -} - -void CacheBuilder::processSelectorStrings(const std::vector& executables) { - const bool log = false; - - _selectorStringsFromExecutables = 0; - - // Don't do this optimisation on watchOS where the shared cache is too small - if (_options.platform == dyld3::Platform::watchOS) - return; - - // Get the method name coalesced section as that is where we need to put these strings - CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname"); - for (const LoadedMachO& executable : executables) { - const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent; - - uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize; - - ma->forEachObjCMethodName(^(const char *methodName) { - std::string_view str = methodName; - auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); - if (itAndInserted.second) { - // If we inserted the string then we need to include it in the total - cacheStringSection.bufferSize += str.size() + 1; - // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName); - ++_selectorStringsFromExecutables; - } - }); - - uint64_t sizeAfterProcessing = cacheStringSection.bufferSize; - if ( log && (sizeBeforeProcessing != sizeAfterProcessing) ) { - printf("Pulled in % 6lld bytes of selectors from %s\n", - sizeAfterProcessing - sizeBeforeProcessing, executable.loadedFileInfo.path); - } - } - - _diagnostics.verbose("Pulled in %lld selector strings from executables\n", _selectorStringsFromExecutables); -} - -void CacheBuilder::parseCoalescableSegments() { - const bool log = false; - - for (DylibInfo& dylib : _sortedDylibs) - _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer); - - if (log) { - for (const char* section : CacheCoalescedText::SupportedSections) { - CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section); - printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section, - sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace); - } - } -} - -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); - 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); - - // assign TEXT segment addresses - _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 - 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; - // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them. - __block size_t sizeOfSections = 0; - __block bool foundCoalescedSection = false; - dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stopSection) { - if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0) - return; - if ( dylib.textCoalescer.sectionWasCoalesced(sectInfo.sectName)) { - foundCoalescedSection = true; - } else { - sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr; - } - }); - if (!foundCoalescedSection) - sizeOfSections = segInfo.sizeOfSections; - - // Keep __TEXT segments 4K or more aligned - 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(sizeOfSections, 12); - loc.dstCacheFileSize = (uint32_t)align(sizeOfSections, 12); - loc.copySegmentSize = (uint32_t)sizeOfSections; - loc.srcSegmentIndex = segInfo.segIndex; - dylib.cacheLocation.push_back(loc); - addr += loc.dstCacheSegmentSize; - }); - } - // move read-only segments to end of TEXT - if ( _archLayout->textAndDataMaxSize != 0 ) { - 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(segInfo.segName, "__LINKEDIT") == 0 ) - return; - - // Keep segments segments 4K or more aligned - 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)(_readExecuteRegion.cacheFileOffset + offsetInRegion); - loc.dstCacheSegmentSize = (uint32_t)align(segInfo.sizeOfSections, 12); - loc.dstCacheFileSize = (uint32_t)segInfo.sizeOfSections; - loc.copySegmentSize = (uint32_t)segInfo.sizeOfSections; - loc.srcSegmentIndex = segInfo.segIndex; - dylib.cacheLocation.push_back(loc); - addr += loc.dstCacheSegmentSize; - }); - } - } - - // reserve space for objc optimization tables and deduped strings - uint64_t objcReadOnlyBufferVMAddr = addr; - _objcReadOnlyBuffer = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress); - - // First the strings as we'll fill in the objc tables later in the optimizer - for (const char* section: CacheCoalescedText::SupportedSections) { - CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(section); - cacheStringSection.bufferAddr = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress); - cacheStringSection.bufferVMAddr = addr; - addr += cacheStringSection.bufferSize; - } - - addr = align(addr, 14); - _objcReadOnlyBufferSizeUsed = addr - objcReadOnlyBufferVMAddr; - - uint32_t totalSelectorRefCount = (uint32_t)_selectorStringsFromExecutables; - uint32_t totalClassDefCount = 0; - uint32_t totalProtocolDefCount = 0; - for (DylibInfo& dylib : _sortedDylibs) { - dyld3::MachOAnalyzer::ObjCInfo info = dylib.input->mappedFile.mh->getObjCInfo(); - totalSelectorRefCount += info.selRefCount; - totalClassDefCount += info.classDefCount; - totalProtocolDefCount += info.protocolDefCount; - } - - // now that shared cache coalesces all selector strings, use that better count - uint32_t coalescedSelectorCount = (uint32_t)_coalescedText.objcMethNames.stringsToOffsets.size(); - if ( coalescedSelectorCount > totalSelectorRefCount ) - totalSelectorRefCount = coalescedSelectorCount; - addr += align(computeReadOnlyObjC(totalSelectorRefCount, totalClassDefCount, totalProtocolDefCount), 14); - _objcReadOnlyBufferSizeAllocated = addr - objcReadOnlyBufferVMAddr; - - - // align TEXT region end - uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2); - _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); - _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 (DylibInfo& dylib : _sortedDylibs) { - __block uint64_t textSegVmAddr = 0; - dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { - if ( _options.platform == dyld3::Platform::watchOS_simulator) - return; - if ( strcmp(segInfo.segName, "__TEXT") == 0 ) - textSegVmAddr = segInfo.vmAddr; - if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) ) - return; - if ( strcmp(segInfo.segName, "__DATA_CONST") != 0 ) - return; - ++dataConstSegmentCount; - // Pack __DATA_CONST segments - 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.dstCacheFileSize = (uint32_t)copySize; - 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 (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 ( _options.platform != dyld3::Platform::watchOS_simulator) { - if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 ) - return; - if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 ) - return; - } - bool forcePageAlignedData = false; - if (_options.platform == dyld3::Platform::macOS) { - forcePageAlignedData = dylib.input->mappedFile.mh->hasUnalignedPointerFixups(); - //if ( forcePageAlignedData ) - // warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str()); - } - if ( (dataConstSegmentCount > 10) && !forcePageAlignedData ) { - // Pack __DATA segments only if we also have __DATA_CONST segments - addr = align(addr, segInfo.p2align); - } - else { - // Keep __DATA segments 4K or more aligned - 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 - _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.dstCacheFileSize = (uint32_t)copySize; - loc.copySegmentSize = (uint32_t)copySize; - loc.srcSegmentIndex = segInfo.segIndex; - dylib.cacheLocation.push_back(loc); - addr += loc.dstCacheSegmentSize; - }); - } - - // 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); - 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 ( _options.platform == dyld3::Platform::watchOS_simulator) - return; - if ( strcmp(segInfo.segName, "__TEXT") == 0 ) - textSegVmAddr = segInfo.vmAddr; - if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) ) - return; - if ( strcmp(segInfo.segName, "__DATA_DIRTY") != 0 ) - return; - // Pack __DATA_DIRTY segments - 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.dstCacheFileSize = (uint32_t)copySize; - loc.copySegmentSize = (uint32_t)copySize; - loc.srcSegmentIndex = segInfo.segIndex; - dylib.cacheLocation.push_back(loc); - addr += loc.dstCacheSegmentSize; - }); - } - - // reserve space for objc r/w optimization tables - _objcReadWriteBufferSizeAllocated = align(computeReadWriteObjC((uint32_t)_sortedDylibs.size(), totalProtocolDefCount), 14); - addr = align(addr, 4); // objc r/w section contains pointer and must be at least pointer align - _objcReadWriteBuffer = _readWriteRegion.buffer + (addr - _readWriteRegion.unslidLoadAddress); - addr += _objcReadWriteBufferSizeAllocated; - - // align DATA region end - uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2); - _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); - _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 ) { - 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 - if ( _archLayout->textAndDataMaxSize == 0 ) { - 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(segInfo.segName, "__LINKEDIT") == 0 ) - return; - - // Keep segments segments 4K or more aligned - 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.dstCacheFileSize = (uint32_t)segInfo.sizeOfSections; - 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), 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(segInfo.segName, "__LINKEDIT") != 0 ) - return; - // Keep segments segments 4K or more aligned - 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.dstCacheFileSize = (uint32_t)copySize; - loc.copySegmentSize = (uint32_t)copySize; - loc.srcSegmentIndex = segInfo.segIndex; - dylib.cacheLocation.push_back(loc); - addr += loc.dstCacheSegmentSize; - }); - } - - // 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 -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 (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n", - (long)lastValue, 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 -void CacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& 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

(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 -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; - - // 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 = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0; - - // set page starts and extras for each page - std::vector pageStarts; - std::vector 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

(pageContent, bitmapForPage, info, pageStarts, pageExtras); - if ( _diagnostics.hasError() ) { + // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe + __block bool badDylib = false; + forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) { + if ( dylibDiag.hasError() ) return; - } - pageContent += pageSize; - bitmapForPage += (sizeof(bool)*(pageSize/4)); - } - - // 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()); -} - -#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k -// fits in to int16_t -static bool smallValue(uint64_t value) -{ - uint32_t high = (value & 0xFFFF8000); - return (high == 0) || (high == 0xFFFF8000); -} - -template -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; - - 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 ( 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); - //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; - } - 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 ( 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); - 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 ( 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); - P::setP(*prevLoc, newValue); - - return true; -} - - -template -void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& 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_SLIDE4_PAGE_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_SLIDE4_PAGE_NO_REBASE ) { - // found first rebase location in page - startValue = i; - } - else if ( !makeRebaseChainV4

(pageContent, lastLocationOffset, offset, info) ) { - // can't record all rebasings in one chain - 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 >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) { - _diagnostics.error("rebase overflow in v4 page extras"); - return; - } - pageExtras.push_back(startValue); - startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_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_SLIDE4_PAGE_USE_EXTRA ) { - // add end bit to extras - pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END; - } - pageStarts.push_back(startValue); -} - - - -template -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; - - // fill in fixed info - assert(_slideInfoFileOffset != 0); - 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 = info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0; - - // set page starts and extras for each page - std::vector pageStarts; - std::vector pageExtras; - pageStarts.reserve(dataPageCount); - uint8_t* pageContent = _readWriteRegion.buffer; - const bool* bitmapForPage = bitmap; - for (unsigned i=0; i < dataPageCount; ++i) { - addPageStartsV4

(pageContent, bitmapForPage, info, pageStarts, pageExtras); - if ( _diagnostics.hasError() ) { - return; - } - pageContent += pageSize; - bitmapForPage += (sizeof(bool)*(pageSize/4)); - } - // fill in computed info - 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_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 < 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 v4 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()); -} -#endif - -/* -void CacheBuilder::writeSlideInfoV1() -{ - // build one 128-byte bitmap per page (4096) of DATA - uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset; - uint8_t* const dataEnd = dataStart + regions[1].size; - const long bitmapSize = (dataEnd - dataStart)/(4*8); - uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1); - for (void* p : _pointersForASLR) { - if ( (p < dataStart) || ( p > dataEnd) ) - terminate("DATA pointer for sliding, out of range\n"); - long offset = (long)((uint8_t*)p - dataStart); - if ( (offset % 4) != 0 ) - terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset); - long byteIndex = offset / (4*8); - long bitInByte = (offset % 32) >> 2; - bitmap[byteIndex] |= (1 << bitInByte); - } - - // allocate worst case size block of all slide info - const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes. - const unsigned toc_count = (unsigned)bitmapSize/entry_size; - dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset); - slideInfo->version = 1; - slideInfo->toc_offset = sizeof(dyld_cache_slide_info); - slideInfo->toc_count = toc_count; - slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128); - slideInfo->entries_count = 0; - slideInfo->entries_size = entry_size; - // append each unique entry - const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap; - dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset()); - int entry_count = 0; - for (int i=0; i < toc_count; ++i) { - const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i]; - // see if it is same as one already added - bool found = false; - for (int j=0; j < entry_count; ++j) { - if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) { - slideInfo->set_toc(i, j); - found = true; - break; - } - } - if ( !found ) { - // append to end - memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size); - slideInfo->set_toc(i, entry_count++); - } - } - slideInfo->entries_count = entry_count; - ::free((void*)bitmap); - - _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2); -} - -*/ - - - -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; - } - 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->arm64e.rebase.next = loc - lastLoc; - } - lastLoc = loc; - } - } - if ( lastLoc != nullptr ) { - // mark last one as end of chain - lastLoc->arm64e.rebase.next = 0; - } - return result; -} - - -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; - } - - // 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() -{ - uint8_t dscHashType; - uint8_t dscHashSize; - uint32_t dscDigestFormat; - bool agile = false; - - // select which codesigning hash - switch (_options.codeSigningDigestMode) { - case DyldSharedCache::Agile: - agile = true; - // Fall through to SHA1, because the main code directory remains SHA1 for compatibility. - [[clang::fallthrough]]; - case DyldSharedCache::SHA1only: -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - dscHashType = CS_HASHTYPE_SHA1; - dscHashSize = CS_HASH_SIZE_SHA1; - dscDigestFormat = kCCDigestSHA1; -#pragma clang diagnostic pop - break; - case DyldSharedCache::SHA256only: - dscHashType = CS_HASHTYPE_SHA256; - dscHashSize = CS_HASH_SIZE_SHA256; - dscDigestFormat = kCCDigestSHA256; - break; - default: - _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.", - _options.codeSigningDigestMode); - return; - } - - std::string cacheIdentifier = "com.apple.dyld.cache."; - cacheIdentifier += _options.archs->name(); - if ( _options.dylibsRemovedDuringMastering ) { - if ( _options.optimizeStubs ) - cacheIdentifier += ".release"; - else - cacheIdentifier += ".development"; - } - // get pointers into shared cache buffer - size_t inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse; - - const uint16_t pageSize = _archLayout->csPageSize; - - // layout code signature contents - uint32_t blobCount = agile ? 4 : 3; - size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0 - uint32_t slotCount = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize); - uint32_t xSlotCount = CSSLOT_REQUIREMENTS; - size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg); - size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount; - size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount; - size_t cdSize = hashOffset + (slotCount * dscHashSize); - size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0; - size_t reqsSize = 12; - size_t cmsSize = sizeof(CS_Blob); - size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex); - size_t cd256Offset = cdOffset + cdSize; - size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile - size_t cmsOffset = reqsOffset + reqsSize; - size_t sbSize = cmsOffset + cmsSize; - size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned - - // 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(_codeSignatureRegion.buffer); - sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE); - sb->length = htonl(sbSize); - sb->count = htonl(blobCount); - sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY); - sb->index[0].offset = htonl(cdOffset); - sb->index[1].type = htonl(CSSLOT_REQUIREMENTS); - sb->index[1].offset = htonl(reqsOffset); - sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE); - sb->index[2].offset = htonl(cmsOffset); - if ( agile ) { - sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0); - sb->index[3].offset = htonl(cd256Offset); - } - - // fill in empty requirements - CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset); - reqs->magic = htonl(CSMAGIC_REQUIREMENTS); - reqs->length = htonl(sizeof(CS_RequirementsBlob)); - reqs->data = 0; - - // initialize fixed fields of Code Directory - CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset); - cd->magic = htonl(CSMAGIC_CODEDIRECTORY); - cd->length = htonl(cdSize); - cd->version = htonl(0x20400); // supports exec segment - cd->flags = htonl(kSecCodeSignatureAdhoc); - cd->hashOffset = htonl(hashOffset); - cd->identOffset = htonl(idOffset); - cd->nSpecialSlots = htonl(xSlotCount); - cd->nCodeSlots = htonl(slotCount); - cd->codeLimit = htonl(inBbufferSize); - cd->hashSize = dscHashSize; - cd->hashType = dscHashType; - cd->platform = 0; // not platform binary - cd->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE); - cd->spare2 = 0; // unused (must be zero) - cd->scatterOffset = 0; // not supported anymore - cd->teamOffset = 0; // no team ID - cd->spare3 = 0; // unused (must be zero) - cd->codeLimit64 = 0; // falls back to codeLimit - - // executable segment info - 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()); - - // add special slot hashes - uint8_t* hashSlot = (uint8_t*)cd + hashOffset; - uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize]; - CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot); - - CS_CodeDirectory* cd256; - uint8_t* hash256Slot; - uint8_t* reqsHash256Slot; - if ( agile ) { - // Note that the assumption here is that the size up to the hashes is the same as for - // sha1 code directory, and that they come last, after everything else. - - cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset); - cd256->magic = htonl(CSMAGIC_CODEDIRECTORY); - cd256->length = htonl(cd256Size); - cd256->version = htonl(0x20400); // supports exec segment - cd256->flags = htonl(kSecCodeSignatureAdhoc); - cd256->hashOffset = htonl(hash256Offset); - cd256->identOffset = htonl(idOffset); - cd256->nSpecialSlots = htonl(xSlotCount); - cd256->nCodeSlots = htonl(slotCount); - cd256->codeLimit = htonl(inBbufferSize); - cd256->hashSize = CS_HASH_SIZE_SHA256; - cd256->hashType = CS_HASHTYPE_SHA256; - cd256->platform = 0; // not platform binary - cd256->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE); - cd256->spare2 = 0; // unused (must be zero) - cd256->scatterOffset = 0; // not supported anymore - cd256->teamOffset = 0; // no team ID - cd256->spare3 = 0; // unused (must be zero) - cd256->codeLimit64 = 0; // falls back to codeLimit - - // executable segment info - cd256->execSegBase = cd->execSegBase; - cd256->execSegLimit = cd->execSegLimit; - cd256->execSegFlags = cd->execSegFlags; - - // initialize dynamic fields of Code Directory - strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str()); - - // add special slot hashes - hash256Slot = (uint8_t*)cd256 + hash256Offset; - reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256]; - CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot); - } - else { - cd256 = NULL; - hash256Slot = NULL; - reqsHash256Slot = NULL; - } - - // fill in empty CMS blob for ad-hoc signing - CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset); - cms->magic = htonl(CSMAGIC_BLOBWRAPPER); - cms->length = htonl(sizeof(CS_Blob)); - - - // alter header of cache to record size and location of code signature - // do this *before* hashing each page - dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer; - cache->codeSignatureOffset = inBbufferSize; - cache->codeSignatureSize = sigSize; - - const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / pageSize); - const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / pageSize); - const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / pageSize); - auto codeSignPage = ^(size_t i) { - const uint8_t* code = nullptr; - // move to correct region - if ( i < rwSlotStart ) - code = _readExecuteRegion.buffer + (i * pageSize); - else if ( i >= rwSlotStart && i < roSlotStart ) - code = _readWriteRegion.buffer + ((i - rwSlotStart) * pageSize); - else if ( i >= roSlotStart && i < localsSlotStart ) - code = _readOnlyRegion.buffer + ((i - roSlotStart) * pageSize); - else - code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * pageSize); - - CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize)); - - if ( agile ) { - CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256)); - } - }; - - // compute hashes - dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) { - codeSignPage(i); + adjustDylibSegments(dylib, dylibDiag, cacheBaseAddress, aslrTracker, + lohTracker, coalescedText); + if ( dylibDiag.hasError() ) + badDylib = true; }); - // Now that we have a code signature, compute a cache UUID by hashing the code signature blob - { - uint8_t* uuidLoc = cache->uuid; - assert(uuid_is_null(uuidLoc)); - static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache"); - uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH]; - CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest); - memcpy(uuidLoc, fullDigest, 16); - // 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, because we modified it by setting uuid in header - codeSignPage(0); + if ( badDylib && !_diagnostics.hasError() ) { + _diagnostics.error("One or more binaries has an error which prevented linking. See other errors."); } - - // hash of entire code directory (cdHash) uses same hash as each page - uint8_t fullCdHash[dscHashSize]; - CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash); - // Note: cdHash is defined as first 20 bytes of hash - memcpy(_cdHashFirst, fullCdHash, 20); - if ( agile ) { - uint8_t fullCdHash256[CS_HASH_SIZE_SHA256]; - CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256); - // Note: cdHash is defined as first 20 bytes of hash, even for sha256 - memcpy(_cdHashSecond, fullCdHash256, 20); - } - else { - memset(_cdHashSecond, 0, 20); - } -} - -const bool CacheBuilder::agileSignature() -{ - return _options.codeSigningDigestMode == DyldSharedCache::Agile; -} - -static const std::string cdHash(uint8_t hash[20]) -{ - char buff[48]; - for (int i = 0; i < 20; ++i) - sprintf(&buff[2*i], "%2.2x", hash[i]); - return buff; -} - -const std::string CacheBuilder::cdHashFirst() -{ - return cdHash(_cdHashFirst); -} - -const std::string CacheBuilder::cdHashSecond() -{ - return cdHash(_cdHashSecond); -} - -const std::string CacheBuilder::uuid() const -{ - dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer; - uuid_string_t uuidStr; - uuid_unparse(cache->uuid, uuidStr); - return uuidStr; -} - -static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad) { - int64_t signedAddend = (int64_t)ad; - assert(((signedAddend << 52) >> 52) == signedAddend); - dyld_cache_patchable_location patch; - patch.cacheOffset = cacheOff; - patch.addend = ad; - patch.authenticated = 0; - patch.usesAddressDiversity = 0; - patch.key = 0; - patch.discriminator = 0; - return patch; -} - -static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad, - dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo) { - int64_t signedAddend = (int64_t)ad; - assert(((signedAddend << 52) >> 52) == signedAddend); - dyld_cache_patchable_location patch; - patch.cacheOffset = cacheOff; - patch.addend = ad; - patch.authenticated = authInfo.arm64e.authBind.auth; - patch.usesAddressDiversity = authInfo.arm64e.authBind.addrDiv; - patch.key = authInfo.arm64e.authBind.key; - patch.discriminator = authInfo.arm64e.authBind.diversity; - return patch; -} - - -void CacheBuilder::buildImageArray(std::vector& aliases) -{ - typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo; - - // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray() - __block std::vector dylibInfos; - __block std::unordered_map 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 dylibAliases; - dylibAliases.reserve(aliases.size()); - for (const auto& alias : aliases) - dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() }); - - dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers; - - handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress, - const dyld_chained_starts_in_image* starts, - const dyld3::Array& targets, - const dyld3::Array& targetInfos) { - imageLoadAddress->forEachFixupInAllChains(_diagnostics, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { - uint64_t offsetInCache; - dyld3::closure::Image::ResolvedSymbolTarget target; - const dyld3::closure::ClosureBuilder::ResolvedTargetInfo* targetInfo; - switch (segInfo->pointer_format) { - case DYLD_CHAINED_PTR_ARM64E: - if ( fixupLoc->arm64e.bind.bind ) { - target = targets[fixupLoc->arm64e.bind.ordinal]; - targetInfo = &targetInfos[fixupLoc->arm64e.bind.ordinal]; - switch ( target.sharedCache.kind ) { - case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache: - offsetInCache = target.sharedCache.offset - targetInfo->addend; - _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache); - _exportsToName[offsetInCache] = targetInfo->foundSymbolName; - if ( fixupLoc->arm64e.authBind.auth ) { - // turn this auth bind into an auth rebase into the cache - fixupLoc->arm64e.authRebase.bind = 0; - fixupLoc->arm64e.authRebase.target = target.sharedCache.offset; - _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend, *fixupLoc)); - } - else { - // turn this plain bind into an plain rebase into the cache - fixupLoc->arm64e.rebase.bind = 0; - fixupLoc->arm64e.rebase.target = _archLayout->sharedMemoryStart + target.sharedCache.offset; - _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend)); - } - _aslrTracker.add(fixupLoc); - break; - case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute: - fixupLoc->raw64 = 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 <= (int)(imageLoadAddress->dependentDylibCount())) ) { - _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1); - } - break; - default: - assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache"); - } - } else { - _aslrTracker.add(fixupLoc); - } - break; - case DYLD_CHAINED_PTR_64: - if ( fixupLoc->generic64.bind.bind ) { - target = targets[fixupLoc->generic64.bind.ordinal]; - targetInfo = &targetInfos[fixupLoc->generic64.bind.ordinal]; - switch ( target.sharedCache.kind ) { - case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache: - offsetInCache = target.sharedCache.offset - targetInfo->addend; - _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache); - _exportsToName[offsetInCache] = targetInfo->foundSymbolName; - // turn this bind into a rebase into the cache - fixupLoc->generic64.rebase.bind = 0; - fixupLoc->generic64.rebase.next = 0; // rechained later - fixupLoc->generic64.rebase.reserved = 0; - fixupLoc->generic64.rebase.high8 = 0; - fixupLoc->generic64.rebase.target = target.sharedCache.offset; - _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend)); - _aslrTracker.add(fixupLoc); - break; - case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute: - fixupLoc->raw64 = target.absolute.value; - // don't record absolute targets for ASLR - if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) { - _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1); - } - break; - default: - assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache"); - } - } - break; - case DYLD_CHAINED_PTR_32: - if ( fixupLoc->generic32.bind.bind ) { - target = targets[fixupLoc->generic32.bind.ordinal]; - targetInfo = &targetInfos[fixupLoc->generic32.bind.ordinal]; - switch ( target.sharedCache.kind ) { - case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache: - offsetInCache = target.sharedCache.offset - targetInfo->addend; - _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache); - _exportsToName[offsetInCache] = targetInfo->foundSymbolName; - // turn this bind into a rebase into the cache - fixupLoc->cache32.next = 0; // rechained later - fixupLoc->cache32.target = (uint32_t)(target.sharedCache.offset); - _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend)); - _aslrTracker.add(fixupLoc); - break; - case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute: - fixupLoc->raw32 = (uint32_t)target.absolute.value; - // don't record absolute targets for ASLR - if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) { - _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1); - } - break; - default: - assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache"); - } - } - break; - default: - assert(0 && "unsupported chained bind type"); - } - - }); - }; - - 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); - if (targetInfo.isWeakDef) - _dylibWeakExports.insert({ targetInfo.foundInDylib, offsetInCache }); - _exportsToUses[offsetInCache].push_back(makePatchLocation(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 <= (int)(mh->dependentDylibCount())) ) { - _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1); - } - break; - default: - assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache"); - } - }; - - // build ImageArray for all dylibs in dyld cache - dyld3::closure::PathOverrides pathOverrides; - dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, *_options.archs, pathOverrides, - dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, &handlers); - dyld3::Array dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size()); - const dyld3::Array 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; - } -} - -static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) { - return a.cacheOffset == b.cacheOffset; -} - -void CacheBuilder::addImageArray() -{ - // build trie of dylib paths - __block std::vector 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 trieBytes; - dylibsTrie.emit(trieBytes); - while ( (trieBytes.size() % 4) != 0 ) - trieBytes.push_back(0); - - // build set of functions to never stub-eliminate because tools may need to override them - std::unordered_set alwaysGeneratePatch; - for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p) - alwaysGeneratePatch.insert(*p); - - // Add the patches for the image array. - __block uint64_t numPatchImages = _imageArray->size(); - __block uint64_t numPatchExports = 0; - __block uint64_t numPatchLocations = 0; - __block uint64_t numPatchExportNameBytes = 0; - - auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh, - CacheOffset offset) -> bool { - if (dylibNeedsPatching) - return true; - if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end()) - return true; - const std::string& exportName = _exportsToName[offset]; - return alwaysGeneratePatch.find(exportName) != alwaysGeneratePatch.end(); - }; - - std::set alwaysPatchDylibs; - for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d) - alwaysPatchDylibs.insert(*d); - - // First calculate how much space we need - const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - cache->forEachImage(^(const mach_header* mh, const char* installName) { - const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh; - const std::set& dylibExports = _dylibToItsExports[ml]; - - // On a customer cache, only store patch locations for interposable dylibs and weak binding - bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); - - uint64_t numDylibExports = 0; - for (CacheOffset exportCacheOffset : dylibExports) { - if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) - continue; - std::vector& uses = _exportsToUses[exportCacheOffset]; - uses.erase(std::unique(uses.begin(), uses.end()), uses.end()); - numPatchLocations += uses.size(); - - std::string exportName = _exportsToName[exportCacheOffset]; - numPatchExportNameBytes += exportName.size() + 1; - } - numPatchExports += numDylibExports; - }); - - // Now reserve the space - __block std::vector patchImages; - __block std::vector patchExports; - __block std::vector patchLocations; - __block std::vector patchExportNames; - - patchImages.reserve(numPatchImages); - patchExports.reserve(numPatchExports); - patchLocations.reserve(numPatchLocations); - patchExportNames.reserve(numPatchExportNameBytes); - - // And now fill it with the patch data - cache->forEachImage(^(const mach_header* mh, const char* installName) { - const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh; - const std::set& dylibExports = _dylibToItsExports[ml]; - - // On a customer cache, only store patch locations for interposable dylibs and weak binding - bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName); - - // Add the patch image which points in to the exports - dyld_cache_image_patches patchImage; - patchImage.patchExportsStartIndex = (uint32_t)patchExports.size(); - patchImage.patchExportsCount = 0; - - // Then add each export which points to a list of locations and a name - for (CacheOffset exportCacheOffset : dylibExports) { - if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) - continue; - ++patchImage.patchExportsCount; - std::vector& uses = _exportsToUses[exportCacheOffset]; - - dyld_cache_patchable_export cacheExport; - cacheExport.cacheOffsetOfImpl = (uint32_t)exportCacheOffset; - cacheExport.patchLocationsStartIndex = (uint32_t)patchLocations.size(); - cacheExport.patchLocationsCount = (uint32_t)uses.size(); - cacheExport.exportNameOffset = (uint32_t)patchExportNames.size(); - patchExports.push_back(cacheExport); - - // Now add the list of locations. - patchLocations.insert(patchLocations.end(), uses.begin(), uses.end()); - - // And add the export name - const std::string& exportName = _exportsToName[exportCacheOffset]; - patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1); - } - patchImages.push_back(patchImage); - }); - - while ( (patchExportNames.size() % 4) != 0 ) - patchExportNames.push_back('\0'); - - uint64_t patchInfoSize = sizeof(dyld_cache_patch_info); - patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size(); - patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size(); - patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size(); - patchInfoSize += patchExportNames.size(); - - // check for fit - uint64_t imageArraySize = _imageArray->size(); - size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse; - if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) { - _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)", - _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024); - return; - } - - // 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()); - - // Also write out the patch info - dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize; - dyldCache->header.patchInfoSize = patchInfoSize; - dyld_cache_patch_info patchInfo; - patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info); - patchInfo.patchTableArrayCount = patchImages.size(); - patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches)); - patchInfo.patchExportArrayCount = patchExports.size(); - patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export)); - patchInfo.patchLocationArrayCount = patchLocations.size(); - patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location)); - patchInfo.patchExportNamesSize = patchExportNames.size(); - ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress, - &patchInfo, sizeof(dyld_cache_patch_info)); - ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress, - &patchImages[0], sizeof(patchImages[0]) * patchImages.size()); - ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress, - &patchExports[0], sizeof(patchExports[0]) * patchExports.size()); - ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress, - &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size()); - ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress, - &patchExportNames[0], patchExportNames.size()); - - _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14); - - // Free the underlying image array buffer - _imageArray->deallocate(); -} - -void CacheBuilder::addOtherImageArray(const std::vector& otherDylibsAndBundles, std::vector& overflowDylibs) -{ - DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - dyld3::closure::PathOverrides pathOverrides; - dyld3::closure::FileSystemNull nullFileSystem; - dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, cache, false, *_options.archs, pathOverrides, - dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _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, "staged_system_apps/") ) - 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); - } - - // 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) { - // Sort mac before iOSMac - bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0; - bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0; - if (isIOSMacA != isIOSMacB) - return !isIOSMacA; - return strcmp(a.path, b.path) < 0; - }); - - const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size()); - - // build trie of paths - __block std::vector 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 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; - } - - // 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); - - // Free the underlying buffer - otherImageArray->deallocate(); -} - - -void CacheBuilder::addClosures(const std::vector& osExecutables) -{ - const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; - - __block std::vector osExecutablesDiags; - __block std::vector 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, *_options.archs, pathOverrides, - dyld3::closure::ClosureBuilder::AtPath::all, false, nullptr, _options.platform, nullptr); - bool issetuid = false; - if ( this->_options.platform == dyld3::Platform::macOS || dyld3::MachOFile::isSimulatorPlatform(this->_options.platform) ) - _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr); - 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 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) { - closuresSpace += entry.second->size(); - } - 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; - } - DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse; - uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse; - std::vector closureEntrys; - uint32_t currentClosureOffset = 0; - for (const auto& entry : closures) { - const dyld3::closure::LaunchClosure* closure = entry.second; - closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset))); - size_t size = closure->size(); - assert((size % 4) == 0); - memcpy(closuresBase+currentClosureOffset, closure, size); - currentClosureOffset += size; - freeSpace -= size; - closure->deallocate(); - } - cache->header.progClosuresSize = currentClosureOffset; - _readOnlyRegion.sizeInUse += currentClosureOffset; - freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse; - // build trie of indexes into closures list - DylibIndexTrie closureTrie(closureEntrys); - std::vector trieBytes; - closureTrie.emit(trieBytes); - while ( (trieBytes.size() % 8) != 0 ) - trieBytes.push_back(0); - if ( trieBytes.size() > freeSpace ) { - _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)", - _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024); - return; - } - memcpy(_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; - BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen); - strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen); - int fd = mkstemp(pathTemplateSpace); - if ( fd != -1 ) { - auto cacheSizeCallback = ^(uint64_t size) { - // if making macOS dyld cache for current OS into standard location - if ( (_options.platform == dyld3::Platform::macOS) && startsWith(path, MACOSX_DYLD_SHARED_CACHE_DIR) ) { - // pin cache file to SSD on fusion drives - apfs_data_pin_location_t where = APFS_PIN_DATA_TO_MAIN; - ::fsctl(pathTemplateSpace, APFSIOC_PIN_DATA, &where, 0); - } - // set final cache file size (may help defragment file) - ::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; - }; - // TOCTOU: verify path is still a realpath (not changed) - char tempPath[MAXPATHLEN]; - if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) { - size_t tempPathLen = strlen(tempPath); - if ( tempPathLen > 7 ) - tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx - if ( path != tempPath ) { - _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath); - ::close(fd); - return; - } - } - else { - _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file"); - ::close(fd); - return; - } - bool fullyWritten = writeCache(cacheSizeCallback, copyCallback); - if ( fullyWritten ) { - ::fchmod(fd, S_IRUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "r--r--r--" - // TOCTOU: verify path is still a realpath (not changed) - char resolvedPath[PATH_MAX]; - ::realpath(path.c_str(), resolvedPath); - // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer - if ( path != resolvedPath ) { - _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), resolvedPath); - return; - } - 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); -} - -std::string CacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const -{ - const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; - return cache->generateJSONMap(cacheDisposition.c_str()); -} - - -void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) { - for (const DylibInfo& dylibInfo : _sortedDylibs) - callback(dylibInfo.runtimePath); } @@ -3469,24 +124,39 @@ CacheBuilder::ASLR_Tracker::~ASLR_Tracker() { if ( _bitmap != nullptr ) ::free(_bitmap); +#if BUILDING_APP_CACHE_UTIL + if ( _cacheLevels != nullptr ) + ::free(_cacheLevels); +#endif } 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); + _regionEnd = (uint8_t*)rwRegionStart + rwRegionSize; + _bitmap = (bool*)calloc(_pageCount*(_pageSize/kMinimumFixupAlignment)*sizeof(bool), 1); +#if BUILDING_APP_CACHE_UTIL + size_t cacheLevelsSize = (_pageCount*(_pageSize/kMinimumFixupAlignment)*sizeof(uint8_t)); + _cacheLevels = (uint8_t*)malloc(cacheLevelsSize); + memset(_cacheLevels, (int)~0U, cacheLevelsSize); +#endif } -void CacheBuilder::ASLR_Tracker::add(void* loc) +void CacheBuilder::ASLR_Tracker::add(void* loc, uint8_t level) { if (!_enabled) return; uint8_t* p = (uint8_t*)loc; assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = true; + assert(p < _regionEnd); + _bitmap[(p-_regionStart)/kMinimumFixupAlignment] = true; + +#if BUILDING_APP_CACHE_UTIL + if ( level != (uint8_t)~0U ) { + _cacheLevels[(p-_regionStart)/kMinimumFixupAlignment] = level; + } +#endif } void CacheBuilder::ASLR_Tracker::remove(void* loc) @@ -3495,61 +165,191 @@ void CacheBuilder::ASLR_Tracker::remove(void* loc) return; uint8_t* p = (uint8_t*)loc; assert(p >= _regionStart); - assert(p < _endStart); - _bitmap[(p-_regionStart)/4] = false; + assert(p < _regionEnd); + _bitmap[(p-_regionStart)/kMinimumFixupAlignment] = false; } -bool CacheBuilder::ASLR_Tracker::has(void* loc) +bool CacheBuilder::ASLR_Tracker::has(void* loc, uint8_t* level) const { if (!_enabled) return true; uint8_t* p = (uint8_t*)loc; assert(p >= _regionStart); - assert(p < _endStart); - return _bitmap[(p-_regionStart)/4]; + assert(p < _regionEnd); + + if ( _bitmap[(p-_regionStart)/kMinimumFixupAlignment] ) { +#if BUILDING_APP_CACHE_UTIL + if ( level != nullptr ) { + uint8_t levelValue = _cacheLevels[(p-_regionStart)/kMinimumFixupAlignment]; + if ( levelValue != (uint8_t)~0U ) + *level = levelValue; + } +#endif + return true; + } + return false; } +void CacheBuilder::ASLR_Tracker::setHigh8(void* p, uint8_t high8) +{ + _high8Map[p] = high8; +} + +void CacheBuilder::ASLR_Tracker::setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key) +{ + _authDataMap[p] = {diversity, hasAddrDiv, key}; +} + +void CacheBuilder::ASLR_Tracker::setRebaseTarget32(void*p, uint32_t targetVMAddr) +{ + _rebaseTarget32[p] = targetVMAddr; +} + +void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p, uint64_t targetVMAddr) +{ + _rebaseTarget64[p] = targetVMAddr; +} + +bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte) const +{ + auto pos = _high8Map.find(p); + if ( pos == _high8Map.end() ) + return false; + *highByte = pos->second; + return true; +} + +bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key) const +{ + auto pos = _authDataMap.find(p); + if ( pos == _authDataMap.end() ) + return false; + *diversity = pos->second.diversity; + *hasAddrDiv = pos->second.addrDiv; + *key = pos->second.key; + return true; +} + +bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr) const +{ + auto pos = _rebaseTarget32.find(p); + if ( pos == _rebaseTarget32.end() ) + return false; + *vmAddr = pos->second; + return true; +} + +bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr) const +{ + auto pos = _rebaseTarget64.find(p); + if ( pos == _rebaseTarget64.end() ) + return false; + *vmAddr = pos->second; + return true; +} + +std::vector CacheBuilder::ASLR_Tracker::getRebaseTargets() const { + std::vector targets; + for (const auto& target : _rebaseTarget32) + targets.push_back(target.first); + for (const auto& target : _rebaseTarget64) + targets.push_back(target.first); + return targets; +} //////////////////////////// DylibTextCoalescer //////////////////////////////////// -bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName) const { - if (sectionName.size() > 16) - sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - if (it == supportedSections.end()) - return false; - return !it->second->empty(); +bool CacheBuilder::DylibTextCoalescer::segmentWasCoalesced(std::string_view segmentName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); + + if ( segmentName == "__OBJC_CONST" ) { + return !cfStrings.empty(); + } + + return false; } -CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) { +bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view segmentName, + std::string_view sectionName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); if (sectionName.size() > 16) sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; + + if ( segmentName == "__TEXT" ) { + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + if (it == supportedSections.end()) + return false; + return !it->second->empty(); + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + return !cfStrings.empty(); + } + } + + return false; } -const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) const { +CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& +CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); if (sectionName.size() > 16) sectionName = sectionName.substr(0, 16); - std::map supportedSections = { - { "__objc_classname", &objcClassNames }, - { "__objc_methname", &objcMethNames }, - { "__objc_methtype", &objcMethTypes } - }; - auto it = supportedSections.find(sectionName); - assert(it != supportedSections.end()); - return *it->second; + + if ( segmentName == "__TEXT" ) { + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + return cfStrings; + } + } + + assert(false); +} + +const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& +CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + + if ( segmentName == "__TEXT" ) { + std::map supportedSections = { + { "__objc_classname", &objcClassNames }, + { "__objc_methname", &objcMethNames }, + { "__objc_methtype", &objcMethTypes } + }; + auto it = supportedSections.find(sectionName); + assert(it != supportedSections.end()); + return *it->second; + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + return cfStrings; + } + } + + assert(false); } //////////////////////////// CacheCoalescedText //////////////////////////////////// @@ -3559,8 +359,11 @@ const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = { "__objc_methtype", }; -void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma, - DylibTextCoalescer& textCoalescer) { +void CacheBuilder::CacheCoalescedText:: + parseCoalescableText(const dyld3::MachOAnalyzer* ma, + DylibTextCoalescer& textCoalescer, + const IMPCaches::SelectorMap& selectors, + IMPCaches::HoleMap& selectorsHoleMap) { static const bool log = false; // We can only remove sections if we know we have split seg v2 to point to it @@ -3586,17 +389,20 @@ void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAn const std::set supportedSections(std::begin(SupportedSections), std::end(SupportedSections)); int64_t slide = ma->getSlide(); + bool isSelectorsSection = false; for (auto sectionInfoIt = textSectionInfos.rbegin(); sectionInfoIt != textSectionInfos.rend(); ++sectionInfoIt) { const std::string& sectionName = sectionInfoIt->first; const dyld3::MachOAnalyzer::SectionInfo& sectInfo = sectionInfoIt->second; + isSelectorsSection = (sectionName == "__objc_methname"); + // If we find a section we can't handle then stop here. Hopefully we coalesced some from the end. if (supportedSections.find(sectionName) == supportedSections.end()) break; StringSection& cacheStringSection = getSectionData(sectionName); - DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName); + DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer("__TEXT", sectionName); // Walk the strings in this section const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide); @@ -3604,26 +410,313 @@ void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAn const char* end = s + sectInfo.sectSize; while ( s < end ) { std::string_view str = s; - auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); - if (itAndInserted.second) { - // If we inserted the string then we need to include it in the total - cacheStringSection.bufferSize += str.size() + 1; - if (log) - printf("Selector: %s -> %s\n", ma->installName(), s); - } else { + int cacheSectionOffset = 0; + + auto it = cacheStringSection.stringsToOffsets.find(str); + if (it != cacheStringSection.stringsToOffsets.end()) { // Debugging only. If we didn't include the string then we saved that many bytes cacheStringSection.savedSpace += str.size() + 1; + cacheSectionOffset = it->second; + } else if (isSelectorsSection) { + // If we are in the selectors section, we need to move + // the selectors in the selector map to their correct addresses, + // and fill the holes with the rest + +#if BUILDING_APP_CACHE_UTIL + cacheSectionOffset = cacheStringSection.bufferSize; +#else + const IMPCaches::SelectorMap::UnderlyingMap & map = selectors.map; + IMPCaches::SelectorMap::UnderlyingMap::const_iterator selectorsIterator = map.find(str); + if (selectorsIterator != map.end()) { + cacheSectionOffset = selectorsIterator->second->offset; + } else { + cacheSectionOffset = selectorsHoleMap.addStringOfSize((unsigned)str.size() + 1); + } +#endif + cacheStringSection.stringsToOffsets[str] = cacheSectionOffset; + uint32_t sizeAtLeast = cacheSectionOffset + (uint32_t)str.size() + 1; + if (cacheStringSection.bufferSize < sizeAtLeast) { + cacheStringSection.bufferSize = sizeAtLeast; + } + } else { + auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize }); + cacheSectionOffset = itAndInserted.first->second; + assert(itAndInserted.second); + + cacheStringSection.bufferSize += str.size() + 1; + if (log) { + printf("Selector: %s -> %s\n", ma->installName(), s); + } } // Now keep track of this offset in our source dylib as pointing to this offset uint32_t sourceSectionOffset = (uint32_t)((uint64_t)s - (uint64_t)content); - uint32_t cacheSectionOffset = itAndInserted.first->second; sectionStringData[sourceSectionOffset] = cacheSectionOffset; s += str.size() + 1; } } } +void CacheBuilder::CacheCoalescedText::parseCFConstants(const dyld3::MachOAnalyzer *ma, + DylibTextCoalescer &textCoalescer) { + static const bool log = false; + + // FIXME: Re-enable this once we can correctly patch the shared cache + if ( ma != nullptr ) + return; + + if ( !ma->is64() ) + return; + + // We only support chained fixups as we need to rewrite binds/rebases after applying split seg + // and that is much easier with chained fixups than opcodes + if ( !ma->hasChainedFixupsLoadCommand() ) + return; + + // FIXME: Support DYLD_CHAINED_PTR_ARM64E_USERLAND once ld64 moves to it. + const uint16_t pointerFormat = ma->chainedPointerFormat(); + if ( pointerFormat != DYLD_CHAINED_PTR_ARM64E ) + return; + + // We can only remove sections if we know we have split seg v2 to point to it + // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new constants + // which are no longer in the same segment + if ( !ma->isSplitSegV2() ) + return; + + // We can only remove sections from the end of a segment, so cache them all and walk backwards. + __block std::vector> dataSectionInfos; + __block uint64_t cstringStartVMAddr = 0; + __block uint64_t cstringEndVMAddr = 0; + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { + if ( malformedSectionRange ) + return; + if ( strcmp(sectInfo.segInfo.segName, "__OBJC_CONST") == 0 ) { + dataSectionInfos.push_back({ sectInfo.sectName, sectInfo }); + return; + } + if ( strcmp(sectInfo.segInfo.segName, "__TEXT") == 0 ) { + if ( strcmp(sectInfo.sectName, "__cstring") == 0 ) { + if ( ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) { + cstringStartVMAddr = sectInfo.sectAddr; + cstringEndVMAddr = cstringStartVMAddr + sectInfo.sectSize; + } + } + } + }); + + // We need to clear the chained pointer fixups for the whole segment, so can only + // process any type of CF object if we can process them all + if ( dataSectionInfos.size() != 1 ) + return; + + if ( dataSectionInfos.front().first != "__cfstring" ) + return; + + if ( cstringStartVMAddr == 0 ) + return; + + const dyld3::MachOAnalyzer::SectionInfo& cfStringsSection = dataSectionInfos.back().second; + + // A CFString is layed out in memory as + // { + // uintptr_t isa; + // uint32_t encoding; + // uint32_t padding; + // uintptr_t cstringData; + // uintptr_t cstringLength; + // } + const uint64_t cstringDataOffset = 16; + const char* className = cfStrings.isaClassName; + if ( cfStringsSection.sectSize % (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize ) { + // We don't support padding or any kind on the section + return; + } + + uint64_t baseAddress = ma->preferredLoadAddress(); + + uint64_t startVMOffset = cfStringsSection.sectAddr - baseAddress; + uint64_t endVMOffset = startVMOffset + cfStringsSection.sectSize; + + __block Diagnostics diag; + + // Make sure no symbols are pointing in to this section + __block bool hasSymbols = false; + ma->forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + uint64_t vmOffset = n_value - baseAddress; + if ( vmOffset < startVMOffset ) + return; + if ( vmOffset >= endVMOffset ) + return; + // In range of our section + hasSymbols = true; + stop = true; + }); + if ( diag.hasError() ) + return; + if ( hasSymbols ) + return; + + ma->forEachLocalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + uint64_t vmOffset = n_value - baseAddress; + if ( vmOffset < startVMOffset ) + return; + if ( vmOffset >= endVMOffset ) + return; + // In range of our section + hasSymbols = true; + stop = true; + }); + if ( diag.hasError() ) + return; + if ( hasSymbols ) + return; + + ma->forEachExportedSymbol(diag, ^(const char *symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char *importName, bool &stop) { + if ( imageOffset < startVMOffset ) + return; + if ( imageOffset >= endVMOffset ) + return; + // In range of our section + hasSymbols = true; + stop = true; + }); + if ( diag.hasError() ) + return; + if ( hasSymbols ) + return; + + __block std::vector dependentPaths; + ma->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, + bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + dependentPaths.push_back(loadPath); + }); + + // Find all the binds to the ISA class. These delineate the atoms + // In CoreFoundation itself, we are looking for rebases to the ISA + __block std::vector atomOffsets; + + bool dylibExportsISA = strcmp(ma->installName(), cfStrings.isaInstallName) == 0; + if ( !dylibExportsISA ) { + // This dylib doens't export the class, so look for binds to the ISA + __block std::vector> bindTargetSymbols; + ma->forEachChainedFixupTarget(diag, ^(int libraryOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { + bindTargetSymbols.push_back({ symbolName, libraryOrdinal }); + }); + + __block bool foundBadBind = false; + ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { + if ( foundBadBind ) + return; + ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, + const dyld_chained_starts_in_segment* segInfo, bool& stopFixups) { + // Skip anything not in this section + uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + if ( vmOffset < startVMOffset ) + return; + if ( vmOffset >= endVMOffset ) + return; + + uint32_t bindOrdinal; + int64_t ptrAddend; + if ( fixupLoc->isBind(pointerFormat, bindOrdinal, ptrAddend) ) { + if ( ptrAddend != 0 ) { + foundBadBind = true; + stopFixups = true; + return; + } + if ( bindOrdinal >= bindTargetSymbols.size() ) { + foundBadBind = true; + stopFixups = true; + return; + } + if ( strcmp(bindTargetSymbols[bindOrdinal].first, className) != 0 ) { + foundBadBind = true; + stopFixups = true; + return; + } + int libOrdinal = bindTargetSymbols[bindOrdinal].second; + if ( libOrdinal <= 0 ) { + foundBadBind = true; + stopFixups = true; + return; + } + int depIndex = libOrdinal - 1; + if ( depIndex >= dependentPaths.size() ) { + foundBadBind = true; + stopFixups = true; + return; + } + const char* depLoadPath = dependentPaths[depIndex]; + // All dylibs must find the ISA in the same place + if ( strcmp(cfStrings.isaInstallName, depLoadPath) != 0 ) { + foundBadBind = true; + stopFixups = true; + return; + } + atomOffsets.push_back(vmOffset); + } + }); + }); + if ( foundBadBind ) + return; + + if ( atomOffsets.empty() ) + return; + } + + if ( diag.hasError() ) + return; + + // Find all the rebases in the atoms, which correpond to pointers strings + __block std::map sectionRebases; + ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) { + ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stopFixups) { + // Skip anything not in this section + uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + if ( vmOffset < startVMOffset ) + return; + if ( vmOffset >= endVMOffset ) + return; + + uint64_t rebaseTargetRuntimeOffset; + if ( fixupLoc->isRebase(pointerFormat, 0, rebaseTargetRuntimeOffset) ) { + if ( dylibExportsISA && (rebaseTargetRuntimeOffset == cfStrings.isaVMOffset) ) { + atomOffsets.push_back(vmOffset); + } else { + sectionRebases[vmOffset] = rebaseTargetRuntimeOffset; + } + } + }); + }); + if ( diag.hasError() ) + return; + + // Every atom should have a single rebase to a cstring + if ( sectionRebases.size() != atomOffsets.size() ) + return; + + std::sort(atomOffsets.begin(), atomOffsets.end()); + for (uint64_t atomOffset : atomOffsets) { + auto it = sectionRebases.find(atomOffset + cstringDataOffset); + if ( it == sectionRebases.end() ) + return; + } + + CFSection& stringSection = this->cfStrings; + DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionData = textCoalescer.getSectionCoalescer("__OBJC_CONST", "__cfstring"); + for (uint64_t atomOffset : atomOffsets) { + if ( log ) + printf("%s: found __cfstring at: 0x%llx\n", ma->installName(), atomOffset); + + // Now keep track of this offset in our source dylib as pointing to this offset + uint32_t sourceSectionOffset = (uint32_t)(atomOffset - startVMOffset); + uint32_t cacheSectionOffset = stringSection.bufferSize; + sectionData[sourceSectionOffset] = cacheSectionOffset; + stringSection.bufferSize += (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize; + } +} + void CacheBuilder::CacheCoalescedText::clear() { *this = CacheBuilder::CacheCoalescedText(); } @@ -3655,3 +748,67 @@ const CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoales assert(it != supportedSections.end()); return *it->second; } + +uint64_t CacheBuilder::CacheCoalescedText::getSectionVMAddr(std::string_view segmentName, + std::string_view sectionName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + + if ( segmentName == "__TEXT" ) { + return getSectionData(sectionName).bufferVMAddr; + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + return cfStrings.bufferVMAddr; + } + } + + assert(false); +} + +uint8_t* CacheBuilder::CacheCoalescedText::getSectionBufferAddr(std::string_view segmentName, + std::string_view sectionName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + + if ( segmentName == "__TEXT" ) { + return getSectionData(sectionName).bufferAddr; + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + return cfStrings.bufferAddr; + } + } + + assert(false); +} + +uint64_t CacheBuilder::CacheCoalescedText::getSectionObjcTag(std::string_view segmentName, + std::string_view sectionName) const { + if (segmentName.size() > 16) + segmentName = segmentName.substr(0, 16); + if (sectionName.size() > 16) + sectionName = sectionName.substr(0, 16); + + if ( segmentName == "__TEXT" ) { + // Nothing has a tag in __TEXT + return 0; + } + + if ( segmentName == "__OBJC_CONST" ) { + if ( sectionName == "__cfstring" ) { + // This is defined by objc as the tag we put in the high bits + // FIXME: Get a tag from objc + // return 1ULL << 63; + return 0; + } + } + + assert(false); +} diff --git a/dyld3/shared-cache/CacheBuilder.h b/dyld3/shared-cache/CacheBuilder.h index 6d6ef41..eca46e5 100644 --- a/dyld3/shared-cache/CacheBuilder.h +++ b/dyld3/shared-cache/CacheBuilder.h @@ -35,8 +35,7 @@ #include "DyldSharedCache.h" #include "Diagnostics.h" #include "MachOAnalyzer.h" - - +#include "IMPCaches.hpp" template class LinkeditOptimizer; @@ -44,6 +43,7 @@ template class LinkeditOptimizer; class CacheBuilder { public: CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem); + virtual ~CacheBuilder(); struct InputFile { enum State { @@ -69,30 +69,26 @@ public: InputFile* inputFile; }; - void build(std::vector& inputFiles, - std::vector& aliases); - void build(const std::vector& dylibs, - const std::vector& otherOsDylibsInput, - const std::vector& osExecutables, - std::vector& aliases); - void build(const std::vector& dylibsToCache, - const std::vector& otherOsDylibs, - const std::vector& osExecutables, - std::vector& aliases); - void writeFile(const std::string& path); - void writeBuffer(uint8_t*& buffer, uint64_t& size); - void writeMapFile(const std::string& path); - std::string getMapFileBuffer(const std::string& cacheDisposition) const; - void deleteBuffer(); std::string errorMessage(); - const std::set warnings(); - const std::set evictions(); - const bool agileSignature(); - const std::string cdHashFirst(); - const std::string cdHashSecond(); - const std::string uuid() const; - void forEachCacheDylib(void (^callback)(const std::string& path)); + struct Region + { + uint8_t* buffer = nullptr; + uint64_t bufferSize = 0; + uint64_t sizeInUse = 0; + uint64_t unslidLoadAddress = 0; + uint64_t cacheFileOffset = 0; + uint8_t initProt = 0; + uint8_t maxProt = 0; + std::string name; + uint64_t index = ~0ULL; // The index of this region in the final binary + + // Each region can optionally have its own slide info + uint8_t* slideInfoBuffer = nullptr; + uint64_t slideInfoBufferSizeAllocated = 0; + uint64_t slideInfoFileOffset = 0; + uint64_t slideInfoFileSize = 0; + }; struct SegmentMappingInfo { const void* srcSegment; @@ -104,6 +100,8 @@ public: uint32_t dstCacheFileSize; uint32_t copySegmentSize; uint32_t srcSegmentIndex; + // Used by the AppCacheBuilder to work out which one of the regions this segment is in + const Region* parentRegion = nullptr; }; struct DylibTextCoalescer { @@ -114,9 +112,12 @@ public: DylibSectionOffsetToCacheSectionOffset objcMethNames; DylibSectionOffsetToCacheSectionOffset objcMethTypes; - bool sectionWasCoalesced(std::string_view sectionName) const; - DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName); - const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName) const; + DylibSectionOffsetToCacheSectionOffset cfStrings; + + bool segmentWasCoalesced(std::string_view segmentName) const; + bool sectionWasCoalesced(std::string_view segmentName, std::string_view sectionName) const; + DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view segmentName, std::string_view sectionName); + const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) const; }; struct CacheCoalescedText { @@ -132,75 +133,131 @@ public: uint64_t savedSpace = 0; }; + struct CFSection { + uint8_t* bufferAddr = nullptr; + uint32_t bufferSize = 0; + uint64_t bufferVMAddr = 0; + uint64_t cacheFileOffset = 0; + + // The install name of the dylib for the ISA + const char* isaInstallName = nullptr; + const char* isaClassName = "___CFConstantStringClassReference"; + uint64_t isaVMOffset = 0; + }; + StringSection objcClassNames; StringSection objcMethNames; StringSection objcMethTypes; + CFSection cfStrings; + void parseCoalescableText(const dyld3::MachOAnalyzer* ma, - DylibTextCoalescer& textCoalescer); + DylibTextCoalescer& textCoalescer, + const IMPCaches::SelectorMap& selectors, + IMPCaches::HoleMap& selectorHoleMap); + void parseCFConstants(const dyld3::MachOAnalyzer* ma, + DylibTextCoalescer& textCoalescer); void clear(); StringSection& getSectionData(std::string_view sectionName); const StringSection& getSectionData(std::string_view sectionName) const; + uint64_t getSectionVMAddr(std::string_view segmentName, std::string_view sectionName) const; + uint8_t* getSectionBufferAddr(std::string_view segmentName, std::string_view sectionName) const; + uint64_t getSectionObjcTag(std::string_view segmentName, std::string_view sectionName) const; }; class ASLR_Tracker { public: - ~ASLR_Tracker(); + ASLR_Tracker() = default; + ~ASLR_Tracker(); + + ASLR_Tracker(ASLR_Tracker&&) = delete; + ASLR_Tracker(const ASLR_Tracker&) = delete; + ASLR_Tracker& operator=(ASLR_Tracker&& other) = delete; + ASLR_Tracker& operator=(const ASLR_Tracker& other) = delete; void setDataRegion(const void* rwRegionStart, size_t rwRegionSize); - void add(void* p); + void add(void* loc, uint8_t level = (uint8_t)~0); + void setHigh8(void* p, uint8_t high8); + void setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key); + void setRebaseTarget32(void*p, uint32_t targetVMAddr); + void setRebaseTarget64(void*p, uint64_t targetVMAddr); void remove(void* p); - bool has(void* p); + bool has(void* loc, uint8_t* level = nullptr) const; const bool* bitmap() { return _bitmap; } unsigned dataPageCount() { return _pageCount; } - void disable() { _enabled = false; }; + unsigned pageSize() const { return _pageSize; } + void disable() { _enabled = false; }; + bool hasHigh8(void* p, uint8_t* highByte) const; + bool hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key) const; + bool hasRebaseTarget32(void* p, uint32_t* vmAddr) const; + bool hasRebaseTarget64(void* p, uint64_t* vmAddr) const; + + // Get all the out of band rebase targets. Used for the kernel collection builder + // to emit the classic relocations + std::vector getRebaseTargets() const; private: + enum { +#if BUILDING_APP_CACHE_UTIL + // The x86_64 kernel collection needs 1-byte aligned fixups + kMinimumFixupAlignment = 1 +#else + // Shared cache fixups must be at least 4-byte aligned + kMinimumFixupAlignment = 4 +#endif + }; + uint8_t* _regionStart = nullptr; - uint8_t* _endStart = nullptr; + uint8_t* _regionEnd = nullptr; bool* _bitmap = nullptr; unsigned _pageCount = 0; unsigned _pageSize = 4096; bool _enabled = true; + + struct AuthData { + uint16_t diversity; + bool addrDiv; + uint8_t key; + }; + std::unordered_map _high8Map; + std::unordered_map _authDataMap; + std::unordered_map _rebaseTarget32; + std::unordered_map _rebaseTarget64; + + // For kernel collections to work out which other collection a given + // fixup is relative to +#if BUILDING_APP_CACHE_UTIL + uint8_t* _cacheLevels = nullptr; +#endif }; typedef std::map> LOH_Tracker; - struct Region - { - uint8_t* buffer = nullptr; - uint64_t bufferSize = 0; - uint64_t sizeInUse = 0; - uint64_t unslidLoadAddress = 0; - uint64_t cacheFileOffset = 0; + // For use by the LinkeditOptimizer to work out which symbols to strip on each binary + enum class DylibStripMode { + stripNone, + stripLocals, + stripExports, + stripAll }; -private: + struct DylibInfo + { + const LoadedMachO* input; + std::string dylibID; + std::vector cacheLocation; + DylibTextCoalescer textCoalescer; + + // -> pointer + std::unordered_map, IMPCaches::ClassKeyHasher> impCachesClassData; + }; + +protected: template friend class LinkeditOptimizer; - - struct ArchLayout - { - uint64_t sharedMemoryStart; - uint64_t sharedMemorySize; - uint64_t textAndDataMaxSize; - uint64_t sharedRegionPadding; - uint64_t pointerDeltaMask; - const char* archName; - uint16_t csPageSize; - uint8_t sharedRegionAlignP2; - uint8_t slideInfoBytesPerPage; - bool sharedRegionsAreDiscontiguous; - bool is64; - bool useValueAdd; - }; - - static const ArchLayout _s_archLayout[]; - static const char* const _s_neverStubEliminateDylibs[]; - static const char* const _s_neverStubEliminateSymbols[]; struct UnmappedRegion { @@ -209,117 +266,58 @@ private: uint64_t sizeInUse = 0; }; - struct DylibInfo - { - const LoadedMachO* input; - std::string runtimePath; - std::vector cacheLocation; - DylibTextCoalescer textCoalescer; - }; + // Virtual methods overridden by the shared cache builder and app cache builder + virtual void forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) = 0; - void makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder); - void processSelectorStrings(const std::vector& executables); - void parseCoalescableSegments(); - void assignSegmentAddresses(); - - uint64_t cacheOverflowAmount(); - size_t evictLeafDylibs(uint64_t reductionTarget, std::vector& overflowDylibs); - - void fipsSign(); - void codeSign(); - uint64_t pathHash(const char* path); - void writeCacheHeader(); void copyRawSegments(); - void adjustAllImagesForNewSegmentLocations(); - void writeSlideInfoV1(); - 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& aliases); - void addOtherImageArray(const std::vector&, std::vector& overflowDylibs); - void addClosures(const std::vector&); - void markPaddingInaccessible(); - - bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)); - - template void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); - template void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, - std::vector& pageStarts, std::vector& pageExtras); - - template void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount); - template bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info); - template void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info, - std::vector& pageStarts, std::vector& pageExtras); + void adjustAllImagesForNewSegmentLocations(uint64_t cacheBaseAddress, + ASLR_Tracker& aslrTracker, LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText); // implemented in AdjustDylibSegemnts.cpp - void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const; + void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag, + uint64_t cacheBaseAddress, + CacheBuilder::ASLR_Tracker& aslrTracker, + CacheBuilder::LOH_Tracker* lohTracker, + const CacheBuilder::CacheCoalescedText* coalescedText) const; // implemented in OptimizerLinkedit.cpp - void optimizeLinkedit(); - - // implemented in OptimizerObjC.cpp - void optimizeObjC(); - uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount); - uint32_t computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount); + void optimizeLinkedit(UnmappedRegion* localSymbolsRegion, + const std::vector>& images); // implemented in OptimizerBranches.cpp - void optimizeAwayStubs(); - - typedef std::unordered_map InstallNameToMA; - - typedef uint64_t CacheOffset; + void optimizeAwayStubs(const std::vector>& images, + int64_t cacheSlide, uint64_t cacheUnslidAddr, + const DyldSharedCache* dyldCache, + const char* const neverStubEliminateSymbols[]); const DyldSharedCache::CreateOptions& _options; 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 _evictions; - const ArchLayout* _archLayout; - uint32_t _aliasCount; - uint64_t _slideInfoFileOffset; - uint64_t _slideInfoBufferSizeAllocated; - uint8_t* _objcReadOnlyBuffer; - uint64_t _objcReadOnlyBufferSizeUsed; - uint64_t _objcReadOnlyBufferSizeAllocated; - uint8_t* _objcReadWriteBuffer; - uint64_t _objcReadWriteBufferSizeAllocated; + TimeRecorder _timeRecorder; uint64_t _allocatedBufferSize; CacheCoalescedText _coalescedText; - uint64_t _selectorStringsFromExecutables; - std::vector _sortedDylibs; - InstallNameToMA _installNameToCacheDylib; - std::unordered_map _dataDirtySegsOrder; + bool _is64 = false; // Note this is mutable as the only parallel writes to it are done atomically to the bitmap mutable ASLR_Tracker _aslrTracker; - std::map _missingWeakImports; mutable LOH_Tracker _lohTracker; - const dyld3::closure::ImageArray* _imageArray; - uint32_t _sharedStringsPoolVmOffset; - uint8_t _cdHashFirst[20]; - uint8_t _cdHashSecond[20]; - std::unordered_map> _dylibToItsExports; - std::set> _dylibWeakExports; - std::unordered_map> _exportsToUses; - std::unordered_map _exportsToName; }; - - - inline uint64_t align(uint64_t addr, uint8_t p2) { uint64_t mask = (1 << p2); return (addr + mask - 1) & (-mask); } +inline uint8_t* align_buffer(uint8_t* addr, uint8_t p2) +{ + return (uint8_t *)align((uintptr_t)addr, p2); +} #endif /* CacheBuilder_h */ diff --git a/dyld3/shared-cache/DyldSharedCache.cpp b/dyld3/shared-cache/DyldSharedCache.cpp index 2d39af1..beffd5b 100644 --- a/dyld3/shared-cache/DyldSharedCache.cpp +++ b/dyld3/shared-cache/DyldSharedCache.cpp @@ -41,7 +41,7 @@ #include #include #include -#include "CacheBuilder.h" +#include "SharedCacheBuilder.h" #include "FileUtils.h" #endif @@ -59,6 +59,10 @@ #include #endif +#if (BUILDING_LIBDYLD || BUILDING_DYLD) +VIS_HIDDEN bool gEnableSharedCacheDataConst = false; +#endif + #if BUILDING_CACHE_BUILDER DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& options, @@ -67,8 +71,8 @@ DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& const std::vector& otherOsDylibs, const std::vector& osExecutables) { - CreateResults results; - CacheBuilder cache(options, fileSystem); + CreateResults results; + SharedCacheBuilder cache(options, fileSystem); if (!cache.errorMessage().empty()) { results.errorMessage = cache.errorMessage(); return results; @@ -115,7 +119,7 @@ DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& bool DyldSharedCache::verifySelfContained(std::vector& dylibsToCache, std::unordered_set& badZippered, - MappedMachO (^loader)(const std::string& runtimePath), + MappedMachO (^loader)(const std::string& runtimePath, Diagnostics& diag), std::vector>>& rejected) { // build map of dylibs @@ -132,6 +136,7 @@ bool DyldSharedCache::verifySelfContained(std::vector& dylibsToCach } // check all dependencies to assure every dylib in cache only depends on other dylibs in cache + __block std::set missingWeakDylibs; __block bool doAgain = true; while ( doAgain ) { __block std::vector foundMappings; @@ -141,6 +146,8 @@ bool DyldSharedCache::verifySelfContained(std::vector& dylibsToCach if ( badDylibs.count(dylib.runtimePath) != 0 ) continue; dylib.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( isWeak && (missingWeakDylibs.count(loadPath) != 0) ) + return; if ( knownDylibs.count(loadPath) == 0 ) { doAgain = true; if ( badZippered.count(loadPath) != 0 ) { @@ -151,11 +158,24 @@ bool DyldSharedCache::verifySelfContained(std::vector& dylibsToCach badZippered.insert(dylib.mh->installName()); return; } + Diagnostics diag; MappedMachO foundMapping; if ( badDylibs.count(loadPath) == 0 ) - foundMapping = loader(loadPath); + foundMapping = loader(loadPath, diag); if ( foundMapping.length == 0 ) { - badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'"); + // We allow weakly linked dylibs to be missing only if they are not present on disk + // The shared cache doesn't contain enough information to patch them in later if they are + // found on disk, so we don't want to pull something in to cache and cut it off from a dylib it + // could have used. + if ( isWeak ) { + missingWeakDylibs.insert(loadPath); + return; + } + + if (diag.hasError()) + badDylibs[dylib.runtimePath].insert(diag.errorMessage()); + else + badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'"); knownDylibs.erase(dylib.runtimePath); knownDylibs.erase(dylib.mh->installName()); } @@ -209,7 +229,14 @@ const T DyldSharedCache::getAddrField(uint64_t addr) const { return (const T)(addr + slide); } -void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const +uint64_t DyldSharedCache::getCodeSignAddress() const +{ + auto mappings = (const dyld_cache_mapping_info*)((uint8_t*)this + header.mappingOffset); + return mappings[header.mappingCount-1].address + mappings[header.mappingCount-1].size; +} + +void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags)) const { // sanity check cache header if ( strncmp(header.magic, "dyld_v1", 7) != 0 ) @@ -218,10 +245,18 @@ void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_ return; if ( header.mappingCount > 20 ) return; - const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); - const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount]; - for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) { - handler((char*)this + m->fileOffset, m->address, m->size, m->initProt); + if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) { + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount]; + for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) { + handler((char*)this + m->fileOffset, m->address, m->size, m->initProt, m->maxProt, 0); + } + } else { + const dyld_cache_mapping_and_slide_info* mappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset); + const dyld_cache_mapping_and_slide_info* mappingsEnd = &mappings[header.mappingCount]; + for (const dyld_cache_mapping_and_slide_info* m=mappings; m < mappingsEnd; ++m) { + handler((char*)this + m->fileOffset, m->address, m->size, m->initProt, m->maxProt, m->flags); + } } } @@ -236,7 +271,8 @@ bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) c uintptr_t unslidStart = (uintptr_t)addr - slide; // quick out if after end of cache - if ( unslidStart > (mappings[2].address + mappings[2].size) ) + const dyld_cache_mapping_info* lastMapping = &mappings[header.mappingCount - 1]; + if ( unslidStart > (lastMapping->address + lastMapping->size) ) return false; // walk cache regions @@ -252,6 +288,13 @@ bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) c return false; } +bool DyldSharedCache::isAlias(const char* path) 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); + // paths for aliases are store between cache header and first segment + return path < ((char*)mappings[0].address + slide); +} + 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); @@ -293,6 +336,62 @@ void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64 } } +const bool DyldSharedCache::hasLocalSymbolsInfo() const +{ + return (header.localSymbolsOffset != 0 && header.mappingOffset > offsetof(dyld_cache_header,localSymbolsSize)); +} + +const void* DyldSharedCache::getLocalNlistEntries() const +{ + // check for cache without local symbols info + if (!this->hasLocalSymbolsInfo()) + return nullptr; + const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset); + return (uint8_t*)localInfo + localInfo->nlistOffset; +} + +const uint32_t DyldSharedCache::getLocalNlistCount() const +{ + // check for cache without local symbols info + if (!this->hasLocalSymbolsInfo()) + return 0; + const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset); + return localInfo->nlistCount; +} + +const char* DyldSharedCache::getLocalStrings() const +{ + // check for cache without local symbols info + if (!this->hasLocalSymbolsInfo()) + return nullptr; + const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset); + return (char*)localInfo + localInfo->stringsOffset; +} + +const uint32_t DyldSharedCache::getLocalStringsSize() const +{ + // check for cache without local symbols info + if (!this->hasLocalSymbolsInfo()) + return 0; + const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset); + return localInfo->stringsSize; +} + + void DyldSharedCache::forEachLocalSymbolEntry(void (^handler)(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop)) const +{ + // check for cache without local symbols info + if (!this->hasLocalSymbolsInfo()) + return; + const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset); + const auto localEntries = (dyld_cache_local_symbols_entry*)((uint8_t*)localInfo + localInfo->entriesOffset); + bool stop = false; + for (uint32_t i = 0; i < localInfo->entriesCount; i++) { + dyld_cache_local_symbols_entry localEntry = localEntries[i]; + handler(localEntry.dylibOffset, localEntry.nlistStartIndex, localEntry.nlistCount, stop); + } +} + + 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); @@ -302,10 +401,17 @@ const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, uint64_ return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address); } + + const char* DyldSharedCache::getIndexedImagePath(uint32_t index) const +{ + auto dylibs = (const dyld_cache_image_info*)((char*)this + header.imagesOffset); + return (char*)this + dylibs[index].pathFileOffset; +} + 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 ) + if ( (header.mappingOffset <= __offsetof(dyld_cache_header, imagesTextOffset)) || (header.imagesTextCount == 0) ) return; // walk imageText table and call callback for each entry @@ -358,15 +464,16 @@ std::string DyldSharedCache::mapFile() const __block std::vector regionFileOffsets; result.reserve(256*1024); - forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags) { regionStartAddresses.push_back(vmAddr); regionSizes.push_back(size); regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)this); char lineBuffer[256]; const char* prot = "RW"; - if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) ) + if ( maxProt == (VM_PROT_EXECUTE|VM_PROT_READ) ) prot = "EX"; - else if ( permissions == VM_PROT_READ ) + else if ( maxProt == VM_PROT_READ ) prot = "RO"; if ( size > 1024*1024 ) sprintf(lineBuffer, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size); @@ -409,7 +516,8 @@ uint64_t DyldSharedCache::mappedSize() const { __block uint64_t startAddr = 0; __block uint64_t endAddr = 0; - forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags) { if ( startAddr == 0 ) startAddr = vmAddr; uint64_t end = vmAddr+size; @@ -473,6 +581,16 @@ bool DyldSharedCache::hasImagePath(const char* dylibPath, uint32_t& imageIndex) return false; } +bool DyldSharedCache::isOverridablePath(const char* dylibPath) const +{ + // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib + if ( header.cacheType == kDyldSharedCacheTypeProduction ) { + return (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0); + } + // in dev caches we can override all paths + return true; +} + bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const { // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib @@ -480,18 +598,19 @@ bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const if ( header.cacheType == kDyldSharedCacheTypeProduction ) { uint32_t imageIndex; pathIsInDyldCacheWhichCannotBeOverridden = this->hasImagePath(dylibPath, imageIndex); - if ( pathIsInDyldCacheWhichCannotBeOverridden && (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0) ) + if ( pathIsInDyldCacheWhichCannotBeOverridden && isOverridablePath(dylibPath) ) pathIsInDyldCacheWhichCannotBeOverridden = false; } return pathIsInDyldCacheWhichCannotBeOverridden; } +#if !BUILDING_LIBDSC 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) ) + if ( header.mappingOffset < __offsetof(dyld_cache_header, otherImageArrayAddr) ) return nullptr; if ( header.otherImageArrayAddr == 0 ) return nullptr; @@ -511,9 +630,6 @@ const dyld3::closure::Image* DyldSharedCache::findDlopenOtherImage(const char* p 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); @@ -575,7 +691,23 @@ void DyldSharedCache::forEachDlopenImage(void (^handler)(const char* runtimePath } } } -#endif + +void DyldSharedCache::forEachDylibPath(void (^handler)(const char* dylibPath, uint32_t index)) 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* dylibTrieStart = (uint8_t*)(this->header.dylibsTrieAddr + slide); + const uint8_t* dylibTrieEnd = dylibTrieStart + this->header.dylibsTrieSize; + + std::vector dylibEntries; + if ( Trie::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) { + for (DylibIndexTrie::Entry& entry : dylibEntries ) { + handler(entry.name.c_str(), entry.info.index); + } + } +} +#endif // !BUILDING_LIBDYLD && !BUILDING_DYLD +#endif // !BUILDING_LIBDSC const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() const { @@ -594,7 +726,7 @@ const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() cons const dyld3::closure::ImageArray* DyldSharedCache::otherOSImageArray() const { // check for old cache without imagesArray - if ( header.mappingOffset < sizeof(dyld_cache_header) ) + if ( header.mappingOffset < __offsetof(dyld_cache_header, otherImageArrayAddr) ) return nullptr; if ( header.otherImageArrayAddr == 0 ) @@ -665,6 +797,59 @@ void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t } } +#if (BUILDING_LIBDYLD || BUILDING_DYLD) +void DyldSharedCache::changeDataConstPermissions(mach_port_t machTask, uint32_t permissions, + DataConstLogFunc logFunc) 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); + + if ( (permissions & VM_PROT_WRITE) != 0 ) + permissions |= VM_PROT_COPY; + + forEachRegion(^(const void *, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags) { + void* content = (void*)(vmAddr + slide); + if ( ( flags & DYLD_CACHE_MAPPING_CONST_DATA) == 0 ) + return; + if ( logFunc != nullptr ) { + logFunc("dyld: marking shared cache range 0x%x permissions: 0x%09lX -> 0x%09lX\n", + permissions, (long)content, (long)content + size); + } + kern_return_t result = vm_protect(machTask, (vm_address_t)content, (vm_size_t)size, false, permissions); + if ( result != KERN_SUCCESS ) { + if ( logFunc != nullptr ) + logFunc("dyld: failed to mprotect shared cache due to: %d\n", result); + } + }); +} + +DyldSharedCache::DataConstLazyScopedWriter::DataConstLazyScopedWriter(const DyldSharedCache* cache, mach_port_t machTask, DataConstLogFunc logFunc) + : cache(cache), machTask(machTask), logFunc(logFunc) { +} + +DyldSharedCache::DataConstLazyScopedWriter::~DataConstLazyScopedWriter() { + if ( wasMadeWritable ) + cache->changeDataConstPermissions(machTask, VM_PROT_READ, logFunc); +} + +void DyldSharedCache::DataConstLazyScopedWriter::makeWriteable() { + if ( wasMadeWritable ) + return; + if ( !gEnableSharedCacheDataConst ) + return; + if ( cache == nullptr ) + return; + wasMadeWritable = true; + cache->changeDataConstPermissions(machTask, VM_PROT_READ | VM_PROT_WRITE, logFunc); +} + +DyldSharedCache::DataConstScopedWriter::DataConstScopedWriter(const DyldSharedCache* cache, mach_port_t machTask, DataConstLogFunc logFunc) + : writer(cache, machTask, logFunc) { + writer.makeWriteable(); +} +#endif + #if !(BUILDING_LIBDYLD || BUILDING_DYLD) // MRM map file generator std::string DyldSharedCache::generateJSONMap(const char* disposition) const { @@ -738,25 +923,68 @@ std::string DyldSharedCache::generateJSONDependents() const { #endif +#if !(BUILDING_LIBDYLD || BUILDING_DYLD) +dyld3::MachOAnalyzer::VMAddrConverter DyldSharedCache::makeVMAddrConverter(bool contentRebased) const { + typedef dyld3::MachOAnalyzer::VMAddrConverter VMAddrConverter; + __block VMAddrConverter::SharedCacheFormat pointerFormat = VMAddrConverter::SharedCacheFormat::none; + __block uint64_t pointerValueAdd = 0;; + forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) { + assert(slideInfoHeader->version >= 2); + if ( slideInfoHeader->version == 2 ) { + const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader); + assert(slideInfo->delta_mask == 0x00FFFF0000000000); + pointerFormat = VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi; + pointerValueAdd = slideInfo->value_add; + } else if ( slideInfoHeader->version == 3 ) { + pointerFormat = VMAddrConverter::SharedCacheFormat::v3; + pointerValueAdd = unslidLoadAddress(); + } else { + assert(false); + } + }); -const dyld_cache_slide_info* DyldSharedCache::slideInfo() 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 offsetInLinkEditRegion = (header.slideInfoOffset - mappings[2].fileOffset); + VMAddrConverter vmAddrConverter; + vmAddrConverter.preferredLoadAddress = pointerValueAdd; + vmAddrConverter.slide = slide; + vmAddrConverter.chainedPointerFormat = 0; + vmAddrConverter.sharedCacheChainedPointerFormat = pointerFormat; + vmAddrConverter.contentRebased = contentRebased; + + return vmAddrConverter; +} +#endif + +const dyld_cache_slide_info* DyldSharedCache::legacyCacheSlideInfo() const +{ + assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset)); + 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 offsetInLinkEditRegion = (header.slideInfoOffsetUnused - mappings[2].fileOffset); return (dyld_cache_slide_info*)((uint8_t*)(mappings[2].address) + slide + offsetInLinkEditRegion); } -const uint8_t* DyldSharedCache::dataRegionStart() const +const dyld_cache_mapping_info* DyldSharedCache::legacyCacheDataRegionMapping() const { + assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset)); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + return &mappings[1]; +} + +const uint8_t* DyldSharedCache::legacyCacheDataRegionBuffer() const +{ + assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset)); 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); - return (uint8_t*)(mappings[1].address) + slide; + return (uint8_t*)(legacyCacheDataRegionMapping()->address) + slide; } +#if !BUILDING_LIBDSC const objc_opt::objc_opt_t* DyldSharedCache::objcOpt() const { // Find the objc image const dyld3::MachOAnalyzer* objcMA = nullptr; @@ -810,7 +1038,7 @@ const void* DyldSharedCache::objcOptPtrs() const { int64_t slide = objcMA->getSlide(); uint32_t pointerSize = objcMA->pointerSize(); objcMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) { - if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 ) + if ( (strncmp(info.segInfo.segName, "__DATA", 6) != 0) && (strncmp(info.segInfo.segName, "__AUTH", 6) != 0) ) return; if (strcmp(info.sectName, "__objc_opt_ptrs") != 0) return; @@ -827,6 +1055,97 @@ const void* DyldSharedCache::objcOptPtrs() const { return objcPointersContent; } +#endif + +std::pair DyldSharedCache::getObjCConstantRange() const { + const dyld3::MachOAnalyzer* libDyldMA = nullptr; + uint32_t imageIndex; + if ( hasImagePath("/usr/lib/system/libdyld.dylib", imageIndex) ) { + const dyld3::closure::ImageArray* images = cachedDylibsImageArray(); + const dyld3::closure::Image* image = images->imageForNum(imageIndex+1); + libDyldMA = (const dyld3::MachOAnalyzer*)((uintptr_t)this + image->cacheOffset()); + + std::pair ranges = { nullptr, 0 }; +#if TARGET_OS_OSX + ranges.first = libDyldMA->findSectionContent("__DATA", "__objc_ranges", ranges.second); +#else + ranges.first = libDyldMA->findSectionContent("__DATA_CONST", "__objc_ranges", ranges.second); +#endif + return ranges; + } + + return { nullptr, 0 }; +} + +bool DyldSharedCache::hasSlideInfo() const { + if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) { + return header.slideInfoSizeUnused != 0; + } else { + const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset); + for (uint32_t i = 0; i != header.mappingWithSlideCount; ++i) { + if ( slidableMappings[i].slideInfoFileSize != 0 ) { + return true; + } + } + } + return false; +} + +void DyldSharedCache::forEachSlideInfo(void (^handler)(uint64_t mappingStartAddress, uint64_t mappingSize, + const uint8_t* mappingPagesStart, + uint64_t slideInfoOffset, uint64_t slideInfoSize, + const dyld_cache_slide_info* slideInfoHeader)) const { + if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) { + // Old caches should get the slide info from the cache header and assume a single data region. + const dyld_cache_mapping_info* dataMapping = legacyCacheDataRegionMapping(); + uint64_t dataStartAddress = dataMapping->address; + uint64_t dataSize = dataMapping->size; + const uint8_t* dataPagesStart = legacyCacheDataRegionBuffer(); + const dyld_cache_slide_info* slideInfoHeader = legacyCacheSlideInfo(); + + handler(dataStartAddress, dataSize, dataPagesStart, + header.slideInfoOffsetUnused, header.slideInfoSizeUnused, slideInfoHeader); + } else { + const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset); + const dyld_cache_mapping_and_slide_info* linkeditMapping = &slidableMappings[header.mappingWithSlideCount - 1]; + uint64_t sharedCacheSlide = (uint64_t)this - unslidLoadAddress(); + + for (uint32_t i = 0; i != header.mappingWithSlideCount; ++i) { + if ( slidableMappings[i].slideInfoFileOffset != 0 ) { + // Get the data pages + uint64_t dataStartAddress = slidableMappings[i].address; + uint64_t dataSize = slidableMappings[i].size; + const uint8_t* dataPagesStart = (uint8_t*)dataStartAddress + sharedCacheSlide; + + // Get the slide info + uint64_t offsetInLinkEditRegion = (slidableMappings[i].slideInfoFileOffset - linkeditMapping->fileOffset); + const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)((uint8_t*)(linkeditMapping->address) + sharedCacheSlide + offsetInLinkEditRegion); + handler(dataStartAddress, dataSize, dataPagesStart, + slidableMappings[i].slideInfoFileOffset, slidableMappings[i].slideInfoFileSize, slideInfoHeader); + } + } + } +} + +#if BUILDING_LIBDYLD +const char* DyldSharedCache::getCanonicalPath(const char *path) const { + uint32_t dyldCacheImageIndex; + if ( hasImagePath(path, dyldCacheImageIndex) ) + return getIndexedImagePath(dyldCacheImageIndex); +#if TARGET_OS_OSX + // on macOS support "Foo.framework/Foo" symlink + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int realpathErrno = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) { + if ( hasImagePath(resolvedPath, dyldCacheImageIndex) ) + return getIndexedImagePath(dyldCacheImageIndex); + } +#endif + return nullptr; +} +#endif #if !(BUILDING_LIBDYLD || BUILDING_DYLD) void DyldSharedCache::fillMachOAnalyzersMap(std::unordered_map & dylibAnalyzers) const { diff --git a/dyld3/shared-cache/DyldSharedCache.h b/dyld3/shared-cache/DyldSharedCache.h index 2696843..e081648 100644 --- a/dyld3/shared-cache/DyldSharedCache.h +++ b/dyld3/shared-cache/DyldSharedCache.h @@ -28,6 +28,10 @@ #include #include +#if (BUILDING_LIBDYLD || BUILDING_DYLD) +#include +#endif + #if !(BUILDING_LIBDYLD || BUILDING_DYLD) #include #include @@ -40,6 +44,7 @@ #include "Diagnostics.h" #include "MachOAnalyzer.h" #include "Closure.h" +#include "JSON.h" namespace objc_opt { struct objc_opt_t; @@ -57,15 +62,22 @@ public: Agile = 2 }; + enum class LocalSymbolsMode { + keep, + unmap, + strip + }; + struct CreateOptions { std::string outputFilePath; std::string outputMapFilePath; const dyld3::GradedArchs* archs; dyld3::Platform platform; - bool excludeLocalSymbols; + LocalSymbolsMode localSymbolMode; bool optimizeStubs; - bool optimizeObjC; + bool optimizeDyldDlopens; + bool optimizeDyldLaunches; CodeSigningDigestMode codeSigningDigestMode; bool dylibsRemovedDuringMastering; bool inodesAreSameAsRuntime; @@ -76,6 +88,7 @@ public: bool evictLeafDylibsOnOverflow; std::unordered_map dylibOrdering; std::unordered_map dirtyDataSegmentOrdering; + dyld3::json::Node objcOptimizations; std::string loggingPrefix; }; @@ -118,7 +131,7 @@ public: // outset the set. It will call back the loader function to try to find any mising dylibs. static bool verifySelfContained(std::vector& dylibsToCache, std::unordered_set& badZippered, - MappedMachO (^loader)(const std::string& runtimePath), std::vector>>& excluded); + MappedMachO (^loader)(const std::string& runtimePath, Diagnostics& diag), std::vector>>& excluded); // @@ -154,7 +167,7 @@ public: // std::string mapFile() const; -#endif // TARGET_OS_OSX +#endif // BUILDING_CACHE_BUILDER // @@ -181,13 +194,31 @@ public: bool hasImagePath(const char* dylibPath, uint32_t& imageIndex) const; + // + // Is this path (which we know is in the shared cache), overridable + // + bool isOverridablePath(const char* dylibPath) const; + + // // Path is to a dylib in the cache and this is an optimized cache so that path cannot be overridden // bool hasNonOverridablePath(const char* dylibPath) const; - // + // + // Check if shared cache contains local symbols info + // + const bool hasLocalSymbolsInfo() const; + + + // + // Get code signature mapped address + // + uint64_t getCodeSignAddress() const; + + + // // Searches cache for dylib with specified mach_header // bool findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const; @@ -199,13 +230,29 @@ public: // - // Iterates over each dylib in the cache + // Get image entry from index // const mach_header* getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& node) const; + // iterates over all dylibs and aliases + void forEachDylibPath(void (^handler)(const char* dylibPath, uint32_t index)) const; + + // - // Iterates over each dylib in the cache + // Get image path from index + // + const char* getIndexedImagePath(uint32_t index) const; + +#if BUILDING_LIBDYLD + // + // Get the canonical (dylib) path for a given path, which may be a symlink to something in the cache + // + const char* getCanonicalPath(const char* path) const; +#endif + + // + // Iterates over each text segment in the cache // void forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop)) const; @@ -213,14 +260,48 @@ public: // // Iterates over each of the three regions in the cache // - void forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const; + void forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags)) const; + // + // Get local symbols nlist entries + // + const void* getLocalNlistEntries() const; + + + // + // Get local symbols nlist count + // + const uint32_t getLocalNlistCount() const; + + + // + // Get local symbols strings + // + const char* getLocalStrings() const; + + + // + // Get local symbols strings size + // + const uint32_t getLocalStringsSize() const; + + + // + // Iterates over each local symbol entry in the cache + // + void forEachLocalSymbolEntry(void (^handler)(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop)) 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 true if a path is an alternate path (symlink) + // + bool isAlias(const char* path) const; // // returns address the cache would load at if unslid @@ -278,12 +359,17 @@ public: // // Returns the pointer to the slide info for this cache // - const dyld_cache_slide_info* slideInfo() const; + const dyld_cache_slide_info* legacyCacheSlideInfo() const; + + // + // Returns a pointer to the __DATA region mapping in the cache + // + const dyld_cache_mapping_info* legacyCacheDataRegionMapping() const; // // Returns a pointer to the start of the __DATA region in the cache // - const uint8_t* dataRegionStart() const; + const uint8_t* legacyCacheDataRegionBuffer() const; // // Returns a pointer to the shared cache optimized Objective-C data structures @@ -295,6 +381,15 @@ public: // const void* objcOptPtrs() const; + // Returns true if the cache has any slide info, either old style on a single data region + // or on each individual data mapping + bool hasSlideInfo() const; + + void forEachSlideInfo(void (^handler)(uint64_t mappingStartAddress, uint64_t mappingSize, + const uint8_t* mappingPagesStart, + uint64_t slideInfoOffset, uint64_t slideInfoSize, + const dyld_cache_slide_info* slideInfoHeader)) const; + // // returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index @@ -322,6 +417,45 @@ public: return dummy.arm64e.keyName(); } +#if (BUILDING_LIBDYLD || BUILDING_DYLD) + + typedef void (*DataConstLogFunc)(const char*, ...) __attribute__((format(printf, 1, 2))); + void changeDataConstPermissions(mach_port_t machTask, uint32_t permissions, DataConstLogFunc logFunc) const; + + struct DataConstLazyScopedWriter { + DataConstLazyScopedWriter(const DyldSharedCache* cache, mach_port_t machTask, DataConstLogFunc logFunc); + ~DataConstLazyScopedWriter(); + + // Delete all other kinds of constructors to make sure we don't accidentally copy these around + DataConstLazyScopedWriter() = delete; + DataConstLazyScopedWriter(const DataConstLazyScopedWriter&) = delete; + DataConstLazyScopedWriter(DataConstLazyScopedWriter&&) = delete; + DataConstLazyScopedWriter& operator=(const DataConstLazyScopedWriter&) = delete; + DataConstLazyScopedWriter& operator=(DataConstLazyScopedWriter&&) = delete; + + void makeWriteable(); + + const DyldSharedCache* cache = nullptr; + mach_port_t machTask = MACH_PORT_NULL; + DataConstLogFunc logFunc = nullptr; + bool wasMadeWritable = false; + }; + + struct DataConstScopedWriter { + DataConstScopedWriter(const DyldSharedCache* cache, mach_port_t machTask, DataConstLogFunc logFunc); + ~DataConstScopedWriter() = default; + + // Delete all other kinds of constructors to make sure we don't accidentally copy these around + DataConstScopedWriter() = delete; + DataConstScopedWriter(const DataConstScopedWriter&) = delete; + DataConstScopedWriter(DataConstScopedWriter&&) = delete; + DataConstScopedWriter& operator=(const DataConstScopedWriter&) = delete; + DataConstScopedWriter& operator=(DataConstScopedWriter&&) = delete; + + DataConstLazyScopedWriter writer; + }; +#endif + #if !(BUILDING_LIBDYLD || BUILDING_DYLD) // MRM map file generator std::string generateJSONMap(const char* disposition) const; @@ -336,8 +470,26 @@ public: std::string generateJSONDependents() const; #endif + // Note these enum entries are only valid for 64-bit archs. + enum class ConstantClasses { + cfStringAtomSize = 32 + }; + + // Returns the start and size of the range in the shared cache of the ObjC constants, such as + // all of the CFString's which have been moved in to a contiguous range + std::pair getObjCConstantRange() const; + +#if !(BUILDING_LIBDYLD || BUILDING_DYLD) + dyld3::MachOAnalyzer::VMAddrConverter makeVMAddrConverter(bool contentRebased) const; +#endif + dyld_cache_header header; + // The most mappings we could generate. + // For now its __TEXT, __DATA_CONST, __DATA_DIRTY, __DATA, __LINKEDIT, + // and optionally also __AUTH, __AUTH_CONST, __AUTH_DIRTY + static const uint32_t MaxMappings = 8; + private: // Returns a variable of type "const T" which corresponds to the header field with the given unslid address template diff --git a/dyld3/shared-cache/FileUtils.cpp b/dyld3/shared-cache/FileUtils.cpp index ae5cbb6..051c6d7 100644 --- a/dyld3/shared-cache/FileUtils.cpp +++ b/dyld3/shared-cache/FileUtils.cpp @@ -38,9 +38,7 @@ #include #include #include -#ifndef DARLING #include -#endif #include #include @@ -49,15 +47,8 @@ #include "FileUtils.h" #include "StringUtils.h" #include "Diagnostics.h" +#include "JSONReader.h" -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 -extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import)); -#endif - -#ifdef DARLING -static int rootless_check_trusted(const char* path) { return -1; } -static int rootless_check_trusted_class(const char* path, const char* cls) { return -1; } -#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, bool recurse) { @@ -69,7 +60,7 @@ void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path } while (dirent* entry = readdir(dir)) { struct stat statBuf; - std::string dirAndFile = path + "/" + entry->d_name; + std::string dirAndFile = path + (path.back() != '/' ? "/" : "") + entry->d_name; std::string fullDirAndFile = pathPrefix + dirAndFile; switch ( entry->d_type ) { case DT_REG: @@ -142,49 +133,6 @@ const void* mapFileReadOnly(const char* path, size_t& mappedSize) return nullptr; } -static bool sipIsEnabled() -{ - static bool rootlessEnabled; - static dispatch_once_t onceToken; - // Check to make sure file system protections are on at all - dispatch_once(&onceToken, ^{ - rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0); - }); - return rootlessEnabled; -} - -bool isProtectedBySIP(const std::string& path) -{ - if ( !sipIsEnabled() ) - return false; - - return (rootless_check_trusted(path.c_str()) == 0); -} - -bool isProtectedBySIPExceptDyld(const std::string& path) -{ - if ( !sipIsEnabled() ) - return false; - - return (rootless_check_trusted_class(path.c_str(), "dyld") == 0); -} - -bool isProtectedBySIP(int fd) -{ - if ( !sipIsEnabled() ) - return false; - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 - return (rootless_check_trusted_fd(fd) == 0); -#else - // fallback to using rootless_check_trusted - char realPath[MAXPATHLEN]; - if ( fcntl(fd, F_GETPATH, realPath) == 0 ) - return (rootless_check_trusted(realPath) == 0); - return false; -#endif -} - bool fileExists(const std::string& path) { struct stat statBuf; diff --git a/dyld3/shared-cache/FileUtils.h b/dyld3/shared-cache/FileUtils.h index 6bc5410..86e2649 100644 --- a/dyld3/shared-cache/FileUtils.h +++ b/dyld3/shared-cache/FileUtils.h @@ -36,6 +36,7 @@ #include #include "DyldSharedCache.h" +#include "JSON.h" class Diagnostics; @@ -73,10 +74,6 @@ bool safeSave(const void* buffer, size_t bufferLen, const std::string& path); const void* mapFileReadOnly(const char* path, size_t& mappedSize); -bool isProtectedBySIP(const std::string& path); -bool isProtectedBySIPExceptDyld(const std::string& path); -bool isProtectedBySIP(int fd); - bool fileExists(const std::string& path); std::unordered_map parseOrderFile(const std::string& orderFileData); diff --git a/dyld3/shared-cache/IMPCaches.cpp b/dyld3/shared-cache/IMPCaches.cpp new file mode 100644 index 0000000..5b7b7e8 --- /dev/null +++ b/dyld3/shared-cache/IMPCaches.cpp @@ -0,0 +1,2194 @@ +// +// IMPCaches.cpp +// dyld_shared_cache_builder +// +// Created by Thomas Deniau on 18/12/2019. +// + +#include "FileAbstraction.hpp" +#include "IMPCaches.hpp" +#include "IMPCachesBuilder.hpp" +#include "JSONReader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace IMPCaches { + +std::string ClassData::description() const +{ + std::stringstream ss; + ss << name << " modulo:" << modulo(); + if (isMetaclass) ss << " (metaclass)"; + return ss.str(); +} + +std::string ClassData::PlacementAttempt::description() const +{ + std::stringstream stream; + stream << "needed bits: " << neededBits << ", shift: " << shift; + return stream.str(); +} + +typename ClassData::PlacementAttempt ClassData::attemptForShift(int shiftToTry, int neededBitsToTry) const +{ + int totalNumberOfBitsToSet = 0; + int mask = (1 << neededBitsToTry) - 1; + for (const Method& method : methods) { + totalNumberOfBitsToSet += method.selector->numberOfBitsToSet(shiftToTry, mask); + } + + return ClassData::PlacementAttempt(totalNumberOfBitsToSet, shiftToTry, neededBitsToTry); +} + +std::vector ClassData::attempts() const +{ + // We have 26 MB of selectors, and among them only ~ 7MB are deemed + // "interesting" to include in our hash tables. + + // So we should be able to fit on 24 bits of address space (~ 16 MB of selectors), + // but need to keep the low 7 bits available so that we + // don't have to worry about selector length and so that + // we don't have to worry about collisions. (i.e. multiple selectors + // end up at the same address with this algorithm and then we play + // in another step with + // the low 7 bits to give them all an unique address). + // Then if there are holes given this 128-byte alignment, we can + // fill the holes with selectors excluded from the algorithm. + + // Let us grow the hash tables to one more bit if needed + // as the full problem is too difficult. + std::vector allowedNeededBits { neededBits, neededBits + 1 }; + std::vector attempts; + for (auto i : allowedNeededBits) { + // Go thrugh all the possible shifts, starting at 0, knowing that + // shift+neededBits needs to fit on 17 bits. + + for (int shiftToTry = 0; shiftToTry <= 17 - i; shiftToTry++) { + attempts.push_back(attemptForShift(shiftToTry, i)); + } + } + sort(attempts.begin(), attempts.end()); + return attempts; +} + +void ClassData::resetSlots() +{ +#if DEBUG_SLOTS + slots.assign(slots.size(), nullptr); +#else + slots.assign(slots.size(), false); +#endif +} + +void ClassData::backtrack(typename ClassData::PlacementAttempt::Result& resultToBacktrackFrom) +{ + if (!resultToBacktrackFrom.success) { + // We backtrack from a failure if we decided to skip a class that was too difficult to place. + // In this case there's nothing to do. + + return; + } + + // Restore the addresses and masks we had in place before we did the step that let to resultToBacktrackFrom + typename PlacementAttempt::PreviousState previousState = resultToBacktrackFrom.previousState; + for (const Method& method : methods) { + Selector* selector = method.selector; + typename PlacementAttempt::PreviousMethodAddress previousMethod = previousState.methods[selector]; + selector->inProgressBucketIndex = previousMethod.address; + selector->fixedBitsMask = previousMethod.fixedBitsMask; + } + + shift = previousState.shift; + neededBits = previousState.neededBits; +} + +// Compute the number of needed bits for the hash table now that all the methods have been added +void ClassData::didFinishAddingMethods() +{ + if (methods.size() == 0) { + neededBits = 0; + } else { + neededBits = (int)ceil(log2(double(methods.size()))); + } +} + +bool ClassData::hadToIncreaseSize() const +{ + if (methods.size() == 0) return false; + return neededBits > (int)(ceil(log2((double)(methods.size())))); +} + +/// Try to see if we can make the shift and mask in @param attempt work. +typename ClassData::PlacementAttempt::Result ClassData::applyAttempt(const PlacementAttempt& attempt, std::minstd_rand & randomNumberGenerator) +{ + std::vector sortedMethods; + sortedMethods.reserve(methods.size()); + for (const Method& method : methods) { + sortedMethods.push_back(method.selector); + } + + // Solve from most constrained to least constrained + std::sort(sortedMethods.begin(), sortedMethods.end(), [attempt](Selector* m1, Selector* m2) { + return m1->numberOfBitsToSet(attempt.shift, attempt.mask()) < m2->numberOfBitsToSet(attempt.shift, attempt.mask()); + }); + + if (slots.size() < (1 << attempt.neededBits)) { +#if DEBUG_SLOTS + slots.resize(1 << attempt.neededBits, nullptr); +#else + slots.resize(1 << attempt.neededBits, false); +#endif + } + + resetSlots(); + + std::vector addresses; + for (auto m : sortedMethods) { + bool found = false; + + // Check if all the bits are already assigned + int shiftedMask = attempt.mask() << attempt.shift; + if ((m->fixedBitsMask & shiftedMask) == shiftedMask) { + int index = (m->inProgressBucketIndex >> attempt.shift) & attempt.mask(); + if (slots[index]) { + typename ClassData::PlacementAttempt::Result result; + result.success = false; + return result; + } +#if DEBUG_SLOTS + slots[index] = m; +#else + slots[index] = true; +#endif + found = true; + addresses.push_back(index); + } else { + // Some bits are not assigned yet, so try to find an address that would be compatible + // with the existing bits for this method. + + int attemptModulo = 1 << attempt.neededBits; + + // possibleAddresses = 0.. possibleAddresses(attemptModulo); + std::iota(possibleAddresses.begin(), possibleAddresses.end(), 0); + + // We randomize the addresses to try so that two random selectors + // have as many ranges of different bits as possible, in order + // to find a satisfying shift for every class. + + std::shuffle(possibleAddresses.begin(), possibleAddresses.end(), randomNumberGenerator); + for (auto i : possibleAddresses) { + int futureAddress = m->inProgressBucketIndex | (i << attempt.shift); + int slot = (futureAddress >> attempt.shift) & attempt.mask(); + + // Make sure the new address is compatible with the existing bits + bool addressesMatch = (futureAddress & m->fixedBitsMask) == (m->inProgressBucketIndex & m->fixedBitsMask); + if (addressesMatch && !slots[slot]) { +#if DEBUG_SLOTS + slots[slot] = m; +#else + slots[slot] = true; +#endif + found = true; + addresses.push_back(i); + break; + } + } + if (!found) { + typename ClassData::PlacementAttempt::Result result; + result.success = false; + return result; + } + } + } + + // We succeeded, record the state so that we can backtrack if needed + std::unordered_map previousMethods; + for (unsigned long i = 0; i < sortedMethods.size(); i++) { + Selector* m = sortedMethods[i]; + int previousAddress = m->inProgressBucketIndex; + int previousMask = m->fixedBitsMask; + m->inProgressBucketIndex |= addresses[i] << attempt.shift; + m->fixedBitsMask |= attempt.mask() << attempt.shift; + previousMethods[m] = typename PlacementAttempt::PreviousMethodAddress { + .address = previousAddress, + .fixedBitsMask = previousMask + }; + } + + typename PlacementAttempt::PreviousState previousState { + .neededBits = neededBits, + .shift = shift, + .methods = previousMethods + }; + shift = attempt.shift; + neededBits = attempt.neededBits; + + typename PlacementAttempt::Result result { + .success = true, + .previousState = previousState + }; + + return result; +} + +bool ClassData::checkConsistency() { + resetSlots(); + for (const Method& method : methods) { + const Selector* s = method.selector; + int slotIndex = (s->inProgressBucketIndex >> shift) & mask(); + if (slots[slotIndex]) { + return false; + } +#if DEBUG_SLOTS + slots[slotIndex] = s; +#else + slots[slotIndex] = true; +#endif + } + return true; +} + +Constraint ClassData::constraintForMethod(const Selector* method) { + resetSlots(); + allowedValues.clear(); + + // Fill the slots with all our methods except `method` + for (const Method& m : methods) { + const Selector* s = m.selector; + if (s == method) { + continue; + } + + int slotIndex = (s->inProgressBucketIndex >> shift) & mask(); +#if DEBUG_SLOTS + assert(slots[slotIndex] == nullptr); + slots[slotIndex] = s; +#else + assert(!slots[slotIndex]); + slots[slotIndex] = true; +#endif + } + + // What are the remaining empty slots in which we could put `method`? + int max = 1 << neededBits; + for (int i = 0 ; i < max ; i++) { + if (!slots[i]) { + allowedValues.push_back(i); + } + } + + auto allowedSet = std::unordered_set(allowedValues.begin(), allowedValues.end()); + return Constraint { + .mask = mask(), + .shift = shift, + .allowedValues = allowedSet + }; +} + +size_t ClassData::sizeInSharedCache() const { + return IMPCaches::sizeForImpCacheWithCount(modulo()); +} + +int AddressSpace::sizeAtIndex(int idx) const +{ + if (sizes.find(idx) != sizes.end()) { + return sizes.at(idx); + } else { + return 0; + } +} + +void AddressSpace::removeUninterestingSelectors() { + for (auto& [k,v] : methodsByIndex) { + v.erase(std::remove_if(v.begin(), v.end(), [](const Selector* s){ + return s->classes.size() == 0; + }), v.end()); + } +} + +int AddressSpace::sizeAvailableAfterIndex(int idx) const +{ + int availableAfterThisAddress = bagSizeAtIndex(idx) - sizeAtIndex(idx); + for (int j = idx + 1; j < maximumIndex; j++) { + if (methodsByIndex.find(j) != methodsByIndex.end()) { + break; + } else { + availableAfterThisAddress += bagSizeAtIndex(j); + } + } + + return availableAfterThisAddress; +} + +// Because some selectors are longer than 128 bytes, we have to sometimes let +// them overflow into the next 128-byte bucket. This method tells you if you +// can place a method in a bucket without colliding with an overflowing selector +// from one of the previous buckets. +bool AddressSpace::canPlaceWithoutFillingOverflowCellAtIndex(int idx) const +{ + if ((idx == 0) || (sizeAtIndex(idx) > 0)) { + return true; + } + + int j = idx; + int availableOnOrBefore = 0; + + while (j > 0 && (sizeAtIndex(j) == 0)) { + availableOnOrBefore += bagSizeAtIndex(j); + j -= 1; + } + + int sizeOfFirstNonEmptyCellBefore = sizeAtIndex(j); + return (sizeOfFirstNonEmptyCellBefore < availableOnOrBefore); +} + +bool AddressSpace::canPlaceMethodAtIndex(const Selector* method, int idx) const +{ + int existingSize = sizeAtIndex(idx); + bool canPlaceWithoutFillingOverflow = canPlaceWithoutFillingOverflowCellAtIndex(idx); + + if (!canPlaceWithoutFillingOverflow) { + return false; + } + + int available = bagSizeAtIndex(idx) - existingSize; + int methodSize = method->size(); + bool enoughRoom = available > methodSize; + + if (enoughRoom) { + return true; + } + + bool tooBigButOverflowExists = (methodSize > 64) && (available > 0) && (sizeAvailableAfterIndex(idx) > methodSize); + + return tooBigButOverflowExists; +} + +void AddressSpace::placeMethodAtIndex(Selector* method, int idx) +{ + auto [it, success] = methodsByIndex.try_emplace(idx, std::vector()); + it->second.push_back(method); + + auto [it2, success2] = sizes.try_emplace(idx, 0); + it2->second += method->size(); +} + +// At this point selected are already sorted into 128-byte buckets. +// Now fill in the low 7 bits of each address, and return a list +// of intervals [ ..... selector data....][ ...... hole ....][.... selector data...] +// so that we can stuff in selectors that don't participate in +// static IMP caches +void AddressSpace::computeLowBits(HoleMap& selectors) const { + int currentEndOffset = magicSelector.length() + 1; + + std::set & holes = selectors.holes; + holes.clear(); + + std::vector orderedIndices; + for (const auto& [index, selectors] : methodsByIndex) { + orderedIndices.push_back(index); + } + std::sort(orderedIndices.begin(), orderedIndices.end()); + for (int index : orderedIndices) { + const std::vector & selectorsAtThisIndex = methodsByIndex.at(index); + int bucketOffset = index << bagSizeShift; + if (bucketOffset > currentEndOffset) { + holes.insert(Hole { + .startAddress = currentEndOffset, + .endAddress = bucketOffset, + }); + currentEndOffset = bucketOffset; + } + for (Selector* s : selectorsAtThisIndex) { + s->offset = currentEndOffset; + currentEndOffset += s->size(); + } + } + + selectors.endAddress = currentEndOffset; +} + +int HoleMap::addStringOfSize(unsigned size) { +// static int i = 0; +// if (i++ % 1000 == 0) { +// printf("Inserted 1000 more strings, number of holes = %lu\n", holes.size()); +// } + Hole neededHole = Hole { + .startAddress = 0, + .endAddress = static_cast(size) + }; + std::set::iterator it = holes.lower_bound(neededHole); + if (it == holes.end()) { + // insert at the end + int end = endAddress; + endAddress += size; + return end; + } else { + // Remove this hole and insert a smaller one instead + + int address = it->startAddress; + Hole updatedHole = *it; + updatedHole.startAddress += size; + holes.erase(it); + + // Don't insert if the hole is empty or won't fit any selector + if (updatedHole.size() > 1) { + holes.insert(updatedHole); + } + return address; + } +} + +void HoleMap::clear() { + holes.clear(); + endAddress = 0; +} + +unsigned long HoleMap::totalHoleSize() const { + unsigned long result = 0; + for (const Hole& hole : holes) { + result += hole.size(); + } + return result; +} + +std::ostream& operator<<(std::ostream& o, const HoleMap& m) { + int size = 0; + int count = 0; + for (const Hole& h : m.holes) { + if (h.size() == size) { + count++; + } else { + o << count << " holes of size " << size << std::endl; + size = h.size(); + count = 1; + } + } + return o; +} + +std::ostream& operator<<(std::ostream& o, const AddressSpace& a) +{ + int maximumIndex = 0; + for (const auto& kvp : a.methodsByIndex) { + maximumIndex = std::max(maximumIndex, kvp.first); + } + + std::vector lengths; + std::map> sortedMethodsByAddress; + sortedMethodsByAddress.insert(a.methodsByIndex.begin(), a.methodsByIndex.end()); + + std::map sortedSizes; + sortedSizes.insert(a.sizes.begin(), a.sizes.end()); + + for (const auto& kvp : sortedSizes) { + lengths.push_back(kvp.second); + } + + for (const auto& kvp : sortedMethodsByAddress) { + o << std::setw(5) << kvp.first << ": "; + for (Selector* m : kvp.second) { + o << m->name << " "; + } + o << "\n"; + } + + o << "Max address " << maximumIndex << "\n"; + o << "Average length " << (double)std::accumulate(lengths.begin(), lengths.end(), 0) / lengths.size() << "\n"; + + return o; +} + +// Returns a constraint that is the intersection of "this" and "other", i.e. a constraint for which the allowed values +// are the intersection of the allowed values of "this" and "other" (taking into account shift and mask) +Constraint Constraint::intersecting(const Constraint& other) const +{ + if ((mask == other.mask) && (shift == other.shift)) { + // fast path + std::unordered_set intersection; + for (int allowedValue : allowedValues) { + if (other.allowedValues.find(allowedValue) != other.allowedValues.end()) { + intersection.insert(allowedValue); + } + } + + return Constraint { + .mask = mask, + .shift = shift, + .allowedValues = intersection + }; + } + + int shiftedMask = mask << shift; + int otherShift = other.shift; + int otherMask = other.mask; + int otherShiftedMask = other.mask << otherShift; + int intersectionMask = shiftedMask & otherShiftedMask; + const std::unordered_set& otherAllowedValues = other.allowedValues; + + // Always make sure we start with the left-most mask as self + if (shiftedMask < otherShiftedMask) { + return other.intersecting(*this); + } + + // If there are no real constraints on our side, just return the other + if ((mask == 0) && (allowedValues.size() == 1) && (*(allowedValues.begin()) == 0)) { + return other; + } + + // If there are no real constraints on the other side, just return our constraint + if ((otherMask == 0) && (otherAllowedValues.size() == 1) && (*(otherAllowedValues.begin()) == 0)) { + return *this; + } + + if (otherShift >= shift) { + // [self..[other]..self] + // Restrict the allowed values to make sure they have the right bits. + int shiftDifference = otherShift - shift; + std::unordered_set combinedAllowedValues; + for (int v : allowedValues) { + int val = (v >> shiftDifference) & otherMask; + if (otherAllowedValues.find(val) != otherAllowedValues.end()) { + combinedAllowedValues.insert(v); + } + } + + return Constraint { + .mask = mask, + .shift = shift, + .allowedValues = combinedAllowedValues + }; + } + + int highestBit = (int)fls(shiftedMask) - 1; + int otherHighestBit = (int)fls(otherShiftedMask) - 1; + int otherMaskLength = fls(otherMask + 1) - 1; + + if (otherShiftedMask < (1 << shift)) { + // [self]....[other] + // Start by shifting all the allowed values in self + int numberOfUnconstrainedBits = shift - otherHighestBit - 1; + int maxUnconstrained = 1 << numberOfUnconstrainedBits; + std::set includingUnrestrictedBits; + + if (numberOfUnconstrainedBits > 0) { + for (const int allowed : allowedValues) { + int shifted = allowed << numberOfUnconstrainedBits; + for (int unconstrained = 0; unconstrained < maxUnconstrained; unconstrained++) { + // Mix in unrestricted bits, then shift by [other]'s length + includingUnrestrictedBits.insert((shifted | unconstrained) << otherMaskLength); + } + }; + } else { + for (const int allowed : allowedValues) { + // Shift all the values by [other]'s length + includingUnrestrictedBits.insert(allowed << otherMaskLength); + } + } + + // Or in the values for [other] + std::unordered_set finalAllowedValues; + for (const int allowed : includingUnrestrictedBits) { + for (const int otherValue : otherAllowedValues) { + finalAllowedValues.insert(allowed | otherValue); + } + } + + return Constraint { + .mask = ((1 << (highestBit + 1)) - 1) >> otherShift, + .shift = otherShift, + .allowedValues = finalAllowedValues + }; + + } else { + // Overlap. + // [self....[other....self].....other]....... + // We need to + // * determine the set of bits allowed in the intersection + // * filter each set of values to keep only these + // * do the cross-product + + // Bits in the intersection + + int shiftDifference = shift - otherShift; + std::set selfIntersectingBits; + for (const int v : allowedValues) { + selfIntersectingBits.insert(((v << shift) & intersectionMask) >> shift); + } + std::set otherIntersectingBits; + for (const int v : otherAllowedValues) { + otherIntersectingBits.insert(((v << otherShift) & intersectionMask) >> shift); + } + + std::set intersectingBits; + std::set_intersection(selfIntersectingBits.begin(), selfIntersectingBits.end(), otherIntersectingBits.begin(), otherIntersectingBits.end(), std::inserter(intersectingBits, intersectingBits.begin())); + + std::unordered_set values; + // Swift here was constructing a list of values for self and other + // filtered on which elements had the right values for intersectionMask + // This would avoid the n^3 loop at the expense of some storage... FIXME + for (const int intersecting : intersectingBits) { + int intersectingShifted = intersecting << shift; + for (int selfAllowed : allowedValues) { + if (((selfAllowed << shift) & intersectionMask) == intersectingShifted) { + for (int otherAllowed : otherAllowedValues) { + if (((otherAllowed << otherShift) & intersectionMask) == intersectingShifted) { + values.insert((selfAllowed << shiftDifference) | otherAllowed); + } + } + } + } + } + + return Constraint { + .mask = (shiftedMask | otherShiftedMask) >> otherShift, + .shift = otherShift, + .allowedValues = values + }; + } +} + +std::ostream& operator << (std::ostream& o, const std::unordered_set & s) { + o << "{"; + for (int i : s) { + o << i << ", "; + } + o << "}"; + return o; +} + +std::ostream& operator << (std::ostream& o, const Constraint& c) { + o << "(x >> " << c.shift << " & " << c.mask << " == " << c.allowedValues; + return o; +} + +bool ConstraintSet::add(const Constraint& c) { + if (constraints.find(c) != constraints.end()) { + return false; + } + constraints.insert(c); + if (mergedConstraint.has_value()) { + mergedConstraint = mergedConstraint->intersecting(c); + } else { + mergedConstraint = c; + } + return true; +} + +void ConstraintSet::clear() { + constraints.clear(); + mergedConstraint.reset(); +} + +bool IMPCachesBuilder::isClassInteresting(const ObjCClass& theClass) const { + std::string_view classNameStr(theClass.className); + if (theClass.isMetaClass) { + return neededMetaclasses.find(classNameStr) != neededMetaclasses.end(); + } else { + return neededClasses.find(classNameStr) != neededClasses.end(); + } +} + +bool IMPCachesBuilder::isClassInterestingOrTracked(const ObjCClass& theClass) const { + std::string_view classNameStr(theClass.className); + auto& neededArray = theClass.isMetaClass ? neededMetaclasses : neededClasses; + auto& trackedArray = theClass.isMetaClass ? trackedMetaclasses : trackedClasses; + + return (neededArray.find(classNameStr) != neededArray.end()) || + (trackedArray.find(classNameStr) != trackedArray.end()); +} + +void IMPCachesBuilder::addMethod(IMPCaches::ClassData* classDataPtr, const char* methodName, const char* installName, const char* className, const char* catName, bool inlined, bool fromFlattening) { + std::string_view methodNameView(methodName); + + auto [selectorIterator, success] = selectors.map.try_emplace(methodNameView, std::make_unique()); + IMPCaches::Selector* thisSelectorData = selectorIterator->second.get(); + if (success) { + thisSelectorData->name = methodName; + } + + std::vector & methods = classDataPtr->methods; + // Check in the existing methods to see if the method already exists... + bool exists = false; + for (const ClassData::Method& m : methods) { + if (m.selector == thisSelectorData) { + // printf("Preventing insertion of duplicate %s.%s\n", classDataPtr->name, methodName); + exists = true; + break; + } + } + + if (!exists) { + ClassData::Method m { + .installName = installName, + .className = className, + .categoryName = catName, + .selector = thisSelectorData, + .wasInlined = inlined, + .fromFlattening = fromFlattening + }; + + thisSelectorData->classes.push_back(classDataPtr); + classDataPtr->methods.push_back(m); + } +} + +void IMPCachesBuilder::inlineMethodIfNeeded(IMPCaches::ClassData* classToInlineIn, const char* classToInlineFrom, const char* catToInlineFrom, const char* installNameToInlineFrom, const char* name, std::set& seenSelectors, bool isFlattening) { + std::string_view nameView(name); + + if ((nameView == ".cxx_construct") || (nameView == ".cxx_destruct")) { + // These selectors should never be inherited. + // object_cxxConstructFromClass / object_cxxDestructFromClass walk the class hierarchy and call them all. + + return; + } + + bool shouldInline = isFlattening || (selectorsToInline.find(nameView) != selectorsToInline.end()); + if (shouldInline) { + // The selector hasn't necessarily been seen at this point: eg. we don't build an IMP cache for UIView, so we haven't seen -[UIView superview] yet. + + auto [it, inserted] = selectors.map.try_emplace(nameView, std::make_unique()); + IMPCaches::Selector* thisSelectorData = it->second.get(); + + if (inserted) { + thisSelectorData->name = name; + } + + if (inserted || seenSelectors.find(thisSelectorData) == seenSelectors.end()) { + seenSelectors.insert(thisSelectorData); + const bool inlined = true; + addMethod(classToInlineIn, name, installNameToInlineFrom, classToInlineFrom, catToInlineFrom, inlined, isFlattening); + } + } +} + +// This goes through all the superclasses of the interesting classes so that +// we can track their methods for inlining. +// Since it goes through the superclasses, we also take this opportunity +// to add subclassses of duplicate classes to the duplicate classes set. +void IMPCachesBuilder::buildTrackedClasses(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma) { + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false); + ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma; + + // The class might not be in the map as we exclude classes with missing weak superclasses + auto classIt = objcClasses.find(classLocation); + if ( classIt == objcClasses.end() ) + return; + const auto& theClass = classIt->second; + ClassKey theClassKey { + .name = theClass.className, + .metaclass = theClass.isMetaClass + }; + + if (!isClassInteresting(theClass)) return; + + // Go through superclasses and add them to the tracked set. + const IMPCaches::IMPCachesBuilder::ObjCClass* currentClass = &theClass; + bool rootClass = false; + do { + ClassKey k { + .name = currentClass->className, + .metaclass = currentClass->isMetaClass + }; + + // If one of the superclasses of theClass is in the duplicate classes set, + // add theClass to the duplicate classes as well. + if (duplicateClasses.find(k) != duplicateClasses.end()) { + duplicateClasses.insert(theClassKey); + } + + if (currentClass->isMetaClass) { + trackedMetaclasses.insert(currentClass->className); + } else { + trackedClasses.insert(currentClass->className); + } + rootClass = currentClass->isRootClass; + if (!rootClass) { + // The superclass might not be in the map as we exclude classes with missing weak superclasses + auto superclassIt = objcClasses.find(currentClass->superclass); + if ( superclassIt == objcClasses.end() ) + break; + currentClass = &superclassIt->second; + } + } while (!rootClass); + }); +} + +/// Parses the method lists of all the classes in @param dylib so that we populate the methods we want in each IMP cache skeleton. +void IMPCachesBuilder::populateMethodLists(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma, int* duplicateClassCount) { + const uint32_t pointerSize = ma->pointerSize(); + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false); + + ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + + const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma; + + // The class might not be in the map as we exclude classes with missing weak superclasses + auto classIt = objcClasses.find(classLocation); + if ( classIt == objcClasses.end() ) + return; + const auto& theClass = classIt->second; + + if (!isClassInterestingOrTracked(theClass)) return; + bool interesting = isClassInteresting(theClass); + + std::string_view classNameString(theClass.className); + std::unique_ptr thisData = std::make_unique(); + IMPCaches::ClassData* thisDataPtr = thisData.get(); + thisData->name = theClass.className; + thisData->isMetaclass = isMetaClass; + thisData->shouldGenerateImpCache = interesting; + + ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + dyld3::MachOAnalyzer::PrintableStringResult printableStringResult; + const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult); + if (printableStringResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) { + return; + } + + const bool inlined = false; + const bool fromFlattening = false; + addMethod(thisDataPtr, methodName, ma->installName(), theClass.className, NULL, inlined, fromFlattening); + }); + + ClassKey key { + .name = classNameString, + .metaclass = isMetaClass + }; + assert(dylib.impCachesClassData.find(key) == dylib.impCachesClassData.end()); + + if (duplicateClasses.find(key) != duplicateClasses.end()) { + // We can't just set shouldGenerateImpCache to false ; we do it later + // when we have built the flattening hierarchies in order to drop + // any related classes as well. + + thisData->isPartOfDuplicateSet = true; + *duplicateClassCount += 1; + } + + dylib.impCachesClassData[key] = std::move(thisData); + }); +} + +/// Parses all the categories within the same image as a class so that we can add the corresponding methods to the IMP cache skeletons, too. +void IMPCachesBuilder::attachCategories(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma) { + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false); + ma->forEachObjCCategory(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + + const uint8_t* categoryLocation = (categoryVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma; + ObjCCategory previouslyFoundCategory = objcCategories.at(categoryLocation); + + __block dyld3::MachOAnalyzer::PrintableStringResult printableStringResult; + const char* catName = ma->getPrintableString(objcCategory.nameVMAddr, printableStringResult); + + if (previouslyFoundCategory.classMA != ma) { + // Cross-image category + return; + } + + // The class might not be in the map as we exclude classes with missing weak superclasses + auto classIt = objcClasses.find(previouslyFoundCategory.cls); + if ( classIt == objcClasses.end() ) + return; + const auto& theClass = classIt->second; + + auto& theMetaClass = objcClasses[theClass.metaClass]; + auto classNameString = std::string_view(theClass.className); + + if (isClassInterestingOrTracked(theClass)) { + ClassKey key { + .name = classNameString, + .metaclass = false + }; + ClassData* clsData = dylib.impCachesClassData.at(key).get(); + + ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + // The config file should specify only classes without cross-image categories, so we should have found a class here + assert(clsData != NULL); + const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult); + const bool inlined = false; + const bool fromFlattening = false; + addMethod(clsData, methodName, ma->installName(), theClass.className, catName, inlined, fromFlattening); + }); + } + if (isClassInterestingOrTracked(theMetaClass)) { + ClassKey key { + .name = classNameString, + .metaclass = true + }; + ClassData* metaclsData = dylib.impCachesClassData.at(key).get(); + + ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + assert(metaclsData != NULL); + const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult); + const bool inlined = false; + const bool fromFlattening = false; + addMethod(metaclsData, methodName, ma->installName(), theMetaClass.className, catName, inlined, fromFlattening); + }); + } + }); +} + +struct FlatteningRootLookupResult { + bool isInFlatteningHierarchy; + + const uint8_t * flatteningRootSuperclassLocation; + ClassData::ClassLocator flatteningRootSuperclass; + std::set superclassesInFlatteningHierarchy; + const char * flatteningRootName; +}; + +static FlatteningRootLookupResult findFlatteningRoot(const uint8_t* classLocation, + const std::unordered_map & objcClasses, + const std::unordered_set & classHierarchiesToFlatten, + const std::unordered_set & metaclassHierarchiesToFlatten, + bool storeSuperclasses) { + FlatteningRootLookupResult result; + const uint8_t* superclassLocation = classLocation; + __block bool rootClass = false; + bool success = false; + + while (!rootClass) { + const auto it = objcClasses.find(superclassLocation); + if (it == objcClasses.end()) { + break; + } + const IMPCachesBuilder::ObjCClass& iteratedClass = it->second; + rootClass = iteratedClass.isRootClass; + superclassLocation = iteratedClass.superclass; + + if (storeSuperclasses) { + result.superclassesInFlatteningHierarchy.insert(iteratedClass.className); + } + + bool metaClassBeingFlattened = iteratedClass.isMetaClass && metaclassHierarchiesToFlatten.find(iteratedClass.className) != metaclassHierarchiesToFlatten.end(); + bool classBeingFlattened = !iteratedClass.isMetaClass && classHierarchiesToFlatten.find(iteratedClass.className) != classHierarchiesToFlatten.end(); + + if (metaClassBeingFlattened || classBeingFlattened) { + result.flatteningRootName = iteratedClass.className; + result.flatteningRootSuperclassLocation = iteratedClass.superclass; + result.flatteningRootSuperclass = iteratedClass.superclassLocator(); + success = true; + break; + } + } + + result.isInFlatteningHierarchy = success; + + return result; +} +/// Inline selectors from parent classes into child classes for performance +void IMPCachesBuilder::inlineSelectors(CacheBuilder::DylibInfo& dylib, std::unordered_map & dylibsByInstallName, const dyld3::MachOAnalyzer* ma) { + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false); + ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + + const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma; + + // The class might not be in the map as we exclude classes with missing weak superclasses + auto classIt = objcClasses.find(classLocation); + if ( classIt == objcClasses.end() ) + return; + const auto& theClass = classIt->second; + + if (!isClassInteresting(theClass)) { + return; + } + + std::string_view classNameString(theClass.className); + ClassKey key { + .name = classNameString, + .metaclass = isMetaClass + }; + + IMPCaches::ClassData* thisDataPtr = dylib.impCachesClassData.at(key).get(); + assert(thisDataPtr != NULL); + + __block std::set seenSelectors; + for (const ClassData::Method& method : thisDataPtr->methods) { + seenSelectors.insert(method.selector); + }; + + // Check the superclass hierarchy to see if we're in a flattened hierarchy + // (meaning we should inline all of the selectors up to the flattening root) + FlatteningRootLookupResult flatteningInfo = findFlatteningRoot(classLocation, objcClasses, classHierarchiesToFlatten, metaclassHierarchiesToFlatten, false); + + if (flatteningInfo.isInFlatteningHierarchy) { + // try again and record superclasses this time + // (maybe premature optimization, but given the small number of classes where flattening + // is actually happening I did not want to gather this set every time) + + flatteningInfo = findFlatteningRoot(classLocation, objcClasses, classHierarchiesToFlatten, metaclassHierarchiesToFlatten, true); + + assert(flatteningInfo.isInFlatteningHierarchy); + + thisDataPtr->flatteningRootSuperclass = flatteningInfo.flatteningRootSuperclass; + thisDataPtr->flatteningRootName = flatteningInfo.flatteningRootName; + thisDataPtr->flattenedSuperclasses = flatteningInfo.superclassesInFlatteningHierarchy; + } + + // Iterate again to actually flatten/inline the selectors + const uint8_t *superclassLocation = classLocation; + __block const dyld3::MachOAnalyzer* currentMA = ma; + bool isRootClass = false; + bool isFlattening = flatteningInfo.isInFlatteningHierarchy; + + while (!isRootClass) { + const auto it = objcClasses.find(superclassLocation); + if (it == objcClasses.end()) { + break; + } + ObjCClass& iteratedClass = it->second; + isRootClass = iteratedClass.isRootClass; + + CacheBuilder::DylibInfo* classDylib = dylibsByInstallName.at(currentMA->installName()); + ClassKey keyForIteratedClass { + .name = std::string_view(iteratedClass.className), + .metaclass = iteratedClass.isMetaClass + }; + auto classDataIt = classDylib->impCachesClassData.find(keyForIteratedClass); + + // We should have added this class to our data in populateMethodLists() + assert(classDataIt != classDylib->impCachesClassData.end()); + + IMPCaches::ClassData* classData = classDataIt->second.get(); + + for (const ClassData::Method& m : classData->methods) { + // If the method found in the superclass was inlined from a further superclass, we'll inline it + // when we reach that class (otherwise the install name / class name the method is coming from will be wrong) + if (!m.wasInlined) { + inlineMethodIfNeeded(thisDataPtr, m.className, m.categoryName, currentMA->installName(), m.selector->name, seenSelectors, isFlattening); + } + } + + currentMA = iteratedClass.superclassMA; + assert(isRootClass || (currentMA != nullptr)); + superclassLocation = iteratedClass.superclass; + + if (isFlattening && (iteratedClass.superclass == flatteningInfo.flatteningRootSuperclassLocation)) { + // we reached the flattening root, turn flattening off + isFlattening = false; + } + } + }); +} + +/// Parses all the source dylibs to fill the IMP cache skeletons with all the methods we want to have there. +bool IMPCachesBuilder::parseDylibs(Diagnostics& diag) { + std::unordered_map dylibsByInstallName; + + for (CacheBuilder::DylibInfo& d : dylibs) { + const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile; + const dyld3::MachOAnalyzer* ma = mapped.mh; + + dylibsByInstallName.insert(std::make_pair(std::string_view(ma->installName()), &d)); + + // Build the set of tracked classes (interesting classes + their superclasses) + buildTrackedClasses(d, ma); + } + + int totalDuplicateClassCount = 0; + + for (CacheBuilder::DylibInfo& d : dylibs) { + const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile; + const dyld3::MachOAnalyzer* ma = mapped.mh; + + int duplicateClassCount = 0; + // First, go through all classes and populate their method lists. + populateMethodLists(d, ma, &duplicateClassCount); + totalDuplicateClassCount += duplicateClassCount; + + // Now go through all categories and attach them as well + attachCategories(d, ma); + } + + _diagnostics.verbose("[IMP caches] Not generating caches for %d duplicate classes or children of duplicate classes\n", totalDuplicateClassCount); + + // Ensure that all the selectors will fit on 16 MB as that's the constant + // embedded in the placement algorithm + uint32_t totalSize = 0; + for (const auto& [k,v] : selectors.map) { + totalSize += v->size(); + } + if (totalSize >= (1 << 24)) { + diag.warning("Dropping all IMP caches ; too many selectors\n"); + return false; + } + + for (CacheBuilder::DylibInfo& d : dylibs) { + const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile; + const dyld3::MachOAnalyzer* ma = mapped.mh; + + // Now that all categories are attached, handle any selector inheritance if needed + // (Do this after category attachment so that inlined selectors don't override categories) + + inlineSelectors(d, dylibsByInstallName, ma); + } + + removeUninterestingClasses(); + + unsigned count = 0; + for (CacheBuilder::DylibInfo& d : dylibs) { + count += d.impCachesClassData.size(); + } + + constexpr bool logAllSelectors = false; + + diag.verbose("[IMP Caches] parsed %u classes\n", count); + for (CacheBuilder::DylibInfo& d : dylibs) { + for (auto it = d.impCachesClassData.begin() ; it != d.impCachesClassData.end() ; it++) { + auto& c = it->second; + c->didFinishAddingMethods(); + if (logAllSelectors) { + printf("%s\n", c->description().c_str()); + std::vector sortedMethods(c->methods.begin(), c->methods.end()); + std::sort(sortedMethods.begin(), sortedMethods.end(), [](const ClassData::Method& a, const ClassData::Method& b) { + return strcmp(a.selector->name, b.selector->name) < 0; + }); + for (const ClassData::Method& m : sortedMethods) { + const Selector* s = m.selector; + printf(" %s", s->name); + if (m.categoryName != nullptr) { + printf(" (from %s::%s+%s)\n", m.installName, m.className, m.categoryName); + } else if (m.className != c->name) { + printf(" (from %s::%s)\n", m.installName, m.className); + } else { + printf("\n"); + } + } + } + } + } + + for (auto& s : selectorsToInline) { + auto selectorsIt = selectors.map.find(s); + if (selectorsIt == selectors.map.end()) { + diag.warning("Requested selector to inline not found in any classes: %s\n", s.data()); + continue; + } + inlinedSelectors.push_back(selectors.map.at(s).get()); + } + std::sort(inlinedSelectors.begin(), inlinedSelectors.end(), [](const Selector* a, const Selector *b) { + return a->offset < b->offset; + }); + + return true; +} + +IMPCachesBuilder::TargetClassFindingResult IMPCachesBuilder::findTargetClass(const dyld3::MachOAnalyzer* ma, uint64_t targetClassVMAddr, uint64_t targetClassPointerVMAddr,const char* logContext, const std::unordered_map & bindLocations, std::vector & bindTargets, std::unordered_map &dylibMap) { + constexpr bool log = false; + uint64_t loadAddress = ma->preferredLoadAddress(); + + uint64_t superclassRuntimeOffset = targetClassPointerVMAddr - loadAddress; + auto bindIt = bindLocations.find(superclassRuntimeOffset); + + if ( bindIt == bindLocations.end() ) { + if (targetClassVMAddr != 0) { + // A rebase, ie, a fixup to a class in this dylib + if ( log ) + printf("%s: %s -> this dylib\n", ma->installName(), logContext); + superclassRuntimeOffset = targetClassVMAddr - loadAddress; + return TargetClassFindingResult { + .success = true, + .foundInDylib = ma, + .location = (const uint8_t*)ma + superclassRuntimeOffset + }; + } else { + // A bind, ie, a fixup to a class in another dylib + return TargetClassFindingResult { .success = false }; + } + } + + const BindTarget& bindTarget = bindTargets[bindIt->second]; + if ( log ) + printf("%s: %s -> %s: %s\n", ma->installName(), logContext, + bindTarget.targetDylib->installName(), bindTarget.symbolName.c_str()); + + dyld3::MachOLoaded::DependentToMachOLoaded finder = ^(const dyld3::MachOLoaded* mh, uint32_t depIndex) { + auto dylibIt = dylibMap.find(mh->installName()); + if ( dylibIt == dylibMap.end() ) { + // Missing weak dylib? + return (const dyld3::MachOLoaded*)nullptr; + } + + if ( depIndex >= dylibIt->second.dependentLibraries.size() ) { + // Out of bounds dependent + assert(false); + } + + auto depIt = dylibMap.find(dylibIt->second.dependentLibraries[depIndex]); + if ( depIt == dylibMap.end() ) { + // Missing weak dylib? + return (const dyld3::MachOLoaded*)nullptr; + } + return (const dyld3::MachOLoaded*)depIt->second.ma; + }; + + if (bindTarget.targetDylib != nullptr) { + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + bool found = bindTarget.targetDylib->findExportedSymbol(_diagnostics, bindTarget.symbolName.c_str(), + false, foundInfo, finder); + if ( !found ) { + if ( !bindTarget.isWeakImport ) { + _diagnostics.verbose("Could not find non-weak target class: '%s'\n", bindTarget.symbolName.c_str()); + } + // We couldn't find the target class. This is an error if the symbol is not a weak-import, but + // we'll let symbol resolution work that out later if we really have a missing symbol which matters + // For IMP caches, we'll just ignore this class. + return TargetClassFindingResult { .success = false }; + } + assert(found); + assert(foundInfo.kind == dyld3::MachOAnalyzer::FoundSymbol::Kind::headerOffset); + if (foundInfo.foundInDylib == nullptr) { + if ( log ) + printf("null foundInDylib\n"); + } + return TargetClassFindingResult { + .success = true, + .foundInDylib = foundInfo.foundInDylib, + .location = (uint8_t*)foundInfo.foundInDylib + foundInfo.value, + }; + } else { + if ( log ) + printf("No target dylib found for %s\n", logContext); + return TargetClassFindingResult { .success = false }; + } +} + +void IMPCachesBuilder::buildClassesMap(Diagnostics& diag) { + const uint32_t pointerSize = 8; + + __block std::unordered_map dylibMap; + + for (CacheBuilder::DylibInfo& dylib : dylibs) { + const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh; + __block std::vector dependentLibraries; + ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, + bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + dependentLibraries.push_back(loadPath); + }); + dylibMap[ma->installName()] = { ma, dependentLibraries }; + } + + const bool log = false; + + __block ClassSet seenClasses; + + for (CacheBuilder::DylibInfo& dylib : dylibs) { + const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh; + __block std::vector dependentLibraries; + ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, + bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + auto it = dylibMap.find(loadPath); + if (it == dylibMap.end()) { + char resolvedSymlinkPath[PATH_MAX]; + + // The dylib may link on a dylib which has moved and has a symlink to a new path. + if (_fileSystem.getRealPath(loadPath, resolvedSymlinkPath)) { + it = dylibMap.find(resolvedSymlinkPath); + } + } + if (it == dylibMap.end()) { + assert(isWeak); + dependentLibraries.push_back(nullptr); + } else { + dependentLibraries.push_back(it->second.ma); + } + }); + __block std::vector bindTargets; + + // Map from vmOffsets in this binary to which bind target they point to + __block std::unordered_map bindLocations; + if ( ma->hasChainedFixups() ) { + // arm64e binaries + ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool &stop) { + if (libOrdinal == BIND_SPECIAL_DYLIB_SELF) { + bindTargets.push_back({ symbolName, ma, weakImport }); + } else if ( libOrdinal < 0 ) { + // A special ordinal such as weak. Just put in a placeholder for now + bindTargets.push_back({ symbolName, nullptr, weakImport }); + } else { + assert(libOrdinal <= (int)dependentLibraries.size()); + const dyld3::MachOAnalyzer *target = dependentLibraries[libOrdinal-1]; + assert(weakImport || (target != nullptr)); + bindTargets.push_back({ symbolName, target, weakImport }); + } + }); + ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* startsInfo) { + ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, + const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) { + uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + uint32_t bindOrdinal; + int64_t addend; + if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) { + if ( bindOrdinal < bindTargets.size() ) { + bindLocations[fixupOffset] = bindOrdinal; + } + else { + diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.size()); + fixupsStop = true; + } + } + }); + }); + } else { + // Non-arm64e (for now...) + ma->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, + bool weakImport, bool lazyBind, uint64_t addend, bool &stop) { + if ( log ) + printf("0x%llx %s: %s\n", runtimeOffset, ma->installName(), symbolName); + if ( libOrdinal < 0 ) { + // A special ordinal such as weak. Just put in a placeholder for now + bindTargets.push_back({ symbolName, nullptr, weakImport }); + } else if (libOrdinal == BIND_SPECIAL_DYLIB_SELF) { + bindTargets.push_back({ symbolName, ma, weakImport }); + } else { + assert(libOrdinal <= (int)dependentLibraries.size()); + bindTargets.push_back({ symbolName, dependentLibraries[libOrdinal - 1], weakImport }); + } + bindLocations[runtimeOffset] = bindTargets.size() - 1; + }, ^(const char* symbolName) { + // We don't need this as its only for weak def coalescing + }); + } + const uint64_t loadAddress = ma->preferredLoadAddress(); + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false); + ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& maDiag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + uint64_t classNameVMAddr = objcClass.nameVMAddr(pointerSize); + dyld3::MachOAnalyzer::PrintableStringResult classNameResult = dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection; + const char* className = ma->getPrintableString(classNameVMAddr, classNameResult); + assert(classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint); + if ( log ) + printf("%s: %s\n", ma->installName(), className); + + const uint32_t RO_ROOT = (1<<1); + bool isRootClass = (objcClass.flags(pointerSize) & RO_ROOT) != 0; + auto result = findTargetClass(ma, objcClass.superclassVMAddr, classSuperclassVMAddr, className, bindLocations, bindTargets, dylibMap); + + if (result.success || isRootClass) { + uint64_t classRuntimeOffset = classVMAddr - loadAddress; + const uint8_t* classLocation = (const uint8_t*)ma + classRuntimeOffset; + objcClasses[classLocation] = ObjCClass { + .superclassMA = (const dyld3::MachOAnalyzer*) result.foundInDylib, + .metaClass = isMetaClass ? nullptr : ((const uint8_t*)ma + objcClass.isaVMAddr - loadAddress), + .superclass = result.location, + .methodListVMaddr = objcClass.baseMethodsVMAddr(pointerSize), + .className = className, + .isRootClass = isRootClass, + .isMetaClass = isMetaClass, + }; + + ClassKey k { + .name = className, + .metaclass = isMetaClass + }; + + auto [it, success] = seenClasses.insert(k); + if (!success) { + duplicateClasses.insert(k); + } + } + }); + + ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& maDiag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + dyld3::MachOAnalyzer::PrintableStringResult catNameResult = dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection; + const char *catName = ma->getPrintableString(objcCategory.nameVMAddr, catNameResult); + + uint64_t clsVMAddr = categoryVMAddr + pointerSize; + TargetClassFindingResult result = findTargetClass(ma, objcCategory.clsVMAddr, clsVMAddr, catName, bindLocations, bindTargets, dylibMap); + uint64_t catRuntimeOffset = categoryVMAddr - loadAddress; + const uint8_t *catLocation = (const uint8_t*)ma + catRuntimeOffset; + if (result.success) { + objcCategories[catLocation] = ObjCCategory { + .classMA = (const dyld3::MachOAnalyzer*)result.foundInDylib, + .cls = result.location + }; + } else { + // This happens for categories on weak classes that may be missing. + objcCategories[catLocation] = ObjCCategory { + .classMA = (const dyld3::MachOAnalyzer*)nullptr, + .cls = nullptr + }; + } + }); + } + + // Print the class hierarchy just to see that we found everything + if (log) { + for (const auto& [location, theClass] : objcClasses) { + printf("%p %c%s", location, theClass.isMetaClass ? '+' : '-', theClass.className); + bool isRoot = theClass.isRootClass; + const uint8_t* superclass = theClass.superclass; + while ( !isRoot ) { + auto it = objcClasses.find(superclass); + // assert(it != objcClasses.end()); + if (it == objcClasses.end()) { + printf(": missing"); + break; + } + printf(" : %c%s", it->second.isMetaClass ? '+' : '-', it->second.className); + isRoot = it->second.isRootClass; + superclass = it->second.superclass; + } + printf("\n"); + } + } +} + +const std::string * IMPCachesBuilder::nameAndIsMetaclassPairFromNode(const dyld3::json::Node & node, bool* metaclass) { + const dyld3::json::Node& metaclassNode = dyld3::json::getRequiredValue(_diagnostics, node, "metaclass"); + if (_diagnostics.hasError()) return nullptr; + + if (metaclass != nullptr) *metaclass = dyld3::json::parseRequiredInt(_diagnostics, metaclassNode) != 0; + const dyld3::json::Node& nameNode = dyld3::json::getRequiredValue(_diagnostics, node, "name"); + if (_diagnostics.hasError()) return nullptr; + + return &(dyld3::json::parseRequiredString(_diagnostics, nameNode)); +} + +IMPCachesBuilder::IMPCachesBuilder(std::vector& allDylibs, const dyld3::json::Node& optimizerConfiguration, Diagnostics& diag, TimeRecorder& timeRecorder, const dyld3::closure::FileSystem& fileSystem) : dylibs(allDylibs), _diagnostics(diag), _timeRecorder(timeRecorder), _fileSystem(fileSystem) { + const dyld3::json::Node * version = dyld3::json::getOptionalValue(diag, optimizerConfiguration, "version"); + int64_t versionInt = (version != NULL) ? dyld3::json::parseRequiredInt(diag, *version) : 1; + if (versionInt == 2) { + // v2 has a single neededClasses array, with a key to know if it's a metaclass + // or class. This lets us order them by importance so that we handle the important + // cases first in the algorithm, while it's still easy to place things (as we process + // more classes, constraints build up and we risk dropping difficult classes) + + const dyld3::json::Node& classes = dyld3::json::getRequiredValue(diag, optimizerConfiguration, "neededClasses"); + if (diag.hasError()) return; + + int i = 0; + for (const dyld3::json::Node& n : classes.array) { + bool metaclass = false; + const std::string *name = nameAndIsMetaclassPairFromNode(n, &metaclass); + if (name != nullptr) { + if (metaclass) { + neededMetaclasses[*name] = i++; + } else { + neededClasses[*name] = i++; + } + } else { + // nameAndIsMetaclassPairFromNode already logs an error in this case, + // so nothing to do here + } + } + } else { + auto metaclasses = optimizerConfiguration.map.find("neededMetaclasses"); + int i = 0; + + if (metaclasses != optimizerConfiguration.map.cend()) { + for (const dyld3::json::Node& n : metaclasses->second.array) { + neededMetaclasses[n.value] = i++; + } + } + + auto classes = optimizerConfiguration.map.find("neededClasses"); + + if (classes != optimizerConfiguration.map.cend()) { + for (const dyld3::json::Node& n : classes->second.array) { + neededClasses[n.value] = i++; + } + } + } + + auto sels = optimizerConfiguration.map.find("selectorsToInline"); + if (sels != optimizerConfiguration.map.cend()) { + for (const dyld3::json::Node& n : sels->second.array) { + selectorsToInline.insert(n.value); + } + } + + const dyld3::json::Node* classHierarchiesToFlattenNode = dyld3::json::getOptionalValue(diag, optimizerConfiguration, "flatteningRoots"); + if (classHierarchiesToFlattenNode != nullptr) { + for (const dyld3::json::Node& n : classHierarchiesToFlattenNode->array) { + bool metaclass = false; + const std::string *name = nameAndIsMetaclassPairFromNode(n, &metaclass); + if (metaclass) { + metaclassHierarchiesToFlatten.insert(*name); + } else { + classHierarchiesToFlatten.insert(*name); + } + } + } else { + // For old files, we assume we should flatten OS_object, this was implied + // before we decided to extend this set. + + metaclassHierarchiesToFlatten.insert("OS_object"); + classHierarchiesToFlatten.insert("OS_object"); + } +} + +struct BacktrackingState { + // Index into the next array that we are currently trying + int currentAttemptIndex; + + // Possible placement attempts for this class + std::vector attempts; + + // What we had to modify to attempt the current placement. This needs + // to be reversed if we backtrack. + std::optional result; + + // We also need to store the state of the random number generator, + // because when reverting to a snapshot we need to apply exactly the same + // steps as last time. + std::minstd_rand randomNumberGenerator; + + bool operator== (const BacktrackingState & other) { + // Don't test attempts, they will be the same as long as the class index is + // the same, and we never compare states for different indices + return (currentAttemptIndex == other.currentAttemptIndex) && (randomNumberGenerator == other.randomNumberGenerator); + } + + bool operator!= (const BacktrackingState & other) { + return !(*this == other); + } +}; + +static void backtrack(std::vector& backtrackingStack, int& numberOfDroppedClasses, std::vector& allClasses) { + int i = (int)backtrackingStack.size() - 1; + assert(i>0); + BacktrackingState & last = backtrackingStack.back(); + if (!last.result) { + // backtracking over a skipped class + numberOfDroppedClasses--; + } + allClasses[i]->backtrack(*(last.result)); + backtrackingStack.pop_back(); +}; + +template +static void forEachClassInFlatteningHierarchy(const IMPCaches::ClassData *parentClass, const std::vector& allClasses, const Func &callback) { + if (!parentClass->flatteningRootSuperclass.has_value()) { + return; + } + for (IMPCaches::ClassData * c : allClasses) { + // If c has parentClass in its flattening hierarchy + if ((c != parentClass) + && c->flatteningRootSuperclass.has_value() + && (*(c->flatteningRootSuperclass) == *(parentClass->flatteningRootSuperclass)) + && (c->flatteningRootName == parentClass->flatteningRootName) + && (c->flattenedSuperclasses.find(parentClass->name) != c->flattenedSuperclasses.end())) { + callback(c); + } + } +} + +static void dropClass(Diagnostics& diagnostics, unsigned long& currentClassIndex, int& numberOfDroppedClasses, std::vector& backtrackingStack, std::minstd_rand& randomNumberGenerator, std::vector& allClasses, const char* reason) { + IMPCaches::ClassData* droppedClass = allClasses[currentClassIndex]; + + diagnostics.verbose("%lu: dropping class %s (%s) because %s\n", currentClassIndex, droppedClass->name, droppedClass->isMetaclass ? "metaclass" : "class", reason); + droppedClass->shouldGenerateImpCache = false; + + // If we are inside a flattened hierarchy, we need to also drop any classes inheriting from us, + // as objc relies on all classes inside a flattened hierarchy having constant caches to do invalidation + // properly. + forEachClassInFlatteningHierarchy(droppedClass, allClasses, [&numberOfDroppedClasses, &diagnostics, currentClassIndex](IMPCaches::ClassData *c){ + // Drop it as well. + // We could undrop them if we undrop droppedClass while backtracking or restoring + // a snapshot, but it's not worth the effort. + + if (c->shouldGenerateImpCache) { + numberOfDroppedClasses++; + c->shouldGenerateImpCache = false; + c->droppedBecauseFlatteningSuperclassWasDropped = true; + diagnostics.verbose("%lu: also dropping %s (%s) in the same flattening hierarchy\n", currentClassIndex, c->name, c->isMetaclass ? "metaclass" : "class"); + } + }); + + currentClassIndex++; + numberOfDroppedClasses++; + + BacktrackingState state = { + .currentAttemptIndex = 0, + .randomNumberGenerator = randomNumberGenerator + }; + + backtrackingStack.push_back(state); +}; + +static void resetToSnapshot(std::vector& backtrackingStack, std::vector& bestSolutionSnapshot, std::vector& allClasses, int& numberOfDroppedClasses) { + + // First, backtrack if needed until we reach the first different step. + int firstDifferentStep = -1; + for (int i = 0 ; i < backtrackingStack.size() && i < bestSolutionSnapshot.size() ; i++) { + if (backtrackingStack[i] != bestSolutionSnapshot[i]) { + firstDifferentStep = i; + break; + } + } + + if (firstDifferentStep == -1) { + firstDifferentStep = MIN((int)backtrackingStack.size(), (int)bestSolutionSnapshot.size()); + } + + while (backtrackingStack.size() > firstDifferentStep) { + backtrack(backtrackingStack, numberOfDroppedClasses, allClasses); + } + + // Then apply the steps needed to get to the snapshot. + if (firstDifferentStep < bestSolutionSnapshot.size()) { + for (int i = (int)backtrackingStack.size() ; i < bestSolutionSnapshot.size() ; i++) { + BacktrackingState & state = bestSolutionSnapshot[i]; + + // Make a copy in order not to mutate it should we need to go back... + std::minstd_rand stateRandomNumberGenerator = state.randomNumberGenerator; + if (state.result) { + assert(state.currentAttemptIndex < state.attempts.size()); + typename IMPCaches::ClassData::PlacementAttempt::Result result = allClasses[i]->applyAttempt(state.attempts[state.currentAttemptIndex], stateRandomNumberGenerator); + assert(result.success); + + if (!allClasses[i]->droppedBecauseFlatteningSuperclassWasDropped) { + // shouldGenerateImpCache might have been flipped to false during backtracking + // we're restoring to a snapshot where we did place this class, so restore + // the success bit... + // ... unless we had decided to drop it because other classes were dropped + // (in that case give up and don't attempt to generate a cache for it, + // but still apply the attempt above in order to set the right constraints + // on each selector, which is necessary for snapshot reproducibility) + + allClasses[i]->shouldGenerateImpCache = true; + } + } else { + numberOfDroppedClasses++; + } + + backtrackingStack.push_back(state); + } + } +} + +/// Finds a shift and mask for each class, and start assigning the bits of the selector addresses +int IMPCachesBuilder::findShiftsAndMasks() { + // Always seed the random number generator with 0 to get reproducibility. + // Note: in overflow scenarios, findShiftsAndMasks can be called more than once, + // so make sure to always use the same value when we enter this method. + std::minstd_rand randomNumberGenerator(0); + + // This is a backtracking algorithm, so we need a stack to store our state + // (It goes too deep to do it recursively) + std::vector backtrackingStack; + + // Index of the class we're currently looking at. + unsigned long currentClassIndex = 0; + + // This lets us backtrack by more than one step, going back eg. 4 classes at a time. + // Yes, this means we're not exploring the full solution space, but it's OK because + // there are many solutions out there and we prefer dropping a few classes here and + // there rather than take hours to find the perfect solution. + unsigned long backtrackingLength = 1; + + // Indices of the attempt we had chosen for each class last time we reached the maximum + // number of classes placed so far. + std::vector bestSolutionSnapshot; + +#if 0 + // Debugging facilities where we store the full state corresponding + // to bestSolutionSnapshot to make sure restoring snapshots works. + std::vector bestSolutionSnapshotClasses; + std::unordered_map bestSolutionSnapshotSelectors; +#endif + + // Number of times we have backtracked. When this becomes too high, we go back to the + // previous snapshot and drop the faulty class. + unsigned long backtrackingAttempts = 0; + + // Go through all the classes and find a shift and mask for each, + // backtracking if needed. + std::vector allClasses; + fillAllClasses(allClasses); + + int numberOfDroppedClasses = 0; + + while (currentClassIndex < allClasses.size()) { + /* for (int i = 0 ; i < backtrackingStack.size() ; i++) { + assert(backtrackingStack[i].attempts.size() > 0 || !allClasses[i]->shouldGenerateImpCache); + } */ + + assert(// Either we are adding a new state... + (currentClassIndex == backtrackingStack.size()) + // Or we are backtracking and building on the last state recorded + || (currentClassIndex == (backtrackingStack.size() - 1))); + + IMPCaches::ClassData* c = allClasses[currentClassIndex]; + + if (!c->shouldGenerateImpCache) { + // We have decided to drop this one before, so don't waste time. + dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "we have dropped it before"); + continue; + } + + if (c->isPartOfDuplicateSet) { + dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "it is part of a duplicate set"); + continue; + } + + if (currentClassIndex >= backtrackingStack.size()) { + // We're at the top of the stack. Make a fresh state. + + BacktrackingState state; + state.attempts = c->attempts(); + state.currentAttemptIndex = 0; + state.randomNumberGenerator = randomNumberGenerator; + backtrackingStack.push_back(state); + } else { + // We are backtracking ; don't retry the attempt we tried before, use the next one. + backtrackingStack[currentClassIndex].currentAttemptIndex++; + + // Note that we do not reset randomNumberGenerator to state.randomNumberGenerator + // here, because when backtracking we want to explore a different set of + // possibilities, so let's try other placements. + } + + assert(backtrackingStack.size() == currentClassIndex + 1); + + BacktrackingState& state = backtrackingStack[currentClassIndex]; + + bool placed = false; + + // Go through all the possible placement attempts for this class. + // If one succeeds, place the next class, and if needed we'll backtrack and try the next attempt, etc. + // This is basically an iterative backtracking because + // we don't want the stack to get too deep. + std::vector & attempts = state.attempts; + for (int operationIndex = state.currentAttemptIndex ; operationIndex < (int)attempts.size() ; operationIndex++) { + // Save the state of the random number generator so that we can save its + // state before applying the attempt in the backtracking stack if needed. + std::minstd_rand maybeSuccessfulRNG = randomNumberGenerator; + typename IMPCaches::ClassData::PlacementAttempt::Result result = c->applyAttempt(attempts[operationIndex], randomNumberGenerator); + if (result.success) { + if (currentClassIndex % 1000 == 0) { + _diagnostics.verbose("[IMP Caches] Placed %lu / %lu classes\n", currentClassIndex, allClasses.size()); + } + + //fprintf(stderr, "%lu / %lu: placed %s with operation %d/%lu (%s)\n", currentClassIndex, allClasses.size(), c->description().c_str(), operationIndex, attempts.size(), attempts[operationIndex].description().c_str()); + placed = true; + state.result = result; + state.currentAttemptIndex = operationIndex; + state.randomNumberGenerator = maybeSuccessfulRNG; + break; + } + } + + if (placed) { + currentClassIndex += 1; + } else { + // Remove the current state, which has just failed and does not matter. + // (It was never applied). + backtrackingStack.pop_back(); + + backtrackingAttempts++; + if (backtrackingAttempts > 10) { + // Reset to the best snapshot and drop the next class + + resetToSnapshot(backtrackingStack, bestSolutionSnapshot, allClasses, numberOfDroppedClasses); + +#if 0 +// Expensive book-keeping to make sure that resetting to the snapshot worked. + for (const auto & [k,v] : selectors.map) { + const IMPCaches::Selector & theoretical = bestSolutionSnapshotSelectors[k]; + const IMPCaches::Selector & actual = *v; + + if (theoretical != actual) { + fprintf(stderr, "Failed to restore snapshot of %lu classes; method %s differs, (%x, %x) vs (%x, %x)\n", bestSolutionSnapshot.size(), k.data(), theoretical.inProgressBucketIndex, theoretical.fixedBitsMask, actual.inProgressBucketIndex, actual.fixedBitsMask); + assert(theoretical == actual); + } + } +#endif + + _diagnostics.verbose("*** SNAPSHOT: successfully reset to snapshot of size %lu\n", bestSolutionSnapshot.size()); + + currentClassIndex = backtrackingStack.size(); + dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "it's too difficult to place"); + + // FIXME: we should consider resetting backtrackingLength to the value it had when we snapshotted here (the risk makes this not worth trying at this point in the release). + + backtrackingAttempts = 0; + continue; + } else { + if (currentClassIndex > bestSolutionSnapshot.size()) { + _diagnostics.verbose("*** SNAPSHOT *** %lu / %lu (%s)\n", currentClassIndex, allClasses.size(), c->description().c_str()); + bestSolutionSnapshot = backtrackingStack; + +#if 0 + // Make a full copy of the state so that we can debug resetting to snapshots + bestSolutionSnapshotClasses.clear(); + bestSolutionSnapshotSelectors.clear(); + for (const auto & [k,v] : selectors.map) { + bestSolutionSnapshotSelectors[k] = *v; + } +#endif + } + + _diagnostics.verbose("%lu / %lu (%s): backtracking\n", currentClassIndex, allClasses.size(), c->description().c_str()); + assert(currentClassIndex != 0); // Backtracked all the way to the beginning, no solution + + for (unsigned long j = 0 ; j < backtrackingLength ; j++) { + backtrack(backtrackingStack, numberOfDroppedClasses, allClasses); + currentClassIndex--; + } + + backtrackingLength = std::max(1ul,std::min(std::min(currentClassIndex, backtrackingLength * 2), 1024ul)); + } + } + } + + if (numberOfDroppedClasses > 0) { + _diagnostics.verbose("Dropped %d classes that were too difficult to place\n", numberOfDroppedClasses); + } + + return numberOfDroppedClasses; +} + +void IMPCachesBuilder::fillAllClasses(std::vector & allClasses) { + for (const CacheBuilder::DylibInfo & d : dylibs) { + typedef typename decltype(d.impCachesClassData)::value_type classpair; + + for (const auto& [key, thisClassData]: d.impCachesClassData) { + if (thisClassData->methods.size() > 0 && thisClassData->shouldGenerateImpCache) { + allClasses.push_back(thisClassData.get()); + } + } + } + + + // Only include the classes for which there is actual work to do, + // otherwise we have classes with only 1 choice which makes our + // partial backtracking more difficult. + + std::sort(allClasses.begin(), allClasses.end(), [this](IMPCaches::ClassData* a, IMPCaches::ClassData* b) { + int indexA = a->isMetaclass ? neededMetaclasses[a->name] : neededClasses[a->name]; + int indexB = b->isMetaclass ? neededMetaclasses[b->name] : neededClasses[b->name]; + + return (indexA < indexB); + }); +} + +void IMPCachesBuilder::removeUninterestingClasses() { + // Remove any empty classes and classes for which we don't generate IMP caches now that we've inlined all selectors + // (These classes were just used for inlining purposes) + + for (CacheBuilder::DylibInfo& d : dylibs) { + for (auto it = d.impCachesClassData.begin() ; it != d.impCachesClassData.end() ; /* no increment here */) { + auto& c = it->second; + if (((c->methods.size() == 0) && !(c->flatteningRootSuperclass.has_value())) + || !c->shouldGenerateImpCache) { + // Remove this useless class: delete it from the selectors, and from the master class map + // Note that it is not useless if it is in a flattening hierarchy: all classes in a flattening + // hierarchy must have preopt caches so that objc correctly invalidates the caches on children + // when you attach a category to one of the classes in a flattening hierarchy + + for (auto& m : c->methods) { + auto& classes = m.selector->classes; + classes.erase(std::remove(classes.begin(), classes.end(), c.get()), classes.end()); + } + + it = d.impCachesClassData.erase(it); + } else { + it++; + } + } + } + + + // Now remove from the selector map any selectors that are not used by any classes + + addressSpace.removeUninterestingSelectors(); + for (auto it = selectors.map.begin() ; it != selectors.map.end() ; /* no increment */ ) { + Selector & s = *(it->second); + + if ((s.classes.size() == 0) && (it->first != magicSelector)) { + it = selectors.map.erase(it); + } else { + it++; + } + } +} + +void IMPCachesBuilder::fillAllMethods(std::vector & allMethods) { + typedef typename decltype(selectors.map)::value_type methodpair; + for (auto& [name, selectorData] : selectors.map) { + // Remove all non-interesting classes that were added only for inlining tracking. + if (selectorData->classes.size() > 0) { + allMethods.push_back(selectorData.get()); + } + } +} + +// Main entry point of the algorithm, chaining all the steps. +void IMPCachesBuilder::buildPerfectHashes(IMPCaches::HoleMap& holeMap, Diagnostics& diag) { + _timeRecorder.pushTimedSection(); + int droppedClasses = findShiftsAndMasks(); + _timeRecorder.recordTime("find shifts and masks"); + + if (droppedClasses > 0) { + removeUninterestingClasses(); + } + + droppedClasses = solveGivenShiftsAndMasks(); + + if (droppedClasses > 0) { + removeUninterestingClasses(); + } + + computeLowBits(holeMap); + + _timeRecorder.recordTime("assign selector addresses"); + _timeRecorder.popTimedSection(); +} + +size_t IMPCachesBuilder::totalIMPCachesSize() const { + size_t size = 0; + for (CacheBuilder::DylibInfo& d : dylibs) { + for (const auto& [k,v] : d.impCachesClassData) { + assert(v->shouldGenerateImpCache); + size += v->sizeInSharedCache(); + } + } + return size; +} + +void IMPCachesBuilder::computeLowBits(IMPCaches::HoleMap& holeMap) { + holeMap.clear(); + addressSpace.computeLowBits(holeMap); +} + +// Shuffles selectors around to satisfy size constraints +int IMPCachesBuilder::solveGivenShiftsAndMasks() { + std::vector allClasses; + fillAllClasses(allClasses); + + int hadToIncreaseSizeCount = 0; + int droppedClasses = 0; + + // Sanity check: all methods should have a fixed bits mask + // that at least encompasses the masks of all the classes they are in. + for (const IMPCaches::ClassData* c : allClasses) { + for (const ClassData::Method& m : c->methods) { + assert(((m.selector->fixedBitsMask >> c->shift) & c->mask()) == c->mask()); + } + if (c->hadToIncreaseSize()) { + hadToIncreaseSizeCount++; + } + } + + // Sanity check: all classes should have a valid shift and mask. + for (IMPCaches::ClassData* c : allClasses) { + assert(c->checkConsistency()); + } + + + // Now that everything is placed, try to adjust placement within the + // constraints so that we can respect alignment + + _diagnostics.verbose("[IMP Caches] Placed %lu classes, increasing hash table size for %d\n", allClasses.size(), hadToIncreaseSizeCount); + + std::vector methodsSortedByNumberOfFixedBits; + fillAllMethods(methodsSortedByNumberOfFixedBits); + + std::sort(methodsSortedByNumberOfFixedBits.begin(), methodsSortedByNumberOfFixedBits.end(), [](const IMPCaches::Selector* a, const IMPCaches::Selector* b) -> bool { + + // Place the methods with the greatest number of fixed bits first + // as they will have the most constraints + + // If we have the same number of fixed bits, place the methods + // in the largest number of classes first, as they will likely + // have more constraints on their bits + + std::tuple ta = std::make_tuple(a->numberOfSetBits(), a->classes.size(), std::string_view(a->name)); + std::tuple tb = std::make_tuple(b->numberOfSetBits(), b->classes.size(), std::string_view(b->name)); + + return ta > tb; + }); + + std::default_random_engine generator; + + _diagnostics.verbose("[IMP Caches] Rearranging selectors in 128-byte buckets…\n"); + + IMPCaches::ConstraintSet cs; + for (unsigned long methodIndex = 0 ; methodIndex < methodsSortedByNumberOfFixedBits.size() ; methodIndex++) { + IMPCaches::Selector* m = methodsSortedByNumberOfFixedBits[methodIndex]; + if (addressSpace.canPlaceMethodAtIndex(m, m->inProgressBucketIndex)) { + addressSpace.placeMethodAtIndex(m, m->inProgressBucketIndex); + } else { + // Try to find another address for m + cs.clear(); + +#if DEBUG + std::vector sortedClasses = m->classes; + + // Sort the classes so that we can always debug the same thing. + std::sort(sortedClasses.begin(), sortedClasses.end(), [](const IMPCaches::ClassData* a, const IMPCaches::ClassData* b) { + return *a < *b; + }); + + std::vector & classes = sortedClasses; +#else + std::vector & classes = m->classes; +#endif + + bool atLeastOneConstraint = false; + + // Go through all the classes the method is used in and add constraints + for (IMPCaches::ClassData* c : classes) { + if (!c->shouldGenerateImpCache) continue; + atLeastOneConstraint = true; + IMPCaches::Constraint constraint = c->constraintForMethod(m); + cs.add(constraint); + } + + if (!atLeastOneConstraint) { + // This method is only used in classes we have just dropped. + continue; + } + + auto dropClassesWithThisMethod = [this, &classes, &allClasses, &droppedClasses](){ + for (IMPCaches::ClassData* c : classes) { + c->shouldGenerateImpCache = false; + _diagnostics.verbose("Dropping class %s, selectors too difficult to place\n", c->name); + droppedClasses++; + forEachClassInFlatteningHierarchy(c, allClasses, [this](IMPCaches::ClassData *toDrop) { + if (toDrop->shouldGenerateImpCache) { + toDrop->shouldGenerateImpCache = false; + toDrop->droppedBecauseFlatteningSuperclassWasDropped = true; + _diagnostics.verbose("Dropping class %s in the same flattening hierarchy\n", toDrop->name); + } + }); + } + }; + + IMPCaches::Constraint& mergedConstraint = *(cs.mergedConstraint); + + if (mergedConstraint.allowedValues.size() == 0) { + dropClassesWithThisMethod(); + continue; + } + + bool foundValue = false; + std::unordered_set & allowedValues = mergedConstraint.allowedValues; + int modulo = mergedConstraint.mask + 1; + int multiplier = 1 << mergedConstraint.shift; + // We want to go through: + // [((0 + allowedValues) << shift) + k, ((modulo + allowedValues) << shift) + k, ((2*modulo + allowedValue) << shift) + k, ....] etc. + // but we want to randomize this so that we don't completely + // fill up the small addresses. If we do, and we end up with a + // constraint that forces us to zero the high bits, we'll fail + // to find room for the selector. + + // Range for the multiplier of the modulo above + int addressesCount = std::max(((addressSpace.maximumIndex + 1) >> mergedConstraint.shift) / modulo, 1); + + // Fill "addresses" with [0, addressesCount[ so that we can shuffle it below + std::vector addresses(addressesCount); + std::iota(addresses.begin(), addresses.end(), 0); + + for (int i = 0 ; i < addressesCount ; i++) { + if (foundValue) { + break; + } + + // Manual Fisher-Yates: + // Pick a random element in [i, end[. Swap it with the i^th element. Repeat if the random element didn't work. + // We don't do std::shuffle because it wastes time to shuffle the whole range if we find happiness in the beginning. + std::uniform_int_distribution distribution(i,addressesCount-1); + int rd = distribution(generator); + int baseAddress = addresses[rd]; + std::swap(addresses[i], addresses[rd]); + + for (int j : allowedValues) { + if (foundValue) { + break; + } + + for (int k = 0 ; k < multiplier; k++) { + int bucketIndex = ((baseAddress * modulo + j) << mergedConstraint.shift) | k; + if (bucketIndex >= addressSpace.maximumIndex) { + continue; + } + + bool canPlace = addressSpace.canPlaceMethodAtIndex(m, bucketIndex); + if (!canPlace) { + continue; + } + + foundValue = true; + //std::cerr << methodIndex << "/" << methodsSortedByNumberOfFixedBits.size() << " Moving " << m->name << " to " << address << "\n"; + m->inProgressBucketIndex = bucketIndex; + addressSpace.placeMethodAtIndex(m, bucketIndex); + break; + } + } + } + + if (!foundValue) { + _diagnostics.verbose("Failed to place %s\n", m->name); + dropClassesWithThisMethod(); + } + } + + if (methodIndex % 1000 == 0) { + _diagnostics.verbose(" %lu/%lu…\n", methodIndex, methodsSortedByNumberOfFixedBits.size()); + } + + } + + if (droppedClasses == 0) { + _diagnostics.verbose("[IMP Caches] Placed all methods\n"); + } else { + _diagnostics.verbose("[IMP Caches] Finished placing methods, dropping %d classes\n", droppedClasses); + } + + constexpr bool log = false; + if (log) { + std::cerr << addressSpace << "\n"; + } + + return droppedClasses; +} + +SelectorMap::SelectorMap() { + std::unique_ptr magicSelectorStruct = std::make_unique(); + magicSelectorStruct->name = magicSelector.data(); + magicSelectorStruct->offset = 0; + map[magicSelector] = std::move(magicSelectorStruct); +} + +HoleMap::HoleMap() { + addStringOfSize(magicSelector.length() + 1); +} + +bool ClassData::ClassLocator::operator==(const ClassData::ClassLocator & other) { + return (segmentIndex == other.segmentIndex) + && (segmentOffset == other.segmentOffset) + && (strcmp(installName, other.installName) == 0); +} + +bool ClassData::operator==(const ClassData & other) { + if (strcmp(name, other.name) != 0) { + return false; + } + if (methods.size() != other.methods.size()) { + return false; + } + for (unsigned i = 0 ; i < methods.size() ; i++) { + if (*(methods[i].selector) != *(other.methods[i].selector)) { + return false; + } + } + + return true; +} + +}; diff --git a/dyld3/shared-cache/IMPCaches.hpp b/dyld3/shared-cache/IMPCaches.hpp new file mode 100644 index 0000000..40ca59a --- /dev/null +++ b/dyld3/shared-cache/IMPCaches.hpp @@ -0,0 +1,380 @@ +// +// IMPCaches.hpp +// dyld_shared_cache_builder +// +// Created by Thomas Deniau on 18/12/2019. +// + +#ifndef IMPCaches_hpp +#define IMPCaches_hpp + +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SLOTS 1 + +template class objc_method_t; + +namespace IMPCaches { +class Selector; +class Constraint; + +class ClassData { +private: +#if DEBUG_SLOTS + std::vector slots; +#else + std::vector slots; +#endif + + // Scratchpad for constraintForMethod + std::vector allowedValues; + + void resetSlots(); + +public: + bool isMetaclass; + + // True most of the time, but can also be false in 2 cases: + // * We have entries for classes for which we don't want to generate IMP caches + // when they are superclasses of "interesting" (= for which we want an IMP cache) + // classes, so that we can properly attach non-cross-image categories to them and + // reference the right IMP for child classes which are actually interesting + // * We can also have failed to generate a cache for this class, or for a class + // in its flattening superclass hierarchy + bool shouldGenerateImpCache; + + // Class has duplicates or is a child of a class with duplicates. + bool isPartOfDuplicateSet = false; + + // This is set when we drop a class because a class in its flattening superclasss + // hierarchy was dropped. In that case, we won't try to flip its shouldGenerateImpCache + // value back to true when restoring a snapshot. (We could keep track of all the + // dependencies but it would be very messy and the reward is only a few classes here and there). + bool droppedBecauseFlatteningSuperclassWasDropped = false; + + uint8_t backtracks = 0; + + // For debug purposes + const char* name; + + struct Method { + const char* installName = nullptr; + const char* className = nullptr; + const char* categoryName = nullptr; + Selector* selector = nullptr; + bool wasInlined = false; + bool fromFlattening = false; + }; + + std::vector methods; + + std::string description() const; + + int neededBits; + + void didFinishAddingMethods(); + + // Not const because this uses the slots as a scratchpad + Constraint constraintForMethod(const Selector* m); + + bool operator<(const ClassData& r) const { + if (isMetaclass != r.isMetaclass) return isMetaclass; + return strcmp(name, r.name) < 0; + } + + struct PlacementAttempt { + int numberOfBitsToSet; + int shift; + int neededBits; + + PlacementAttempt(int _numberOfBitsToSet, int _shift, int _neededBits) : numberOfBitsToSet(_numberOfBitsToSet), shift(_shift), neededBits(_neededBits) { + + } + + std::string description() const; + + friend bool operator<(const PlacementAttempt& l, const PlacementAttempt& r) + { + return std::tie(l.neededBits, l.numberOfBitsToSet, l.shift) + < std::tie(r.neededBits, r.numberOfBitsToSet, r.shift); + } + + int mask() const { + return (1 << neededBits) - 1; + } + + struct PreviousMethodAddress { + int address; + int fixedBitsMask; + }; + + struct PreviousState { + int neededBits; + int shift; + int mask; + std::unordered_map methods; + }; + + struct Result { + bool success; + PreviousState previousState; + }; + }; + + // Possibilities we go through in the greedy backtracking algorithm findShiftsAndMask() + // Each class has a set (attempts()) of shift-mask possibilities, ordered, and we go through them + // sequentially. + PlacementAttempt attemptForShift(int shift, int neededBits) const; + std::vector attempts() const; + + int shift; + + inline int modulo() const { + return 1 << neededBits; + } + + inline int mask() const { + return modulo() - 1; + } + + // Attempt to place the class with the shift/mask in the attempt argument. + typename PlacementAttempt::Result applyAttempt(const PlacementAttempt& attempt, std::minstd_rand & randomNumberGenerator); + + // Reassign the addresses as they were before we produced resultToBacktrackFrom + void backtrack(typename PlacementAttempt::Result& resultToBacktrackFrom); + + // Did we have to grow the size of the hash table to one more bit when attempting to place it? + bool hadToIncreaseSize() const; + + // Not const because this stomps over the slots array for checking + bool checkConsistency(); + + // Size in bytes needed for this hash table in the shared cache. + size_t sizeInSharedCache() const; + + // Used to store the location of a flattening root's superclass, so that + // we can compute its final vmAddr once we are in the ObjC optimizer. + struct ClassLocator { + const char *installName; + unsigned segmentIndex; + unsigned segmentOffset; + bool operator==(const ClassLocator & other); + }; + + std::string_view flatteningRootName; + std::set flattenedSuperclasses; + std::optional flatteningRootSuperclass; + + // For debug purposes + bool operator==(const ClassData &other); +}; + +/// A unique selector. Has a name, a list of classes it's used in, and an address (which is actually an offset from +/// the beginning of the selectors section). Due to how the placement algorithm work, it also has a current +/// partial address and the corresponding bitmask of fixed bits. The algorithm adds bits to the partial address +/// as it makes progress and updates the mask accordingly +class Selector { +public: + // For debug purposes + const char* name; + + /// Classes the selector is used in + std::vector classes; + + /// Which bits of address are already set + int fixedBitsMask; + + /// Current 128-byte bucket index for this selector. Only the bits in fixedBitsMask are actually frozen. + int inProgressBucketIndex; + + /// Full offset of the selector, including its low 7 bits (so, not the bucket's index ; inProgressBucketIndex (assuming all bits are setr by now) << 7 + some low bits) + int offset; // including low bits + + /// Number of bits that you would need to freeze if you were to use this selector with this shift and mask. + int numberOfBitsToSet(int shift, int mask) const { + int fixedBits = (fixedBitsMask >> shift) & mask; + return __builtin_popcount(mask) - __builtin_popcount(fixedBits); + } + + int numberOfSetBits() const { + return __builtin_popcount(fixedBitsMask); + } + + unsigned int size() const { + return (unsigned int)strlen(name) + 1; + } + + // For debug purposes + bool operator==(const Selector &other) const { + return (strcmp(name, other.name) == 0) + && (inProgressBucketIndex == other.inProgressBucketIndex) + && (fixedBitsMask == other.fixedBitsMask); + } + + bool operator!=(const Selector &other) const { + return !(*this == other); + } + +}; + +class AddressSpace; +std::ostream& operator<<(std::ostream& o, const AddressSpace& c); + +struct Hole { + // [startAddress, endAddress[ + int startAddress; + int endAddress; + int size() const { + return endAddress - startAddress; + } + + // All our intervals are non-overlapping + bool operator<(const Hole& other) const { + auto a = std::make_tuple(size(), startAddress); + auto b = std::make_tuple(other.size(), other.startAddress); + return a < b; + } +}; + +/// Represents the holes left by the selector placement algorithm, to be filled later with other selectors we did not target. +class HoleMap { +public: + + /// Returns the position at which we should place a string of size `size`. + int addStringOfSize(unsigned size); + + /// Total size of all the holes + unsigned long totalHoleSize() const; + + // Empty the hole map. + void clear(); + + HoleMap(); + +private: + friend class AddressSpace; + friend std::ostream& operator<< (std::ostream& o, const HoleMap& m); + + int endAddress = 0; + std::set holes; +}; + +// A selector that is known to be at offset 0, to let objc easily compute +// the offset of a selector given the SEL. +constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf"; + +/// This is used to place the selectors in 128-byte buckets. +/// The "indices" below are the indices of the 128-byte bucket. To get an actual selector offset from this, +/// we shift the index to the left by 7 bits, and assign low bits depending on the length of each selector (this happens +/// in @see computeLowBits()). +/// The goal of this class is to validate that selectors can actually be placed in the buckets without overflowing +/// the 128-byte total length limit (based on the length of each individual selector) +class AddressSpace { +public: + int sizeAtIndex(int idx) const; + int sizeAvailableAfterIndex(int idx) const; + bool canPlaceMethodAtIndex(const Selector* method, int idx) const; + void placeMethodAtIndex(Selector* method, int idx); + + // If we decided to drop any classes, remove the selectors that were only present in them + void removeUninterestingSelectors(); + + // Once all selectors are placed in their 128-byte buckets, + // actually assign the low 7 bits for each, and make a map of the + // holes so that we can fill them with other selectors later. + void computeLowBits(HoleMap& selectorsHoleMap) const; + + std::string description() const; + + static const int maximumIndex = (1 << 17) - 1; + static constexpr int bagSizeShift = 7; + + friend std::ostream& operator<< (std::ostream& o, const AddressSpace& c); +private: + inline int bagSizeAtIndex(int idx) const { + static constexpr int bagSize = 1 << bagSizeShift; + static constexpr int bag0Size = bagSize - (magicSelector.length() + 1); + return idx ? bagSize : bag0Size; + } + bool canPlaceWithoutFillingOverflowCellAtIndex(int idx) const; + std::unordered_map> methodsByIndex; + std::unordered_map sizes; +}; + +/// Represents a constraint on some of the bits of an address +/// It stores a set of allowed values for a given range of bits (shift and mask) +class Constraint { +public: + int mask; + int shift; + std::unordered_set allowedValues; + + Constraint intersecting(const Constraint& other) const; + friend std::ostream& operator << (std::ostream& o, const Constraint& c); + + bool operator==(const Constraint& other) const { + return mask == other.mask && + shift == other.shift && + allowedValues == other.allowedValues; + } + + struct Hasher { + size_t operator()(const IMPCaches::Constraint& c) const { + return c.shift << 24 | c.mask << 16 | c.allowedValues.size() << 8 | *c.allowedValues.begin(); + } + }; +}; + +/// Merges several Constraints together to generate a simplified constraint equivalent to the original set of constraints +class ConstraintSet { + std::unordered_set constraints; + +public: + std::optional mergedConstraint; + + bool add(const Constraint& c); + void clear(); +}; + +class SelectorMap { +public: + using UnderlyingMap = std::map>; + UnderlyingMap map; + SelectorMap(); +}; + +// Implemented in OptimizerObjC +size_t sizeForImpCacheWithCount(int entries); + +struct ClassKey { + std::string_view name; + bool metaclass; + + size_t hash() const { + std::size_t seed = 0; + seed ^= std::hash()(name) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash()(metaclass) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + + bool operator==(const ClassKey &other) const { + return (name == other.name) && (metaclass == other.metaclass); + } +}; + +struct ClassKeyHasher { + size_t operator()(const ClassKey& k) const { + return k.hash(); + } +}; + +} + + +#endif /* IMPCaches_hpp */ diff --git a/dyld3/shared-cache/IMPCachesBuilder.hpp b/dyld3/shared-cache/IMPCachesBuilder.hpp new file mode 100644 index 0000000..2301318 --- /dev/null +++ b/dyld3/shared-cache/IMPCachesBuilder.hpp @@ -0,0 +1,186 @@ +// +// IMPCachesBuilder.hpp +// dyld +// +// Created by Thomas Deniau on 20/01/2020. +// + +#pragma once + +#include "CacheBuilder.h" +#include "IMPCaches.hpp" +#include + +namespace IMPCaches { + +class IMPCachesBuilder { +public: + SelectorMap selectors; + + /// Parses source dylibs to figure out which methods end up in which caches + bool parseDylibs(Diagnostics& diag); + + /// Builds a map of the class hierarchy across all dylibs. This is especially used to resolve + /// cross-dylib dependencies for superclasses and categories. + void buildClassesMap(Diagnostics& diag); + + /// The entry point of the algorithm + void buildPerfectHashes(HoleMap& holeMap, Diagnostics& diag); + + /// Regenerate the hole map if we needed to evict dylibs. + void computeLowBits(HoleMap& holeMap); + + // Total size the hash tables will need. + size_t totalIMPCachesSize() const; + + void clear() { + // FIXME: Implement this + } + + /** Classes for which we want to generate IMP caches according to the input JSON config laid down by OrderFiles + * The value is the index of the class in the json file (they are ordered by decreasing order of importance) + * This isn't just a vector because we also need to test for membership quickly */ + std::unordered_map neededClasses; + std::unordered_map neededMetaclasses; + + // Classes for which we don't generate IMP caches, but which we need to track + // to attach categories to them and find the right implementation for + // inlined selectors + std::unordered_set trackedClasses; + std::unordered_set trackedMetaclasses; + + // List of classes with the same name that appear in different images. + // We should not try to play with fire and try to support duplicated + // classes in IMP caches. + + using ClassSet = std::unordered_set; + ClassSet duplicateClasses; + + /// Selectors which we want to inline into child classes' caches. + std::unordered_set selectorsToInline; + std::vector inlinedSelectors; + + // Class hierarchies to flatten: + // In every class, include every selector including + // the ones from superclasses up to the flattening root. + // This lets us enable constant caches for some of the classes which are not leaves. + // We avoid the pyramid of doom by making sure selectors from superclasses are + // included in child caches, up until some flattening root, and msgSend will + // fallback to the superclass of the flattening root if it can't find the selector + // it expects. + std::unordered_set metaclassHierarchiesToFlatten; + std::unordered_set classHierarchiesToFlatten; + + /// All the dylibs the algorith, works on. + std::vector & dylibs; + + IMPCachesBuilder(std::vector& allDylibs, const dyld3::json::Node& optimizerConfiguration, Diagnostics& diag, TimeRecorder& timeRecorder, const dyld3::closure::FileSystem& fileSystem); + + struct ObjCClass { + + const dyld3::MachOAnalyzer* superclassMA = nullptr; + const uint8_t* metaClass = nullptr; + const uint8_t* superclass = nullptr; + uint64_t methodListVMaddr = 0; + const char* className = nullptr; + bool isRootClass = false; + bool isMetaClass = false; + + const IMPCaches::ClassData::ClassLocator superclassLocator() const { + uint64_t loadAddress = superclassMA->preferredLoadAddress(); + __block std::optional segmentIndex; + __block std::optional segmentOffset; + uint64_t superclassVMAddr = (superclass - (const uint8_t*)superclassMA) + loadAddress; + superclassMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + if (info.vmAddr <= superclassVMAddr && (info.vmAddr + info.vmSize) > superclassVMAddr) { + segmentIndex = info.segIndex; + segmentOffset = (unsigned)(superclassVMAddr - info.vmAddr); + stop = true; + return; + } + }); + assert(segmentIndex.has_value() && segmentOffset.has_value()); + return IMPCaches::ClassData::ClassLocator { + .installName = superclassMA->installName(), + .segmentIndex = *segmentIndex, + .segmentOffset = *segmentOffset, + }; + } + }; + struct ObjCCategory { + const dyld3::MachOAnalyzer* classMA = nullptr; + const uint8_t* cls = nullptr; + }; + +private: + + /// Is this a class for which we want to generate an IMP cache? + bool isClassInteresting(const ObjCClass& theClass) const; + + /// Is this a class for which we want to generate an IMP cache, or on which we want to track method attachment by categories? + bool isClassInterestingOrTracked(const ObjCClass& theClass) const; + + /// Adds a method to a given class's cache. + void addMethod(IMPCaches::ClassData* classDataPtr, const char* methodName, const char* installName, const char* className, const char* catName, bool inlined, bool fromFlattening); + + /// Inline a method from a parent's cache to a child's cache. + void inlineMethodIfNeeded(IMPCaches::ClassData* classToInlineIn, const char* classToInlineFrom, const char* catToInlineFrom, const char* installNameToInlineFrom, const char* name, std::set & seenSelectors, bool isFlattening); + + /// Map from location (address in the mmapped source dylib) to class info built by buildClassesMap() + std::unordered_map objcClasses; + + /// Map from location (address in the mmapped source dylib) to category info built by buildClassesMap() + std::unordered_map objcCategories; + + /// Address space where the selectors are going to live. Used to determine at which address we'll actually layout each selector. + IMPCaches::AddressSpace addressSpace; + + /// Find a shift and a mask for each class. Returns the number of classes that we could not place. + int findShiftsAndMasks(); + + /// Shuffles selectors around to satisfy size constraints. Returns the number of classes that we could not place. + int solveGivenShiftsAndMasks(); + + /// Determine classes we need to track category attachment on (for later inlining) + void buildTrackedClasses(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma); + + /// Parses the method lists in the source dylibs to determine what will end up in which IMP cache. + void populateMethodLists(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma, int* duplicateClassCount); + + /// Go through categories and add the methods from the category to the corresponding class's IMP cache + void attachCategories(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma); + + // Inline some selectors (driven by the OrderFiles) from parent caches into child caches. + void inlineSelectors(CacheBuilder::DylibInfo& dylib, std::unordered_map & dylibsByInstallName, const dyld3::MachOAnalyzer* ma); + + void fillAllClasses(std::vector & allClasses); + void fillAllMethods(std::vector & allMethods); + void removeUninterestingClasses(); + + struct TargetClassFindingResult { + bool success; + const dyld3::MachOLoaded* foundInDylib; + const uint8_t* location; + }; + + struct BindTarget { + std::string symbolName = ""; + const dyld3::MachOAnalyzer* targetDylib = nullptr; + bool isWeakImport = false; + }; + + struct DylibAndDeps { + const dyld3::MachOAnalyzer* ma = nullptr; + __block std::vector dependentLibraries; + }; + + TargetClassFindingResult findTargetClass(const dyld3::MachOAnalyzer* ma, uint64_t targetClassVMAddr, uint64_t targetClassPointerVMAddr, const char* logContext, const std::unordered_map & bindLocations, std::vector & bindTargets, std::unordered_map &dylibMap); + + const std::string * nameAndIsMetaclassPairFromNode(const dyld3::json::Node & node, bool* metaclass); + + Diagnostics& _diagnostics; + TimeRecorder& _timeRecorder; + const dyld3::closure::FileSystem& _fileSystem; +}; + +} diff --git a/dyld3/shared-cache/MachOFileAbstraction.hpp b/dyld3/shared-cache/MachOFileAbstraction.hpp index ffa275c..8f34a6d 100644 --- a/dyld3/shared-cache/MachOFileAbstraction.hpp +++ b/dyld3/shared-cache/MachOFileAbstraction.hpp @@ -49,6 +49,19 @@ #define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C #define DYLD_CACHE_ADJ_V2_THREADED_POINTER_64 0x0D +#ifndef LC_FILESET_ENTRY +#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */ +struct fileset_entry_command { + uint32_t cmd; /* LC_FILESET_ENTRY */ + uint32_t cmdsize; /* includes id string */ + uint64_t vmaddr; /* memory address of the dylib */ + uint64_t fileoff; /* file offset of the dylib */ + union lc_str entry_id; /* contained entry id */ + uint32_t reserved; /* entry_id is 32-bits long, so this is the reserved padding */ +}; +#endif + + #include "FileAbstraction.hpp" //#include "Architectures.hpp" @@ -775,8 +788,12 @@ public: } } - if (strcmp(segname, "__DATA") == 0) - return getSection("__DATA_CONST", sectname); + if (strcmp(segname, "__DATA") == 0) { + if (const macho_section

* dataConst = getSection("__DATA_CONST", sectname)) + return dataConst; + if (const macho_section

* dataDirty = getSection("__DATA_DIRTY", sectname)) + return dataDirty; + } return NULL; } @@ -849,6 +866,77 @@ private: dyld_info_command fields; }; + +// +// mach-o build version load command +// +template +class macho_build_version_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t platform() const INLINE { return E::get32(fields.platform); } + void set_platform(uint32_t value) INLINE { E::set32(fields.platform, value); } + + uint32_t minos() const INLINE { return E::get32(fields.minos); } + void set_minos(uint32_t value) INLINE { E::set32(fields.minos, value); } + + uint32_t sdk() const INLINE { return E::get32(fields.sdk); } + void set_sdk(uint32_t value) INLINE { E::set32(fields.sdk, value); } + + uint32_t ntools() const INLINE { return E::get32(fields.ntools); } + void set_ntools(uint32_t value) INLINE { E::set32(fields.ntools, value); } + + typedef typename P::E E; +private: + build_version_command fields; +}; + +// +// mach-o routines load command +// +template struct macho_fileset_entry_command_content {}; +template <> struct macho_fileset_entry_command_content > { fileset_entry_command fields; enum { CMD = LC_FILESET_ENTRY }; }; +template <> struct macho_fileset_entry_command_content > { fileset_entry_command fields; enum { CMD = LC_FILESET_ENTRY }; }; +template <> struct macho_fileset_entry_command_content > { fileset_entry_command fields; enum { CMD = LC_FILESET_ENTRY }; }; +template <> struct macho_fileset_entry_command_content > { fileset_entry_command fields; enum { CMD = LC_FILESET_ENTRY }; }; + +template +class macho_fileset_entry_command { +public: + uint32_t cmd() const INLINE { return E::get32(cache_entry_id.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(cache_entry_id.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(cache_entry_id.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(cache_entry_id.fields.cmdsize, value); } + + uint64_t vmaddr() const INLINE { return P::getP(cache_entry_id.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(cache_entry_id.fields.vmaddr, value); } + + uint64_t fileoff() const INLINE { return P::getP(cache_entry_id.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(cache_entry_id.fields.fileoff, value); } + + uint32_t entry_id_offset() const INLINE { return E::get32(cache_entry_id.fields.entry_id.offset); } + void set_entry_id_offset(uint32_t value) INLINE { E::set32(cache_entry_id.fields.entry_id.offset, value); } + + const char* entry_id() const INLINE { return (const char*)&cache_entry_id.fields + entry_id_offset(); } + void set_entry_id(const char* value) INLINE { + set_entry_id_offset(sizeof(cache_entry_id.fields)); + strcpy(((char*)&cache_entry_id.fields) + sizeof(cache_entry_id.fields), value); + } + + typedef typename P::E E; + enum { + CMD = macho_fileset_entry_command_content

::CMD + }; +private: + macho_fileset_entry_command_content

cache_entry_id; +}; + #ifndef NO_ULEB inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { uint64_t result = 0; @@ -884,7 +972,7 @@ inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) bit += 7; } while (byte & 0x80); // sign extend negative numbers - if ( (byte & 0x40) != 0 ) + if ( ((byte & 0x40) != 0) && (bit < 64) ) result |= (~0ULL) << bit; return result; } diff --git a/dyld3/shared-cache/Manifest.h b/dyld3/shared-cache/Manifest.h deleted file mode 100644 index c89d01b..0000000 --- a/dyld3/shared-cache/Manifest.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2017 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -#ifndef Manifest_h -#define Manifest_h - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#import - -#include "DyldSharedCache.h" -#include "Diagnostics.h" -#include "MachOAnalyzer.h" -#include "ClosureFileSystemPhysical.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; - dyld3::closure::FileSystemPhysical fileSystem; - std::vector dylibsForCache; - std::vector otherDylibsAndBundles; - std::vector mainExecutables; - std::string outputPath; - std::set configNames; -}; - -struct Manifest { - struct UUIDInfo { - const MachOAnalyzer* mh; - uint64_t sliceFileOffset; - std::size_t size; - std::string runtimePath; - std::string buildPath; - std::string installName; - std::string arch; - UUID uuid; - 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(), "", "", "", "") {} - }; - - struct Project { - std::vector sources; - }; - - - struct SegmentInfo { - std::string name; - uint64_t startAddr; - uint64_t endAddr; - }; - - struct CacheInfo { - std::vector regions; - std::string cdHash; - }; - - struct CacheImageInfo { - bool included; - std::string exclusionInfo; - UUID uuid; - std::string installname; - std::vector segments; - CacheImageInfo(void) - : included(true) - { - } - }; - - struct Results { - std::string failure; - std::map dylibs; - std::map bundles; - std::map executables; - - std::set warnings; - CacheInfo developmentCache; - CacheInfo productionCache; - CacheImageInfo& dylibForInstallname(const std::string& installname); - void exclude(const dyld3::MachOAnalyzer* ml, const std::string& reason); - void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason); - }; - - struct Architecture { - mutable Results results; - - bool operator==(const Architecture& O) const; - bool operator!=(const Architecture& other) const; - }; - - struct Configuration { - std::string platformName; - std::string device; - std::string disposition; - std::string metabomTag; - std::set metabomTags; - std::set metabomExcludeTags; - std::set metabomRestrictTags; - std::set restrictedInstallnames; - std::map architectures; - - bool operator==(const Configuration& O) const; - bool operator!=(const Configuration& other) const; - const Architecture& architecture(const std::string& architecture) const; - void forEachArchitecture(std::function lambda) const; - }; - - const std::map& projects(); - const Configuration& configuration(const std::string& configuration) const; - void forEachConfiguration(std::function lambda) const; - - void addProjectSource(const std::string& project, const std::string& source, bool first = false); - - const std::string projectPath(const std::string& projectName); - const bool empty(void); - const std::string dylibOrderFile() const; - void setDylibOrderFile(const std::string& dylibOrderFile); - - const std::string dirtyDataOrderFile() const; - void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile); - - const std::string metabomFile() const; - void setMetabomFile(const std::string& metabomFile); - - const Platform platform() const; - void setPlatform(const Platform platform); - - const std::string& build() const; - void setBuild(const std::string& build); - const uint32_t version() const; - void setVersion(const uint32_t manifestVersion); - bool normalized; - - Manifest(Diagnostics& D, const std::string& path, bool populateIt = true); - void populate(const std::set& overlays); - - BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set& 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(); - 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); - void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function lambda); - bool filterForConfig(const std::string& configName); - std::set 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 lambda); - - void forEachSymlink(std::string configuration, std::function lambda); - -private: - NSDictionary* _manifestDict; - Diagnostics& _diags; - std::map _uuidMap; - std::map, UUID> _installNameMap; - std::vector> _symlinks; - static dispatch_queue_t _identifierQueue; - uint32_t _manifestVersion; - std::string _build; - std::string _dylibOrderFile; - std::string _dirtyDataOrderFile; - std::string _metabomFile; - Platform _platform; - std::map _projects; - std::map _configurations; - std::map> _metabomTagMap; - std::map> _metabomSymlinkTagMap; - std::map> _metabomExcludeTagMap; - std::map> _metabomRestrictedTagMap; - - std::vector dylibsForCache(const std::string& configuration, const std::string& architecture); - std::vector otherDylibsAndBundles(const std::string& configuration, const std::string& architecture); - std::vector mainExecutables(const std::string& configuration, const std::string& architecture); - - const UUIDInfo& infoForUUID(const UUID& uuid) const; - const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const; - void insert(std::vector& mappedMachOs, const CacheImageInfo& imageInfo); - bool loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set& architectures); - bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set& architectures); - void dedupeDispositions(); - void calculateClosure(const std::string& configuration, const std::string& architecture); - void canonicalizeDylib(const std::string& installname); - template - void canonicalizeDylib(const std::string& installname, const uint8_t* p); - void addImplicitAliases(void); -}; -} - -namespace std { -template <> -struct hash { - size_t operator()(const dyld3::UUID& x) const - { - return x.hash(); - } -}; -} - -#endif /* Manifest_h */ diff --git a/dyld3/shared-cache/Manifest.mm b/dyld3/shared-cache/Manifest.mm deleted file mode 100644 index 6d09811..0000000 --- a/dyld3/shared-cache/Manifest.mm +++ /dev/null @@ -1,1227 +0,0 @@ -/* - * Copyright (c) 2017 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -extern "C" { -#include -#include -#include -#include -#include -}; - -#include -#include -#include -#include - -#include "MachOFileAbstraction.hpp" -#include "FileAbstraction.hpp" -#include "Trie.hpp" -#include "FileUtils.h" -#include "StringUtils.h" -#include "MachOFile.h" -#include "MachOAnalyzer.h" - -#include -#include - -#include -#include - -#include "Manifest.h" -#include "ClosureFileSystemPhysical.h" - -namespace { -//FIXME this should be in a class -static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; } - -template -inline bool is_disjoint(const Set1& set1, const Set2& set2) -{ - if (set1.empty() || set2.empty()) - return true; - - typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end(); - typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end(); - - if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin()) - return true; - - while (it1 != it1End && it2 != it2End) { - if (*it1 == *it2) - return false; - if (*it1 < *it2) { - it1++; - } else { - it2++; - } - } - - return true; -} - -} /* Anonymous namespace */ - -namespace dyld3 { -void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason) -{ - 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) -{ - const MachOAnalyzer* mh = manifest.machOForUUID(uuid); - dylibs[uuid].uuid = uuid; - dylibs[uuid].installname = mh->installName(); - dylibs[uuid].included = false; - dylibs[uuid].exclusionInfo = reason; -} - -Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname) -{ - auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair d) { return d.second.installname == installname; }); - assert(i != dylibs.end()); - return i->second; -} - -bool Manifest::Architecture::operator==(const Architecture& O) const -{ - for (auto& dylib : results.dylibs) { - if (dylib.second.included) { - auto Odylib = O.results.dylibs.find(dylib.first); - if (Odylib == O.results.dylibs.end() - || Odylib->second.included == false - || Odylib->second.uuid != dylib.second.uuid) - return false; - } - } - - for (const auto& Odylib : O.results.dylibs) { - if (Odylib.second.included) { - auto dylib = results.dylibs.find(Odylib.first); - if (dylib == results.dylibs.end() - || dylib->second.included == false - || dylib->second.uuid != Odylib.second.uuid) - return false; - } - } - - for (auto& bundle : results.bundles) { - if (bundle.second.included) { - auto Obundle = O.results.bundles.find(bundle.first); - if (Obundle == O.results.bundles.end() - || Obundle->second.included == false - || Obundle->second.uuid != bundle.second.uuid) - return false; - } - } - - for (const auto& Obundle : O.results.bundles) { - if (Obundle.second.included) { - auto bundle = results.bundles.find(Obundle.first); - if (bundle == results.bundles.end() - || bundle->second.included == false - || bundle->second.uuid != Obundle.second.uuid) - return false; - } - } - - for (auto& executable : results.executables) { - if (executable.second.included) { - auto Oexecutable = O.results.executables.find(executable.first); - if (Oexecutable == O.results.executables.end() - || Oexecutable->second.included == false - || Oexecutable->second.uuid != executable.second.uuid) - return false; - } - } - - for (const auto& Oexecutable : O.results.executables) { - if (Oexecutable.second.included) { - auto executable = results.executables.find(Oexecutable.first); - if (executable == results.executables.end() - || executable->second.included == false - || executable->second.uuid != Oexecutable.second.uuid) - return false; - } - } - - return true; -} - -bool Manifest::Configuration::operator==(const Configuration& O) const -{ - return architectures == O.architectures; -} - -bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); } - -const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const -{ - assert(architectures.find(architecture) != architectures.end()); - return architectures.find(architecture)->second; -} - -void Manifest::Configuration::forEachArchitecture(std::function lambda) const -{ - for (const auto& architecutre : architectures) { - lambda(architecutre.first); - } -} - -bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); } - -const std::map& Manifest::projects() -{ - return _projects; -} - -const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const -{ - assert(_configurations.find(configuration) != _configurations.end()); - return _configurations.find(configuration)->second; -} - -void Manifest::forEachConfiguration(std::function lambda) const -{ - for (const auto& configuration : _configurations) { - lambda(configuration.first); - } -} - -void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first) -{ - auto& sources = _projects[project].sources; - if (std::find(sources.begin(), sources.end(), source) == sources.end()) { - if (first) { - sources.insert(sources.begin(), source); - } else { - sources.push_back(source); - } - } -} - -const std::string Manifest::projectPath(const std::string& projectName) -{ - auto project = _projects.find(projectName); - if (project == _projects.end()) - return ""; - if (project->second.sources.size() == 0) - return ""; - return project->second.sources[0]; -} - -const bool Manifest::empty(void) -{ - for (const auto& configuration : _configurations) { - if (configuration.second.architectures.size() != 0) - return false; - } - return true; -} - -const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; }; -void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; }; - -const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; }; -void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; }; - -const std::string Manifest::metabomFile() const { return _metabomFile; }; -void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; }; - -const Platform Manifest::platform() const { return _platform; }; -void Manifest::setPlatform(const Platform platform) { _platform = platform; }; - -const std::string& Manifest::build() const { return _build; }; -void Manifest::setBuild(const std::string& build) { _build = build; }; -const uint32_t Manifest::version() const { return _manifestVersion; }; -void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; }; - -BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set& 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.archs = &dyld3::GradedArchs::forName(arch.c_str()); - options.platform = platform(); - options.excludeLocalSymbols = true; - options.optimizeStubs = optimizeStubs; - options.optimizeObjC = true; - options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ? - DyldSharedCache::Agile : DyldSharedCache::SHA256only; - options.dylibsRemovedDuringMastering = true; - options.inodesAreSameAsRuntime = false; - options.cacheSupportsASLR = true; - options.forSimulator = false; - options.isLocallyBuiltCache = isLocallyBuiltCache; - options.verbose = verbose; - options.evictLeafDylibsOnOverflow = true; - options.loggingPrefix = prefix; - options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile)); - options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile)); - - char rootsDir[PATH_MAX]; - realpath("./Root/", rootsDir); - - dyld3::BuildQueueEntry queueEntry; - retval.configNames = configs; - retval.options = options; - retval.fileSystem = dyld3::closure::FileSystemPhysical(strdup(rootsDir)); - retval.outputPath = outputPath; - retval.dylibsForCache = dylibsForCache(*configs.begin(), arch); - retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch); - retval.mainExecutables = mainExecutables(*configs.begin(), arch); - - return retval; -} - -bool Manifest::loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set& architectures) -{ - assert(!_diags.hasError()); - - const MachOFile* mf = reinterpret_cast(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; - - const MachOAnalyzer* ma = reinterpret_cast(p); - if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), dyld3::GradedArchs::forName(archName.c_str()), _platform) ) { - // Clear the error and punt - _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; - } - - uuid_t uuid; - ma->getUuid(uuid); - - 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(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(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()); - } - } else { - auto info = infoForUUID(i->second); - _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str()); - - // This is the "Good" one, overwrite - if (runtimePath == installName) { - _uuidMap.erase(uuid); - _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName))); - } - } - } else { - _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, ""))); - } - return true; -} - -//FIXME: assert we have not errored first -bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set& architectures) -{ - __block bool retval = false; - const void* p = (uint8_t*)(-1); - struct stat stat_buf; - - std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath); - - if (p == (uint8_t*)(-1)) { - return false; - } - - if ( 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; - }); - } else { - return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures); - } - return retval; -} - - -const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const { - auto i = _uuidMap.find(uuid); - assert(i != _uuidMap.end()); - return i->second; -} - -const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const { - UUIDInfo retval; - auto uuidI = _installNameMap.find(std::make_pair(installName, arch)); - if (uuidI == _installNameMap.end()) - return UUIDInfo(); - - auto i = _uuidMap.find(uuidI->second); - if (i == _uuidMap.end()) - return UUIDInfo(); - return i->second; -} - -const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const { - return infoForUUID(uuid).mh; -} - -const std::string Manifest::buildPathForUUID(const UUID& uuid) { - return infoForUUID(uuid).buildPath; -} - -const std::string Manifest::runtimePathForUUID(const UUID& uuid) { - return infoForUUID(uuid).runtimePath; -} - -const std::string& Manifest::installNameForUUID(const UUID& uuid) { - return infoForUUID(uuid).installName; -} - -Manifest::Manifest(Diagnostics& D, const std::string& path, bool populateIt) : - _diags(D) -{ - _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)]; - if (!_manifestDict) - return; - NSString* platStr = _manifestDict[@"platform"]; - - if (platStr == nullptr) - platStr = @"ios"; - std::string platformString = [platStr UTF8String]; - setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]); - - if (platformString == "ios") { - setPlatform(dyld3::Platform::iOS); - } else if ( (platformString == "tvos") || (platformString == "atv") ) { - setPlatform(dyld3::Platform::tvOS); - } else if ( (platformString == "watchos") || (platformString == "watch") ) { - setPlatform(dyld3::Platform::watchOS); - } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) { - setPlatform(dyld3::Platform::bridgeOS); - } else if ( (platformString == "macos") || (platformString == "osx") ) { - setPlatform(dyld3::Platform::macOS); - } else { - //Fixme should we error? - setPlatform(dyld3::Platform::iOS); - } - - for (NSString* project in _manifestDict[@"projects"]) { - for (NSString* source in _manifestDict[@"projects"][project]) { - addProjectSource([project UTF8String], [source UTF8String]); - } - } - - for (NSString* configuration in _manifestDict[@"configurations"]) { - std::string configStr = [configuration UTF8String]; - std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String]; - - if (_manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { - for (NSString* excludeTag in _manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { - _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]); - _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]); - } - } - - if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { - for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { - _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]); - _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]); - } - } - - _configurations[configStr].metabomTag = configTag; - _configurations[configStr].metabomTags.insert(configTag); - _configurations[configStr].platformName = - [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String]; - - if (endsWith(configStr, "InternalOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS")); - } else if (endsWith(configStr, "VendorOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS")); - } else if (endsWith(configStr, "VendorUIOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS")); - } else if (endsWith(configStr, "CarrierOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS")); - } else if (endsWith(configStr, "FactoryOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS")); - } else if (endsWith(configStr, "DesenseOS")) { - _configurations[configStr].disposition = "internal"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS")); - } else if (endsWith(configStr, "MinosOS")) { - _configurations[configStr].disposition = "minos"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS")); - } else if (endsWith(configStr, "DemoOS")) { - _configurations[configStr].disposition = "demo"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS")); - } else if (endsWith(configStr, "MinosOS")) { - _configurations[configStr].disposition = "minos"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS")); - } else if (endsWith(configStr, "DeveloperOS")) { - _configurations[configStr].disposition = "user"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS")); - } else if (endsWith(configStr, "OS")) { - _configurations[configStr].disposition = "user"; - _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS")); - } - - for (NSString* architecture in _manifestDict[@"configurations"][configuration][@"architectures"]) { - //HACK until B&I stops mastering armv7s - if ([architecture isEqual:@"armv7s"]) break; - _configurations[configStr].architectures[[architecture UTF8String]] = Architecture(); - } - } - - setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]); - setBuild([_manifestDict[@"build"] UTF8String]); - if (_manifestDict[@"dylibOrderFile"]) { - setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]); - } - if (_manifestDict[@"dirtyDataOrderFile"]) { - setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]); - } - - if (populateIt) - populate(std::set()); -} - -// Perform the initialization that populateIt=false omitted. -void Manifest::populate(const std::set& overlays) -{ - auto metabom = MBMetabomOpen(metabomFile().c_str(), false); - auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", ""); - MBEntry entry; - - // Collect every architecture from the configurations. - std::set architectures; - for (auto& configuration : _configurations) { - for (auto& arch : configuration.second.architectures) { - architectures.insert(arch.first); - } - } - - auto filterPath = [](std::string& entryPath) { - // Skip artifacts that happen to be in the build chain - if ( startsWith(entryPath, "/Applications/Xcode.app") ) { - return true; - } - - // Skip variants we can't deal with - if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) { - return true; - } - - // Skip images that are only used in InternalOS variants - if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) { - return true; - } - - // Skip genCache generated dylibs - if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) { - return true; - } - - return false; - }; - - __block std::set seenPaths; - - // FIXME error handling (NULL metabom) - - //First we iterate through the bom and build our objects - while ((entry = MBIteratorNext(metabomEnumerator))) { - BOMFSObject fsObject = MBEntryGetFSObject(entry); - BOMFSObjType entryType = BOMFSObjectType(fsObject); - std::string entryPath = BOMFSObjectPathName(fsObject); - if (entryPath[0] == '.') { - entryPath.erase(0, 1); - } - - if (filterPath(entryPath)) - continue; - - MBTag tag; - auto tagCount = MBEntryGetNumberOfProjectTags(entry); - 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 { - MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); - MBEntryGetProjectTags(entry, tags); - - //Sigh, we can have duplicate entries for the same tag, so build a set to work with - std::set tagStrs; - std::map tagStrMap; - for (auto i = 0; i < tagCount; ++i) { - tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i])); - tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i])); - } - - if (tagStrs.size() > 1) { - std::string projects; - for (const auto& tagStr : tagStrs) { - if (!projects.empty()) - projects += ", "; - - projects += "'" + tagStr + "'"; - } - _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str()); - } - tag = tagStrMap[*tagStrs.begin()]; - free(tags); - } - - std::string projectName = MBMetabomGetProjectForTag(metabom, tag); - tagCount = MBEntryGetNumberOfPackageTags(entry); - MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); - MBEntryGetPackageTags(entry, tags); - std::set tagStrs; - - for (auto i = 0; i < tagCount; ++i) { - tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i])); - } - - 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) { - foundParser = loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures); - if (_diags.hasError()) { - _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str()); - _diags.clearError(); - } - } - - if (foundParser) - seenPaths.insert(entryPath); - } else if (isSymlinkFile) { - const char* target = BOMFSObjectSymlinkTarget(fsObject); - _symlinks.push_back({ entryPath, target }); - _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs)); - } - } - } - - MBIteratorFree(metabomEnumerator); - MBMetabomFree(metabom); - - // Add files from the overlays - for (const auto& overlay : overlays) { - iterateDirectoryTree("", overlay, ^bool(const std::string &dirPath) { - return false; - }, ^(const std::string &path, const struct stat &statBuf) { - std::string relativePath = path; - if (relativePath.size() < overlay.size()) - return; - relativePath = relativePath.substr(overlay.size()); - if (relativePath.front() != '/') - return; - if (filterPath(relativePath)) - return; - // Filter out dylibs we've seen already - if (seenPaths.count(relativePath)) - return; - if (loadParsers(path, relativePath, architectures)) { - seenPaths.insert(relativePath); - // Get the tags from libSystem, assuming that will work. - std::set tagStrs; - auto it = _metabomTagMap.find("/usr/lib/libSystem.B.dylib"); - if (it != _metabomTagMap.end()) - tagStrs = it->second; - _metabomTagMap.insert(std::make_pair(relativePath, tagStrs)); - _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", relativePath.c_str()); - return; - } - if (_diags.hasError()) { - _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str()); - _diags.clearError(); - } - }, - true, true); - } - -} - -void Manifest::insert(std::vector& mappedMachOs, const CacheImageInfo& imageInfo) { - auto info = infoForUUID(imageInfo.uuid); - auto runtimePath = info.runtimePath; - mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0); -} - -std::vector Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture) -{ - std::vector retval; - const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs; - for (const auto& dylib : dylibs) { - if (dylib.second.included) { - insert(retval, dylib.second); - } - } - return retval; -} - -std::vector Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture) -{ - std::vector retval; - const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs; - for (const auto& dylib : dylibs) { - if (!dylib.second.included) { - 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) { - const UUIDInfo& info = infoForUUID(bundle.second.uuid); - if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) ) - insert(retval, bundle.second); - } - - return retval; -} - -std::vector Manifest::mainExecutables(const std::string& configuration, const std::string& architecture) -{ - std::vector retval; - const auto& executables = _configurations[configuration].architectures[architecture].results.executables; - for (const auto& executable : executables) { - insert(retval, executable.second); - } - - 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) { - if (configName == configuration.first) { - std::map filteredConfigs; - filteredConfigs[configName] = configuration.second; - - _configurations = filteredConfigs; - - for (auto& arch : configuration.second.architectures) { - arch.second.results = Manifest::Results(); - } - return true; - } - } - return false; -} -#pragma clang diagnostic pop - -std::set Manifest::resultsForConfiguration(const std::string& configName) { - std::set 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 - // is reved to give us real info. All the other platforms are way smaller anyway. - if (_platform != Platform::iOS) - return; - - std::map, std::set> dispositionSets; - - for (const auto& configuration : _configurations) { - dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first); - } - - for (const auto& dSet : dispositionSets) { - for (const auto &c1 : dSet.second) { - for (const auto &c2 : dSet.second) { - _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag); - } - } - } -} - -void Manifest::calculateClosure() -{ - auto closureSemaphore = dispatch_semaphore_create(32); - auto closureGroup = dispatch_group_create(); - auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0)); - - dedupeDispositions(); - for (auto& config : _configurations) { - for (auto& arch : config.second.architectures) { - dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER); - dispatch_group_async(closureGroup, closureQueue, [&] { - calculateClosure(config.first, arch.first); - dispatch_semaphore_signal(closureSemaphore); - }); - } - } - - dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER); -} - -void Manifest::remove(const std::string& config, const std::string& arch) -{ - if (_configurations.count(config)) - _configurations[config].architectures.erase(arch); -} - - -void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture) -{ - __block auto& configManifest = _configurations[configuration]; - __block auto& archManifest = _configurations[configuration].architectures[architecture]; - __block std::set newUuids; - std::set processedUuids; - std::set cachedUUIDs; - - // Seed anchors - for (auto& uuidInfo : _uuidMap) { - auto info = uuidInfo.second; - if (info.arch != architecture) { - continue; - } - - auto i = _metabomTagMap.find(info.runtimePath); - assert(i != _metabomTagMap.end()); - auto tags = i->second; - if (!is_disjoint(tags, configManifest.metabomTags)) { - newUuids.insert(info.uuid); - } - } - - __block std::set removedUUIDs; - - // Pull in all dependencies - while (!newUuids.empty()) { - std::set uuidsToProcess = newUuids; - newUuids.clear(); - - for (const auto& uuid : uuidsToProcess) { - if (processedUuids.count(uuid) > 0) { - continue; - } - processedUuids.insert(uuid); - - const MachOAnalyzer* mh = machOForUUID(uuid); - auto runtimePath = runtimePathForUUID(uuid); - assert(mh != nullptr); - - 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 (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 = mh->installName(); - } - - // HACK to insert device specific dylib closures into all caches - 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; - } - - __block std::set 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(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(uuid); - } else { - // It can't be placed in the cache, print out the reasons why - std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\""; - for (auto i = reasons.begin(); i != reasons.end(); ++i) { - reasonString += *i; - if (i != --reasons.end()) { - reasonString += "\", \""; - } - } - reasonString += "\")"; - archManifest.results.exclude(mh, reasonString); - removedUUIDs.insert(uuid); - } - } else if (mh->isBundle()) { - if (archManifest.results.bundles.count(uuid) == 0) { - archManifest.results.bundles[uuid].uuid = uuid; - } - } 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" - || runtimePath == "/usr/local/sbin/launchd.development" - || runtimePath == "/usr/libexec/installd") { - continue; - } - if (archManifest.results.executables.count(uuid) == 0) { - archManifest.results.executables[uuid].uuid = uuid; - } - } - } - } - - __block bool doAgain = true; - - //Trim out dylibs that are missing dependencies - while ( doAgain ) { - doAgain = false; - for (const auto& uuid : cachedUUIDs) { - __block std::set badDependencies; - 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; - - auto i = _installNameMap.find(std::make_pair(loadPath, architecture)); - if (i == _installNameMap.end() || removedUUIDs.count(i->second)) { - removedUUIDs.insert(uuid); - badDependencies.insert(loadPath); - doAgain = true; - } - - if (badDependencies.size()) { - std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\""; - for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) { - reasonString += *i; - if (i != --badDependencies.end()) { - reasonString += "\", \""; - } - } - reasonString += "\")"; - archManifest.results.exclude(mh, reasonString); - } - }); - } - - for (const auto& removedUUID : removedUUIDs) { - cachedUUIDs.erase(removedUUID); - } - } - - //Trim out excluded leaf dylibs - __block std::set linkedDylibs; - - for(const auto& uuid : cachedUUIDs) { - 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); - }); - } - - for(const auto& uuid : cachedUUIDs) { - auto info = infoForUUID(uuid); - auto i = _metabomTagMap.find(info.runtimePath); - assert(i != _metabomTagMap.end()); - auto exclusions = _metabomExcludeTagMap.find(configuration); - if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second)) - continue; - - if (linkedDylibs.count(info.installName) != 0) - continue; - - archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node"); - } -} - -void Manifest::writeJSON(const std::string& path) { - NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init]; - for (auto& configuration : _configurations) { - jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init]; - - for (auto& arch : configuration.second.architectures) { - NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init]; - NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init]; - NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init]; - for (auto& dylib : arch.second.results.dylibs) { - NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid)); - if (dylib.second.included) { - [includedDylibsSet addObject:runtimePath]; - } else { - [otherSet addObject:runtimePath]; - } - } - - for (auto& executable : arch.second.results.executables) { - NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid)); - [executablesSet addObject:runtimePath]; - } - - for (auto& bundle : arch.second.results.bundles) { - NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid)); - [otherSet addObject:runtimePath]; - } - - [includedDylibsSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - - [executablesSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - - [otherSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { - return [obj1 compare:obj2]; - }]; - - jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};; - } - } - - NSError* error = nil; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error]; - (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES]; -} - -void Manifest::write(const std::string& path) -{ - if (path.empty()) - return; - - NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init]; - NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init]; - NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init]; - NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init]; - - cacheDict[@"manifest-version"] = @(version()); - cacheDict[@"build"] = cppToObjStr(build()); - cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile()); - cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile()); - cacheDict[@"metabomFile"] = cppToObjStr(metabomFile()); - - cacheDict[@"projects"] = projectDict; - cacheDict[@"results"] = resultsDict; - cacheDict[@"configurations"] = configurationsDict; - - for (const auto& project : projects()) { - NSMutableArray* sources = [[NSMutableArray alloc] init]; - - for (const auto& source : project.second.sources) { - [sources addObject:cppToObjStr(source)]; - } - - projectDict[cppToObjStr(project.first)] = sources; - } - - for (auto& configuration : _configurations) { - NSMutableArray* archArray = [[NSMutableArray alloc] init]; - for (auto& arch : configuration.second.architectures) { - [archArray addObject:cppToObjStr(arch.first)]; - } - - NSMutableArray* excludeTags = [[NSMutableArray alloc] init]; - for (const auto& excludeTag : configuration.second.metabomExcludeTags) { - [excludeTags addObject:cppToObjStr(excludeTag)]; - } - - configurationsDict[cppToObjStr(configuration.first)] = @{ - @"platformName" : cppToObjStr(configuration.second.platformName), - @"metabomTag" : cppToObjStr(configuration.second.metabomTag), - @"metabomExcludeTags" : excludeTags, - @"architectures" : archArray - }; - } - - for (auto& configuration : _configurations) { - NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init]; - for (auto& arch : configuration.second.architectures) { - NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init]; - NSMutableArray* warningsArray = [[NSMutableArray alloc] init]; - NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init]; - NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init]; - NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash); - NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash); - - for (auto& dylib : arch.second.results.dylibs) { - NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init]; - if (dylib.second.included) { - dylibDict[@"included"] = @YES; - } else { - dylibDict[@"included"] = @NO; - dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo); - } - dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict; - } - - for (auto& warning : arch.second.results.warnings) { - [warningsArray addObject:cppToObjStr(warning)]; - } - - BOOL built = arch.second.results.failure.empty(); - archResultsDict[cppToObjStr(arch.first)] = @{ - @"dylibs" : dylibsDict, - @"built" : @(built), - @"failure" : cppToObjStr(arch.second.results.failure), - @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict }, - @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict }, - @"warnings" : warningsArray - }; - } - resultsDict[cppToObjStr(configuration.first)] = archResultsDict; - } - - switch (platform()) { - case Platform::iOS: - cacheDict[@"platform"] = @"ios"; - break; - case Platform::tvOS: - cacheDict[@"platform"] = @"tvos"; - break; - case Platform::watchOS: - cacheDict[@"platform"] = @"watchos"; - break; - case Platform::bridgeOS: - cacheDict[@"platform"] = @"bridgeos"; - break; - case Platform::macOS: - cacheDict[@"platform"] = @"macos"; - break; - case Platform::unknown: - case Platform::iOSMac: - case Platform::iOS_simulator: - case Platform::tvOS_simulator: - case Platform::watchOS_simulator: - case Platform::driverKit: - cacheDict[@"platform"] = @"unknown"; - break; - } - - NSError* error = nil; - NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict - format:NSPropertyListBinaryFormat_v1_0 - options:0 - error:&error]; - (void)[outData writeToFile:cppToObjStr(path) atomically:YES]; -} - - -void Manifest::forEachMachO(std::string configuration, - std::function 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 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 diff --git a/dyld3/shared-cache/ObjC2Abstraction.hpp b/dyld3/shared-cache/ObjC2Abstraction.hpp index 085797c..a0e581b 100644 --- a/dyld3/shared-cache/ObjC2Abstraction.hpp +++ b/dyld3/shared-cache/ObjC2Abstraction.hpp @@ -44,7 +44,7 @@ struct entsize_iterator { entsize_iterator() { } entsize_iterator(const Tlist& list, uint32_t start = 0) - : entsize(list.getEntsize()), index(start), current(&list.get(start)) + : entsize(list.getEntsize()), index(start), current((T*)list.get(start)) { } const entsize_iterator& operator += (ptrdiff_t count) { @@ -152,101 +152,403 @@ public: } }; - -template -class objc_method_list_t; // forward reference - - -template -class objc_method_t { - typedef typename P::uint_t pint_t; - pint_t name; // SEL - pint_t types; // const char * - pint_t imp; // IMP - friend class objc_method_list_t

; -public: - pint_t getName() const { return (pint_t)P::getP(name); } - void setName(pint_t newName) { P::setP(name, newName); } - - struct SortBySELAddress : - public std::binary_function&, - const objc_method_t

&, bool> - { - bool operator() (const objc_method_t

& lhs, - const objc_method_t

& rhs) - { - return lhs.getName() < rhs.getName(); - } - }; -}; - template class objc_method_list_t { + + typedef typename P::uint_t pint_t; + + template + class objc_method_small_t { + typedef typename PtrTy::uint_t pint_t; + int32_t name; // SEL + int32_t types; // const char * + int32_t imp; // IMP + friend class objc_method_list_t; + + objc_method_small_t() = delete; + ~objc_method_small_t() = delete; + objc_method_small_t(const objc_method_small_t& other) = delete; + objc_method_small_t(objc_method_small_t&& other) = delete; + objc_method_small_t& operator=(const objc_method_small_t& other) = delete; + objc_method_small_t& operator=(objc_method_small_t&& other) = delete; + + public: + + pint_t getName(ContentAccessor* cache, bool isOffsetToSel) const { + // We want to return the VM address of the "const char*" our selector + // reference is pointing at. + pint_t* nameRef = (pint_t*)((uint8_t*)&name + name); + if ( isOffsetToSel ) { + // Offset is directly to the SEL, not a selRef + return (pint_t)cache->vmAddrForContent(nameRef); + } else { + return (pint_t)PtrTy::getP(*nameRef); + } + } + // We want to update the selRef we are pointing at with the new content + // We may share the same selRef with other method lists or @SEL expressions, but as + // all of them want the same uniqued selector anyway, its safe to overwrite it here for + // everyone. + void setName(ContentAccessor* cache, pint_t newNameVMAddr, bool isOffsetToSel) { + if ( isOffsetToSel ) { + // Offset is directly to the SEL, not a selRef + void* namePtr = cache->contentForVMAddr(newNameVMAddr); + this->name = (int32_t)(intptr_t)((uint8_t*)namePtr - (uint8_t*)&this->name); + } else { + pint_t* selRef = (pint_t*)((uint8_t*)&name + name); + PtrTy::setP(*selRef, newNameVMAddr); + } + } + // Returns the vmAddr of the types + pint_t getTypes(ContentAccessor* cache) const { + pint_t* typesRef = (pint_t*)((uint8_t*)&types + types); + return (pint_t)cache->vmAddrForContent(typesRef); + } + void setTypes(ContentAccessor* cache, pint_t newTypesVMAddr) { + void* typesPtr = cache->contentForVMAddr(newTypesVMAddr); + this->types = (int32_t)(intptr_t)((uint8_t*)typesPtr - (uint8_t*)&this->types); + } + // Returns the vmAddr of the IMP + pint_t getIMP(ContentAccessor* cache) const { + pint_t* impRef = (pint_t*)((uint8_t*)&imp + imp); + return (pint_t)cache->vmAddrForContent(impRef); + } + void setIMP(ContentAccessor* cache, pint_t newIMPVMAddr) { + void* impPtr = cache->contentForVMAddr(newIMPVMAddr); + this->imp = (int32_t)(intptr_t)((uint8_t*)impPtr - (uint8_t*)&this->imp); + } + + // Swap the contents of this value and other + // This has to recompute all of the relative offsets + void swap(objc_method_small_t* other) { + // Get our targets + uint8_t* ourNameTarget = (uint8_t*)&this->name + this->name; + uint8_t* ourTypesTarget = (uint8_t*)&this->types + this->types; + uint8_t* ourIMPTarget = (uint8_t*)&this->imp + this->imp; + // Get their targets + uint8_t* theirNameTarget = (uint8_t*)&other->name + other->name; + uint8_t* theirTypesTarget = (uint8_t*)&other->types + other->types; + uint8_t* theirIMPTarget = (uint8_t*)&other->imp + other->imp; + // Set our targets + this->name = (int32_t)(intptr_t)(theirNameTarget - (uint8_t*)&this->name); + this->types = (int32_t)(intptr_t)(theirTypesTarget - (uint8_t*)&this->types); + this->imp = (int32_t)(intptr_t)(theirIMPTarget - (uint8_t*)&this->imp); + // Set their targets + other->name = (int32_t)(intptr_t)(ourNameTarget - (uint8_t*)&other->name); + other->types = (int32_t)(intptr_t)(ourTypesTarget - (uint8_t*)&other->types); + other->imp = (int32_t)(intptr_t)(ourIMPTarget - (uint8_t*)&other->imp); + } + + struct SortBySELAddress : + public std::binary_function&, + const objc_method_small_t&, bool> + { + SortBySELAddress(ContentAccessor* cache, bool isOffsetToSel) + : cache(cache), isOffsetToSel(isOffsetToSel) { } + + bool operator() (const objc_method_small_t& lhs, + const objc_method_small_t& rhs) + { + return lhs.getName(cache, isOffsetToSel) < rhs.getName(cache, isOffsetToSel); + } + + ContentAccessor* cache = nullptr; + bool isOffsetToSel = false; + }; + }; + + template + class objc_method_large_t { + typedef typename PtrTy::uint_t pint_t; + pint_t name; // SEL + pint_t types; // const char * + pint_t imp; // IMP + friend class objc_method_list_t; + public: + pint_t getName() const { + return (pint_t)PtrTy::getP(name); + } + void setName(pint_t newName) { + PtrTy::setP(name, newName); + } + pint_t getTypes() const { + return (pint_t)PtrTy::getP(types); + } + void setTypes(pint_t newTypes) { + PtrTy::setP(types, newTypes); + } + pint_t getIMP() const { + return (pint_t)PtrTy::getP(imp); + } + void setIMP(pint_t newIMP) { + PtrTy::setP(imp, newIMP); + } + + struct SortBySELAddress : + public std::binary_function&, + const objc_method_large_t&, bool> + { + bool operator() (const objc_method_large_t& lhs, + const objc_method_large_t& rhs) + { + return lhs.getName() < rhs.getName(); + } + }; + }; + + // Temporary struct to use when sorting small methods as their int32_t offsets can't reach + // from the stack where temporary values are placed, in to the shared cache buffer where the data lives + struct TempMethod { + // Relative methods in the shared cache always use direct offsets to the SEL + // at the point where this is running. That means we don't need to indirect through + // a SEL reference. + pint_t selVMAddr; + pint_t typesVMAddr; + pint_t impVMAddr; + }; + + template + struct SortBySELAddress : + public std::binary_function + { + SortBySELAddress(ContentAccessor* cache) : cache(cache) { } + + bool operator() (const TempMethod& lhs, + const TempMethod& rhs) + { + return lhs.selVMAddr < rhs.selVMAddr; + } + + ContentAccessor* cache = nullptr; + }; + uint32_t entsize; uint32_t count; - objc_method_t

first; + union { + objc_method_small_t

small; + objc_method_large_t

large; + } first; void* operator new (size_t, void* buf) { return buf; } -public: + enum : uint32_t { + // If this is set, the relative method lists name_offset field is an + // offset directly to the SEL, not a SEL ref. + relativeMethodSelectorsAreDirectFlag = 0x40000000, - typedef entsize_iterator, objc_method_list_t

> method_iterator; + // If this is set, then method lists are the new relative format, not + // the old pointer based format + relativeMethodFlag = 0x80000000, + + // The upper 16-bits are all defined to be flags + methodListFlagsMask = 0xFFFF0000 + }; + + uint32_t getFlags() const { + return (P::E::get32(entsize) & methodListFlagsMask); + } + + typedef entsize_iterator, objc_method_list_t

> small_method_iterator; + typedef entsize_iterator, objc_method_list_t

> large_method_iterator; + + small_method_iterator beginSmall() { + assert(usesRelativeMethods()); + return small_method_iterator(*this, 0); + } + small_method_iterator endSmall() { + assert(usesRelativeMethods()); + return small_method_iterator(*this, getCount()); + } + + large_method_iterator beginLarge() { + assert(!usesRelativeMethods()); + return large_method_iterator(*this, 0); + } + large_method_iterator endLarge() { + assert(!usesRelativeMethods()); + return large_method_iterator(*this, getCount()); + } + +public: uint32_t getCount() const { return P::E::get32(count); } - uint32_t getEntsize() const {return P::E::get32(entsize)&~(uint32_t)3;} - - objc_method_t

& get(uint32_t i) const { return *(objc_method_t

*)((uint8_t *)&first + i * getEntsize()); } + uint32_t getEntsize() const { + return P::E::get32(entsize) & ~(uint32_t)3 & ~methodListFlagsMask; + } uint32_t byteSize() const { return byteSizeForCount(getCount(), getEntsize()); } - static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t

)) { - return sizeof(objc_method_list_t

) - sizeof(objc_method_t

) + c*e; + static uint32_t byteSizeForCount(uint32_t c, uint32_t e) { + return sizeof(entsize) + sizeof(count) + c*e; } - method_iterator begin() { return method_iterator(*this, 0); } - method_iterator end() { return method_iterator(*this, getCount()); } - const method_iterator begin() const { return method_iterator(*this, 0); } - const method_iterator end() const { return method_iterator(*this, getCount()); } + bool usesRelativeMethods() const { + return (P::E::get32(entsize) & relativeMethodFlag) != 0; + } - void setFixedUp() { P::E::set32(entsize, getEntsize() | 3); } + void setFixedUp() { + P::E::set32(entsize, getEntsize() | 3 | getFlags()); + } - void getPointers(std::set& pointersToRemove) { - for(method_iterator it = begin(); it != end(); ++it) { - objc_method_t

& entry = *it; - pointersToRemove.insert(&(entry.name)); - pointersToRemove.insert(&(entry.types)); - pointersToRemove.insert(&(entry.imp)); + void setMethodListSelectorsAreDirect() { + P::E::set32(entsize, getEntsize() | getFlags() | relativeMethodSelectorsAreDirectFlag); + } + + void sortMethods(ContentAccessor* cache, pint_t *typelist, bool isOffsetToSel) { + if ( usesRelativeMethods() ) { + // At this point we assume we are using offsets directly to selectors. This + // is so that the TempMethod struct can also use direct offsets and not track the + // SEL reference VMAddrs + assert(isOffsetToSel); + + if ( typelist == nullptr ) { + // This is the case when we are sorting the methods on a class. + // Only protocols have a type list which causes the other sort to be used + // We can't sort the small methods in place as their 32-bit offsets can't reach + // the VM space where the shared cache is being created. Instead create a list + // of large methods and sort those. + + std::vector largeMethods; + for (unsigned i = 0 ; i != count; ++i) { + const objc_method_small_t

* smallMethod = (const objc_method_small_t

*)get(i); + TempMethod largeMethod; + largeMethod.selVMAddr = smallMethod->getName(cache, isOffsetToSel); + largeMethod.typesVMAddr = smallMethod->getTypes(cache); + largeMethod.impVMAddr = smallMethod->getIMP(cache); + largeMethods.push_back(largeMethod); + } + + SortBySELAddress

sorter(cache); + std::stable_sort(largeMethods.begin(), largeMethods.end(), sorter); + + for (unsigned i = 0 ; i != count; ++i) { + const TempMethod& largeMethod = largeMethods[i]; + objc_method_small_t

* smallMethod = (objc_method_small_t

*)get(i); + smallMethod->setName(cache, largeMethod.selVMAddr, isOffsetToSel); + smallMethod->setTypes(cache, largeMethod.typesVMAddr); + smallMethod->setIMP(cache, largeMethod.impVMAddr); + } + +#if 0 + // Check the method lists are sorted + { + typename objc_method_small_t

::SortBySELAddress sorter(cache); + for (uint32_t i = 0; i < getCount(); i++) { + for (uint32_t j = i+1; j < getCount(); j++) { + objc_method_small_t

* mi = (objc_method_small_t

*)get(i); + objc_method_small_t

* mj = (objc_method_small_t

*)get(j); + if ( mi->getName(cache) == mj->getName(cache) ) + continue; + if (! sorter(*mi, *mj)) { + assert(false); + } + } + } + } +#endif + } + else { + typename objc_method_small_t

::SortBySELAddress sorter(cache, isOffsetToSel); + // can't easily use std::stable_sort here + for (uint32_t i = 0; i < getCount(); i++) { + for (uint32_t j = i+1; j < getCount(); j++) { + objc_method_small_t

* mi = (objc_method_small_t

*)get(i); + objc_method_small_t

* mj = (objc_method_small_t

*)get(j); + if (! sorter(*mi, *mj)) { + mi->swap(mj); + if (typelist) std::swap(typelist[i], typelist[j]); + } + } + } + } + } else { + typename objc_method_large_t

::SortBySELAddress sorter; + + if ( typelist == nullptr ) { + // This is the case when we are sorting the methods on a class. + // Only protocols have a type list which causes the other sort to be used + std::stable_sort(beginLarge(), endLarge(), sorter); + } + else { + // can't easily use std::stable_sort here + for (uint32_t i = 0; i < getCount(); i++) { + for (uint32_t j = i+1; j < getCount(); j++) { + objc_method_large_t

* mi = (objc_method_large_t

*)get(i); + objc_method_large_t

* mj = (objc_method_large_t

*)get(j); + if (! sorter(*mi, *mj)) { + std::swap(*mi, *mj); + if (typelist) std::swap(typelist[i], typelist[j]); + } + } + } + } } + // mark method list as sorted + this->setFixedUp(); } - - static void addPointers(uint8_t* methodList, CacheBuilder::ASLR_Tracker& aslrTracker) { - objc_method_list_t

* mlist = (objc_method_list_t

*)methodList; - for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) { - objc_method_t

& entry = *it; - aslrTracker.add(&(entry.name)); - aslrTracker.add(&(entry.types)); - aslrTracker.add(&(entry.imp)); + + pint_t getName(ContentAccessor* cache, uint32_t i, bool isOffsetToSel) { + pint_t name = 0; + if ( usesRelativeMethods() ) { + small_method_iterator it = beginSmall() + i; + objc_method_small_t

& method = *it; + name = method.getName(cache, isOffsetToSel); + } else { + large_method_iterator it = beginLarge() + i; + objc_method_large_t

& method = *it; + name = method.getName(); + } + return name; + } + + void setName(ContentAccessor* cache, uint32_t i, pint_t name, bool isOffsetToSel) { + if ( usesRelativeMethods() ) { + small_method_iterator it = beginSmall() + i; + objc_method_small_t

& method = *it; + method.setName(cache, name, isOffsetToSel); + } else { + large_method_iterator it = beginLarge() + i; + objc_method_large_t

& method = *it; + method.setName(name); } } - static objc_method_list_t

* newMethodList(size_t newCount, uint32_t newEntsize) { - void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); - return new (buf) objc_method_list_t

(newCount, newEntsize); + const char* getStringName(ContentAccessor* cache, uint32_t i, bool isOffsetToSel) { + return (const char*)cache->contentForVMAddr(getName(cache, i, isOffsetToSel)); + } + + pint_t getImp(uint32_t i, ContentAccessor* cache) { + pint_t name = 0; + if ( usesRelativeMethods() ) { + small_method_iterator it = beginSmall() + i; + objc_method_small_t

& method = *it; + name = method.getIMP(cache); + } else { + large_method_iterator it = beginLarge() + i; + objc_method_large_t

& method = *it; + name = method.getIMP(); + } + return name; + } + + void* get(uint32_t i) const { + if ( usesRelativeMethods() ) { + return (void*)(objc_method_small_t

*)((uint8_t *)&first + i * getEntsize()); + } else { + return (void*)(objc_method_large_t

*)((uint8_t *)&first + i * getEntsize()); + } } void operator delete(void * p) { ::free(p); } - objc_method_list_t(uint32_t newCount, - uint32_t newEntsize = sizeof(objc_method_t

)) - : entsize(newEntsize), count(newCount) - { } - private: + // use newMethodList instead void* operator new (size_t); }; @@ -293,7 +595,7 @@ public: uint32_t getEntsize() const { return P::E::get32(entsize); } - objc_ivar_t

& get(pint_t i) const { return *(objc_ivar_t

*)((uint8_t *)&first + i * P::E::get32(entsize)); } + void* get(pint_t i) const { return (void*)(objc_ivar_t

*)((uint8_t *)&first + i * P::E::get32(entsize)); } uint32_t byteSize() const { return byteSizeForCount(getCount(), getEntsize()); @@ -358,7 +660,7 @@ public: uint32_t getEntsize() const { return P::E::get32(entsize); } - objc_property_t

& get(uint32_t i) const { return *(objc_property_t

*)((uint8_t *)&first + i * getEntsize()); } + void* get(uint32_t i) const { return (objc_property_t

*)((uint8_t *)&first + i * getEntsize()); } uint32_t byteSize() const { return byteSizeForCount(getCount(), getEntsize()); @@ -432,6 +734,7 @@ class objc_protocol_t { public: pint_t getIsaVMAddr() const { return (pint_t)P::getP(isa); } void setIsaVMAddr(pint_t newIsa) { P::setP(isa, newIsa); } + void* getISALocation() const { return (void*)&isa; } const char *getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } @@ -491,6 +794,7 @@ public: if (instanceProperties) aslrTracker.add(&instanceProperties); if (extendedMethodTypes) aslrTracker.add(&extendedMethodTypes); if (demangledName) aslrTracker.add(&demangledName); + if (classProperties) aslrTracker.add(&classProperties); } }; @@ -641,12 +945,15 @@ public: objc_class_t

*getIsa(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(isa)); } objc_class_t

*getSuperclass(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(superclass)); } - const pint_t* getSuperClassAddress() const { return &superclass; } // Low bit marks Swift classes. objc_class_data_t

*getData(ContentAccessor* cache) const { return (objc_class_data_t

*)cache->contentForVMAddr(P::getP(data & ~0x3LL)); } + objc_class_t

*getVTable(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(vtable)); } + + pint_t* getVTableAddress() { return &vtable; } + objc_method_list_t

*getMethodList(ContentAccessor* cache) const { objc_class_data_t

* d = getData(cache); return d->getMethodList(cache); @@ -753,9 +1060,9 @@ public: objc_ivar_list_t

*ivars = data->getIvarList(cache); if (ivars) { for (pint_t i = 0; i < ivars->getCount(); i++) { - objc_ivar_t

& ivar = ivars->get(i); + objc_ivar_t

* ivar = (objc_ivar_t

*)ivars->get(i); //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache)); - ivarVisitor.visitIvar(cache, header, cls, &ivar); + ivarVisitor.visitIvar(cache, header, cls, ivar); } } else { //fprintf(stderr, "no ivars\n"); @@ -768,14 +1075,20 @@ public: } }; +enum class ClassWalkerMode { + ClassesOnly, + ClassAndMetaclasses, +}; + // Call visitor.visitClass() on every class. template class ClassWalker { typedef typename P::uint_t pint_t; V& _visitor; + ClassWalkerMode _mode; public: - ClassWalker(V& visitor) : _visitor(visitor) { } + ClassWalker(V& visitor, ClassWalkerMode mode = ClassWalkerMode::ClassesOnly) : _visitor(visitor), _mode(mode) { } void walk(ContentAccessor* cache, const macho_header

* header) { @@ -783,8 +1096,14 @@ public: for (pint_t i = 0; i < classList.count(); i++) { objc_class_t

* cls = classList.get(i); - //fprintf(stderr, "visiting class: %s\n", cls->getName(cache)); - if (cls) _visitor.visitClass(cache, header, cls); + if (cls) { + //fprintf(stderr, "visiting class: %s\n", cls->getName(cache)); + _visitor.visitClass(cache, header, cls); + if (_mode == ClassWalkerMode::ClassAndMetaclasses) { + //fprintf(stderr, "visiting metaclass: %s\n", cls->getIsa(cache)->getName(cache)); + _visitor.visitClass(cache, header, cls->getIsa(cache)); + } + } } } }; @@ -900,10 +1219,10 @@ public: objc_class_t

*cls = classes.get(i); objc_method_list_t

*mlist; if ((mlist = cls->getMethodList(cache))) { - mVisitor.visitMethodList(mlist); + mVisitor.visitMethodList(cache, mlist); } if ((mlist = cls->getIsa(cache)->getMethodList(cache))) { - mVisitor.visitMethodList(mlist); + mVisitor.visitMethodList(cache, mlist); } } @@ -914,10 +1233,10 @@ public: objc_category_t

*cat = cats.get(i); objc_method_list_t

*mlist; if ((mlist = cat->getInstanceMethods(cache))) { - mVisitor.visitMethodList(mlist); + mVisitor.visitMethodList(cache, mlist); } if ((mlist = cat->getClassMethods(cache))) { - mVisitor.visitMethodList(mlist); + mVisitor.visitMethodList(cache, mlist); } } @@ -930,19 +1249,19 @@ public: pint_t *typelist = proto->getExtendedMethodTypes(cache); if ((mlist = proto->getInstanceMethods(cache))) { - mVisitor.visitProtocolMethodList(mlist, typelist); + mVisitor.visitProtocolMethodList(cache, mlist, typelist); if (typelist) typelist += mlist->getCount(); } if ((mlist = proto->getClassMethods(cache))) { - mVisitor.visitProtocolMethodList(mlist, typelist); + mVisitor.visitProtocolMethodList(cache, mlist, typelist); if (typelist) typelist += mlist->getCount(); } if ((mlist = proto->getOptionalInstanceMethods(cache))) { - mVisitor.visitProtocolMethodList(mlist, typelist); + mVisitor.visitProtocolMethodList(cache, mlist, typelist); if (typelist) typelist += mlist->getCount(); } if ((mlist = proto->getOptionalClassMethods(cache))) { - mVisitor.visitProtocolMethodList(mlist, typelist); + mVisitor.visitProtocolMethodList(cache, mlist, typelist); if (typelist) typelist += mlist->getCount(); } } @@ -960,25 +1279,36 @@ class SelectorOptimizer { std::set selectorRefVMAddrs; friend class MethodListWalker >; - void visitMethodList(objc_method_list_t

*mlist) + void visitMethodList(ContentAccessor* cache, objc_method_list_t

*mlist) { // Gather selectors. Update method names. for (uint32_t m = 0; m < mlist->getCount(); m++) { - pint_t oldValue = mlist->get(m).getName(); + // Read names as relative offsets to selRefs + pint_t oldValue = mlist->getName(cache, m, false); pint_t newValue = mVisitor.visit(oldValue); - mlist->get(m).setName(newValue); + // And write names as relative offsets to SELs themselves. + mlist->setName(cache, m, newValue, true); } + // Set this method list as now being relative offsets directly to the selector string + if ( mlist->usesRelativeMethods() ) + mlist->setMethodListSelectorsAreDirect(); + // Do not setFixedUp: the methods are not yet sorted. } - void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *types) + void visitProtocolMethodList(ContentAccessor* cache, objc_method_list_t

*mlist, pint_t *types) { - visitMethodList(mlist); + visitMethodList(cache, mlist); } public: - SelectorOptimizer(V& visitor) : mVisitor(visitor) { } + SelectorOptimizer(V& visitor, bool& relativeMethodListSelectorsAreDirect) : mVisitor(visitor) { + // This pass requires that relative method lists are initially indirected via the selector + // ref. After this pass runs we'll use relative offsets to the selectors themselves + assert(!relativeMethodListSelectorsAreDirect); + relativeMethodListSelectorsAreDirect = true; + } void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) { mVisitor.visitCoalescedStrings(coalescedText); @@ -1160,37 +1490,27 @@ class MethodListSorter { typedef typename P::uint_t pint_t; uint32_t _optimized; + bool _isOffsetToSel; friend class MethodListWalker >; - void visitMethodList(objc_method_list_t

*mlist) - { - typename objc_method_t

::SortBySELAddress sorter; - std::stable_sort(mlist->begin(), mlist->end(), sorter); - mlist->setFixedUp(); + + void sortMethodList(ContentAccessor* cache, objc_method_list_t

*mlist, pint_t *typelist) { + mlist->sortMethods(cache, typelist, _isOffsetToSel); _optimized++; } - void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *typelist) + void visitMethodList(ContentAccessor* cache, objc_method_list_t

*mlist) { - typename objc_method_t

::SortBySELAddress sorter; - // can't easily use std::stable_sort here - for (uint32_t i = 0; i < mlist->getCount(); i++) { - for (uint32_t j = i+1; j < mlist->getCount(); j++) { - objc_method_t

& mi = mlist->get(i); - objc_method_t

& mj = mlist->get(j); - if (! sorter(mi, mj)) { - std::swap(mi, mj); - if (typelist) std::swap(typelist[i], typelist[j]); - } - } - } + sortMethodList(cache, mlist, nullptr); + } - mlist->setFixedUp(); - _optimized++; + void visitProtocolMethodList(ContentAccessor* cache, objc_method_list_t

*mlist, pint_t *typelist) + { + sortMethodList(cache, mlist, typelist); } public: - MethodListSorter() : _optimized(0) { } + MethodListSorter(bool isOffsetToSel) : _optimized(0), _isOffsetToSel(isOffsetToSel) { } size_t optimized() const { return _optimized; } diff --git a/dyld3/shared-cache/OptimizerBranches.cpp b/dyld3/shared-cache/OptimizerBranches.cpp index 67c31c2..b62747c 100644 --- a/dyld3/shared-cache/OptimizerBranches.cpp +++ b/dyld3/shared-cache/OptimizerBranches.cpp @@ -52,11 +52,13 @@ static const bool verbose = false; template class StubOptimizer { public: - StubOptimizer(const DyldSharedCache* cache, macho_header

* mh, Diagnostics& diags); + StubOptimizer(int64_t cacheSlide, uint64_t cacheUnslidAddr, + const std::string& archName, macho_header

* mh, + const char* dylibID, Diagnostics& diags); void buildStubMap(const std::unordered_set& neverStubEliminate); void optimizeStubs(); void optimizeCallSites(std::unordered_map& targetAddrToOptStubAddr); - const char* installName() { return _installName; } + const char* dylibID() { return _dylibID; } const uint8_t* exportsTrie() { if ( _dyldInfo != nullptr ) return &_linkeditBias[_dyldInfo->export_off()]; @@ -79,7 +81,7 @@ public: uint32_t _branchToReUsedOptimizedStubCount = 0; private: - Diagnostics _diagnostics; + Diagnostics& _diagnostics; typedef std::function CallSiteHandler; typedef typename P::uint_t pint_t; @@ -107,6 +109,7 @@ private: int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr); uint32_t setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, int32_t displacement, bool targetIsThumb); + uint32_t cpuSubtype() { return ((dyld3::MachOFile*)_mh)->maskedCpuSubtype(); } struct AddressAndName { pint_t targetVMAddr; const char* targetName; }; @@ -120,11 +123,10 @@ private: macho_header

* _mh; int64_t _cacheSlide = 0; uint64_t _cacheUnslideAddr = 0; - bool _chainedFixups = false; uint32_t _linkeditSize = 0; uint64_t _linkeditAddr = 0; const uint8_t* _linkeditBias = nullptr; - const char* _installName = nullptr; + const char* _dylibID = nullptr; const macho_symtab_command

* _symTabCmd = nullptr; const macho_dysymtab_command

* _dynSymTabCmd = nullptr; const macho_dyld_info_command

* _dyldInfo = nullptr; @@ -144,16 +146,14 @@ private: template -StubOptimizer

::StubOptimizer(const DyldSharedCache* cache, macho_header

* mh, Diagnostics& diags) -: _mh(mh), _diagnostics(diags) +StubOptimizer

::StubOptimizer(int64_t cacheSlide, uint64_t cacheUnslidAddr, + const std::string& archName, + macho_header

* mh, const char* dylibID, + Diagnostics& diags) + : _mh(mh), _dylibID(dylibID), + _cacheSlide(cacheSlide), _cacheUnslideAddr(cacheUnslidAddr), + _diagnostics(diags) { - _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

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); const uint32_t cmd_count = mh->ncmds(); macho_segment_command

* segCmd; @@ -161,9 +161,6 @@ StubOptimizer

::StubOptimizer(const DyldSharedCache* cache, macho_header

* m const macho_load_command

* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd()) { - case LC_ID_DYLIB: - _installName = ((macho_dylib_command

*)cmd)->name(); - break; case LC_SYMTAB: _symTabCmd = (macho_symtab_command

*)cmd; break; @@ -221,17 +218,17 @@ uint32_t StubOptimizer

::lazyPointerAddrFromArmStub(const uint8_t* stubInstruc int32_t stubData = E::get32(*(uint32_t*)(stubInstructions+12)); if ( stubInstr1 != 0xe59fc004 ) { _diagnostics.warning("first instruction of stub (0x%08X) is not 'ldr ip, pc + 12' for stub at addr 0x%0llX in %s", - stubInstr1, (uint64_t)stubVMAddr, _installName); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } if ( stubInstr2 != 0xe08fc00c ) { _diagnostics.warning("second instruction of stub (0x%08X) is not 'add ip, pc, ip' for stub at addr 0x%0llX in %s", - stubInstr1, (uint64_t)stubVMAddr, _installName); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } if ( stubInstr3 != 0xe59cf000 ) { _diagnostics.warning("third instruction of stub (0x%08X) is not 'ldr pc, [ip]' for stub at addr 0x%0llX in %s", - stubInstr1, (uint64_t)stubVMAddr, _installName); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } return stubVMAddr + 12 + stubData; @@ -244,7 +241,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstr uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { _diagnostics.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s", - stubInstr1, (uint64_t)stubVMAddr, _installName); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29); @@ -253,7 +250,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstr uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4)); if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) { _diagnostics.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s", - stubInstr2, (uint64_t)stubVMAddr, _installName); + stubInstr2, (uint64_t)stubVMAddr, _dylibID); return 0; } uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF); @@ -267,7 +264,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64_32Stub(const uint8_t* stubIn uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { _diagnostics.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s", - stubInstr1, (uint64_t)stubVMAddr, _installName); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29); @@ -276,7 +273,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64_32Stub(const uint8_t* stubIn uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4)); if ( (stubInstr2 & 0xFFC003FF) != 0xB9400210 ) { _diagnostics.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s", - stubInstr2, (uint64_t)stubVMAddr, _installName); + stubInstr2, (uint64_t)stubVMAddr, _dylibID); return 0; } uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF); @@ -294,7 +291,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst // 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); + stubInstr1, (uint64_t)stubVMAddr, _dylibID); return 0; } int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29); @@ -305,7 +302,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst 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); + stubInstr2, (uint64_t)stubVMAddr, _dylibID); return 0; } uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10); @@ -314,7 +311,7 @@ uint64_t StubOptimizer

::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst 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); + stubInstr2, (uint64_t)stubVMAddr, _dylibID); return 0; } return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue; @@ -356,19 +353,19 @@ void StubOptimizer

::buildStubMap(const std::unordered_set& never default: if ( symbolIndex >= _symTabCmd->nsyms() ) { _diagnostics.warning("symbol index out of range (%d of %d) for stub at addr 0x%0llX in %s", - symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _installName); + symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _dylibID); continue; } const macho_nlist

* sym = &symbolTable[symbolIndex]; uint32_t stringOffset = sym->n_strx(); if ( stringOffset > _symTabCmd->strsize() ) { _diagnostics.warning("symbol string offset out of range (%u of %u) for stub at addr 0x%0llX in %s", - stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _installName); + stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _dylibID); continue; } const char* symName = &symbolStrings[stringOffset]; if ( neverStubEliminate.count(symName) ) { - //fprintf(stderr, "stubVMAddr=0x%llX, not bypassing stub to %s in %s because target is interposable\n", (uint64_t)stubVMAddr, symName, _installName); + //fprintf(stderr, "stubVMAddr=0x%llX, not bypassing stub to %s in %s because target is interposable\n", (uint64_t)stubVMAddr, symName, _dylibID); _stubsLeftInterposable++; continue; } @@ -376,19 +373,19 @@ void StubOptimizer

::buildStubMap(const std::unordered_set& never pint_t targetLPAddr = 0; switch ( _mh->cputype() ) { case CPU_TYPE_ARM64: - case CPU_TYPE_ARM64_32: #if SUPPORT_ARCH_arm64e - if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E) + if (cpuSubtype() == CPU_SUBTYPE_ARM64E) targetLPAddr = (pint_t)lazyPointerAddrFromArm64eStub(stubInstrs, stubVMAddr); else -#endif -#if SUPPORT_ARCH_arm64_32 - if (_mh->cputype() == CPU_TYPE_ARM64_32) - targetLPAddr = (pint_t)lazyPointerAddrFromArm64_32Stub(stubInstrs, stubVMAddr); - else #endif targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr); break; +#if SUPPORT_ARCH_arm64_32 + case CPU_TYPE_ARM64_32: + if (cpuSubtype() == CPU_TYPE_ARM64_32) + targetLPAddr = (pint_t)lazyPointerAddrFromArm64_32Stub(stubInstrs, stubVMAddr); + break; +#endif case CPU_TYPE_ARM: targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr); break; @@ -415,40 +412,29 @@ void StubOptimizer

::buildStubMap(const std::unordered_set& never break; default: lpValue = (pint_t)P::getP(lpContent[j]); - - // Fixup threaded rebase/bind - if ( _chainedFixups ) { - dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr; - ptr.raw64 = lpValue; - assert(ptr.arm64e.authRebase.bind == 0); - if ( ptr.arm64e.authRebase.auth ) { - lpValue = (pint_t)(_cacheUnslideAddr + ptr.arm64e.authRebase.target); - } - else { - lpValue = (pint_t)ptr.arm64e.unpackTarget(); - } - } - lpVMAddr = (pint_t)sect->addr() + j * sizeof(pint_t); if ( symbolIndex >= _symTabCmd->nsyms() ) { _diagnostics.warning("symbol index out of range (%d of %d) for lazy pointer at addr 0x%0llX in %s", - symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _installName); + symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _dylibID); continue; } const macho_nlist

* sym = &symbolTable[symbolIndex]; uint32_t stringOffset = sym->n_strx(); if ( stringOffset > _symTabCmd->strsize() ) { _diagnostics.warning("symbol string offset out of range (%u of %u) for lazy pointer at addr 0x%0llX in %s", - stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _installName); + stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _dylibID); continue; } const char* symName = &symbolStrings[stringOffset]; if ( (lpValue > textSegStartAddr) && (lpValue< textSegEndAddr) ) { - //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)lpVMAddr, symName, _installName); + //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)lpVMAddr, symName, _dylibID); } else if ( (sizeof(pint_t) == 8) && ((lpValue % 4) != 0) ) { - _diagnostics.warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) in %s", - (uint64_t)lpVMAddr, (uint64_t)lpValue, _installName); + // Only warn on lazy pointers which correspond to call targets + if ( sectionType == S_LAZY_SYMBOL_POINTERS ) { + _diagnostics.warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) for symbol '%s' in %s", + (uint64_t)lpVMAddr, (uint64_t)lpValue, symName, _dylibID); + } } else { _lpAddrToTargetAddr[lpVMAddr] = lpValue; @@ -473,7 +459,7 @@ void StubOptimizer

::forEachCallSiteToAStub(CallSiteHandler handler) const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) { - _diagnostics.error("malformed split seg info in %s", _installName); + _diagnostics.error("malformed split seg info in %s", _dylibID); return; } @@ -497,7 +483,7 @@ void StubOptimizer

::forEachCallSiteToAStub(CallSiteHandler handler) for (uint64_t k=0; k < fromOffsetCount; ++k) { uint64_t kind = read_uleb128(p, infoEnd); if ( kind > 13 ) { - _diagnostics.error("bad kind (%llu) value in %s\n", kind, _installName); + _diagnostics.error("bad kind (%llu) value in %s\n", kind, _dylibID); } uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); uint64_t fromSectionOffset = 0; @@ -551,7 +537,7 @@ template uint32_t StubOptimizer

::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, int32_t displacement, bool targetIsThumb) { if ( (displacement > 16777214) || (displacement < (-16777216)) ) { - _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _installName); + _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _dylibID); return 0; } bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); @@ -571,12 +557,12 @@ uint32_t StubOptimizer

::setDisplacementInThumbBranch(uint32_t instruction, u } else if (is_b) { if ( !targetIsThumb ) { - _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName); + _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _dylibID); return 0; } } else { - _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _installName); + _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _dylibID); return 0; } uint32_t s = (uint32_t)(displacement >> 24) & 0x1; @@ -602,13 +588,13 @@ void StubOptimizer

::optimizeArmCallSites(std::unordered_map::optimizeCallSites(std::unordered_map& case CPU_TYPE_ARM64: optimizeArm64CallSites(targetAddrToOptStubAddr); #if SUPPORT_ARCH_arm64e - if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E) + if (cpuSubtype() == CPU_SUBTYPE_ARM64E) optimizeArm64eStubs(); else #endif @@ -891,38 +877,47 @@ void StubOptimizer

::optimizeCallSites(std::unordered_map& _diagnostics.verbose("dylib has %6u BLs to %4u stubs. Changed %5u, %5u, %5u BLs to use direct branch, optimized stub, neighbor's optimized stub. " "%5u stubs left interposable, %4u stubs optimized. path=%s\n", _branchToStubCount, _stubCount, _branchOptimizedToDirectCount, _branchToOptimizedStubCount, _branchToReUsedOptimizedStubCount, - _stubsLeftInterposable, _stubOptimizedCount, _installName); + _stubsLeftInterposable, _stubOptimizedCount, _dylibID); } } template -void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unordered_map& targetAddrToOptStubAddr, - const char* const neverStubEliminateDylibs[], const char* const neverStubEliminateSymbols[], +void bypassStubs(std::vector> images, + const std::string& archName, + int64_t cacheSlide, uint64_t cacheUnslidAddr, + const DyldSharedCache* dyldCache, + const char* const neverStubEliminateSymbols[], Diagnostics& diags) { + std::unordered_map targetAddrToOptStubAddr; diags.verbose("Stub elimination optimization:\n"); // construct a StubOptimizer for each image __block std::vector*> optimizers; - cache->forEachImage(^(const mach_header* mh, const char* installName) { - optimizers.push_back(new StubOptimizer

(cache, (macho_header

*)mh, diags)); - }); + for (std::pair image : images) { + optimizers.push_back(new StubOptimizer

(cacheSlide, cacheUnslidAddr, archName, + (macho_header

*)image.first, image.second, + diags)); + } // build set of functions to never stub-eliminate because tools may need to override them std::unordered_set neverStubEliminate; for (const char* const* p=neverStubEliminateSymbols; *p != nullptr; ++p) { neverStubEliminate.insert(*p); } - for (const char* const* d=neverStubEliminateDylibs; *d != nullptr; ++d) { + +#if !BUILDING_APP_CACHE_UTIL + // Customer shared caches support overriding libdispatch + if ( dyldCache != nullptr ) { for (StubOptimizer

* op : optimizers) { - if ( strcmp(op->installName(), *d) == 0 ) { + if ( dyldCache->isOverridablePath(op->dylibID()) ) { // add all exports const uint8_t* exportsStart = op->exportsTrie(); const uint8_t* exportsEnd = exportsStart + op->exportsTrieSize(); std::vector exports; if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) { - diags.error("malformed exports trie in %s", *d); + diags.error("malformed exports trie in %s", op->dylibID()); return; } for(const ExportInfoTrie::Entry& entry : exports) { @@ -931,6 +926,7 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unord } } } +#endif // build maps of stubs-to-lp and lp-to-target for (StubOptimizer

* op : optimizers) @@ -954,20 +950,32 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unord delete op; } -void CacheBuilder::optimizeAwayStubs() +void CacheBuilder::optimizeAwayStubs(const std::vector>& images, + int64_t cacheSlide, uint64_t cacheUnslidAddr, + const DyldSharedCache* dyldCache, + const char* const neverStubEliminateSymbols[]) { std::unordered_map targetAddrToOptStubAddr; - - DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; - std::string archName = dyldCache->archName(); + std::string archName = _options.archs->name(); #if SUPPORT_ARCH_arm64_32 - if ( startsWith(archName, "arm64_32") ) - bypassStubs >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics); - else + if ( startsWith(archName, "arm64_32") ) { + bypassStubs >(images, archName, cacheSlide, cacheUnslidAddr, + dyldCache, neverStubEliminateSymbols, + _diagnostics); + return; + } #endif - if ( startsWith(archName, "arm64") ) - bypassStubs >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics); - else if ( archName == "armv7k" ) - bypassStubs>(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics); + if ( startsWith(archName, "arm64") ) { + bypassStubs >(images, archName, cacheSlide, cacheUnslidAddr, + dyldCache, neverStubEliminateSymbols, + _diagnostics); + return; + } + if ( archName == "armv7k" ) { + bypassStubs >(images, archName, cacheSlide, cacheUnslidAddr, + dyldCache, neverStubEliminateSymbols, + _diagnostics); + return; + } // no stub optimization done for other arches } diff --git a/dyld3/shared-cache/OptimizerLinkedit.cpp b/dyld3/shared-cache/OptimizerLinkedit.cpp index 048522b..98e9333 100644 --- a/dyld3/shared-cache/OptimizerLinkedit.cpp +++ b/dyld3/shared-cache/OptimizerLinkedit.cpp @@ -53,7 +53,12 @@ class SortedStringPool public: // add a string and symbol table entry index to be updated later void add(uint32_t symbolIndex, const char* symbolName) { - _map[symbolName].push_back(symbolIndex); + _map[symbolName].push_back({ symbolIndex, false }); + } + + // add a string and symbol table entry index to be updated later + void addIndirect(uint32_t symbolIndex, const char* symbolName) { + _map[symbolName].push_back({ symbolIndex, true }); } // copy sorted strings to buffer and update all symbol's string offsets @@ -66,8 +71,13 @@ public: // append string to pool strcpy(&dstStringPool[poolOffset], symName.c_str()); // set each string offset of each symbol using it - for (uint32_t symbolIndex : entry.second) { - symbolTable[symbolIndex].set_n_strx(poolOffset); + for (std::pair symbolIndexAndIndirect : entry.second) { + if ( symbolIndexAndIndirect.second ) { + // Indirect + symbolTable[symbolIndexAndIndirect.first].set_n_value(poolOffset); + } else { + symbolTable[symbolIndexAndIndirect.first].set_n_strx(poolOffset); + } } poolOffset += symName.size() + 1; } @@ -85,7 +95,7 @@ public: private: - std::map> _map; + std::map>> _map; }; @@ -103,11 +113,12 @@ struct LocalSymbolInfo template class LinkeditOptimizer { public: - LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diag); + LinkeditOptimizer(const void* containerBuffer, macho_header

* mh, const char* dylibID, + Diagnostics& diag); uint32_t linkeditSize() { return _linkeditSize; } uint64_t linkeditAddr() { return _linkeditAddr; } - const char* installName() { return _installName; } + const char* dylibID() { return _dylibID; } void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); @@ -124,6 +135,9 @@ public: uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount, uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize); + typedef CacheBuilder::DylibStripMode DylibStripMode; + void setStripMode(DylibStripMode stripMode); + macho_header

* machHeader() { return _mh; } const std::vector getDownwardDependents() { return _downDependentPaths; } const std::vector getAllDependents() { return _allDependentPaths; } @@ -138,8 +152,11 @@ public: const std::vector*>& segCmds() { return _segCmds; } - static void optimizeLinkedit(CacheBuilder& builder); - static void mergeLinkedits(CacheBuilder& builder, std::vector*>& optimizers); + static void optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer, + CacheBuilder::UnmappedRegion* localSymbolsRegion, + const std::vector>& images); + static void mergeLinkedits(CacheBuilder& builder, CacheBuilder::UnmappedRegion* localSymbolsRegion, + std::vector*>& optimizers); private: @@ -147,12 +164,12 @@ private: typedef typename P::E E; macho_header

* _mh; - void* _cacheBuffer; + const void* _containerBuffer; Diagnostics& _diagnostics; uint32_t _linkeditSize = 0; uint64_t _linkeditAddr = 0; const uint8_t* _linkeditBias = nullptr; - const char* _installName = nullptr; + const char* _dylibID = nullptr; macho_symtab_command

* _symTabCmd = nullptr; macho_dysymtab_command

* _dynSymTabCmd = nullptr; macho_dyld_info_command

* _dyldInfo = nullptr; @@ -182,348 +199,14 @@ private: uint32_t _newDataInCodeOffset = 0; uint32_t _newIndirectSymbolTableOffset = 0; uint64_t _dyldSectionAddr = 0; -}; - - - -template -class AcceleratorTables { -public: - AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers); - - uint32_t totalSize() const; - void copyTo(uint8_t* buffer); - -private: - typedef typename P::E E; - - struct NodeChain; - - struct DepNode { - std::vector _dependents; - unsigned _depth; - const char* _installName; - - DepNode() : _depth(0), _installName(nullptr) { } - void computeDepth(); - static void verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set& visitedNodes, const std::vector& from); - }; - - struct NodeChain { - NodeChain* prev; - DepNode* node; - }; - - std::unordered_map*, DepNode> _depDAG; - std::vector _extraInfo; - std::vector _trieBytes; - std::vector _reExportArray; - std::vector _dependencyArray; - std::vector _bottomUpArray; - std::vector _initializers; - std::vector _dofSections; - std::vector _rangeTable; - std::unordered_map*, uint32_t> _machHeaderToImageIndex; - std::unordered_map*> _dylibPathToMachHeader; - std::unordered_map*, LinkeditOptimizer

*> _machHeaderToOptimizer; - dyld_cache_accelerator_info _acceleratorInfoHeader; + DylibStripMode _stripMode = DylibStripMode::stripAll; }; template -void AcceleratorTables

::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables

::DepNode* target, struct AcceleratorTables

::NodeChain& chain, Diagnostics& diag, - std::unordered_set& visitedNodes, const std::vector::DepNode*>& from) { - for (DepNode* node : from) { - bool foundCycle = (node == target); - for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) { - if ( c->node == target ) { - foundCycle = true; - break; - } - } - if ( foundCycle ) { - NodeChain* chp = &chain; - std::string msg = std::string("found cycle for ") + target->_installName; - while (chp != nullptr) { - msg = msg + "\n " + chp->node->_installName; - chp = chp->prev; - } - diag.warning("%s", msg.c_str()); - return; - } - - if ( visitedNodes.count(node) ) - continue; - visitedNodes.insert(node); - NodeChain nextChain; - nextChain.prev = &chain; - nextChain.node = node; - verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents); - } -} - -const uint16_t kBranchIslandDylibIndex = 0x7FFF; - -template -AcceleratorTables

::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers) -{ - // build table mapping tables to map between mach_header, index, and optimizer - for ( LinkeditOptimizer

* op : optimizers ) { - _machHeaderToOptimizer[op->machHeader()] = op; - } - const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset); - uint64_t cacheStartAddress = mappings[0].address; - const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset); - for (unsigned i=0; i < cache->header.imagesCount; ++i) { - uint64_t segCacheFileOffset = images[i].address - cacheStartAddress; - macho_header

* mhMapped = (macho_header

*)((uint8_t*)cache+segCacheFileOffset); - const char* path = (char*)cache + images[i].pathFileOffset; - _dylibPathToMachHeader[path] = mhMapped; - // don't add alias entries (path offset in pool near start of cache) to header->index map - if ( images[i].pathFileOffset > segCacheFileOffset ) - _machHeaderToImageIndex[mhMapped] = i; - } - - - // build DAG of image dependencies - for (LinkeditOptimizer

* op : optimizers) { - _depDAG[op->machHeader()]._installName = op->installName(); - } - for (LinkeditOptimizer

* op : optimizers) { - DepNode& node = _depDAG[op->machHeader()]; - for (const char* depPath : op->getDownwardDependents()) { - macho_header

* depMH = _dylibPathToMachHeader[depPath]; - if ( depMH != nullptr ) { - DepNode* depNode = &_depDAG[depMH]; - node._dependents.push_back(depNode); - } - } - } - - // check for cycles in DAG - for (auto& entry : _depDAG) { - DepNode* node = &entry.second; - NodeChain chain; - chain.prev = nullptr; - chain.node = node; - std::unordered_set visitedNodes; - DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents); - } - - // compute depth for each DAG node - for (auto& entry : _depDAG) { - entry.second.computeDepth(); - } - - // build sorted (bottom up) list of images - std::vector*> sortedMachHeaders; - sortedMachHeaders.reserve(optimizers.size()); - for (LinkeditOptimizer

* op : optimizers) { - if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 ) - sortedMachHeaders.push_back(op->machHeader()); - else - _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex; - } - std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(), - [&](macho_header

* lmh, macho_header

* rmh) -> bool { - if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth ) - return (_depDAG[lmh]._depth < _depDAG[rmh]._depth); - else - return (lmh < rmh); - }); - - // build zeroed array of extra infos - dyld_cache_image_info_extra emptyExtra; - emptyExtra.exportsTrieAddr = 0; - emptyExtra.weakBindingsAddr = 0; - emptyExtra.exportsTrieSize = 0; - emptyExtra.weakBindingsSize = 0; - emptyExtra.dependentsStartArrayIndex = 0; - emptyExtra.reExportsStartArrayIndex = 0; - _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra); - - //for ( macho_header

* mh : sortedMachHeaders ) { - // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName()); - //} - - // build dependency table - _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies" - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned index = _machHeaderToImageIndex[mh]; - auto depPaths = op->getAllDependents(); - if ( depPaths.empty() ) { - _extraInfo[index].dependentsStartArrayIndex = 0; - } - else { - _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size(); - auto downPaths = op->getDownwardDependents(); - for (const char* depPath : depPaths) { - macho_header

* depMH = _dylibPathToMachHeader[depPath]; - uint16_t depIndex = _machHeaderToImageIndex[depMH]; - if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end()) - depIndex |= 0x8000; - _dependencyArray.push_back(depIndex); - } - _dependencyArray.push_back(0xFFFF); // mark end of list - } - } - - // build re-exports table - _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports" - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned index = _machHeaderToImageIndex[mh]; - auto reExPaths = op->getReExportPaths(); - if ( reExPaths.empty() ) { - _extraInfo[index].reExportsStartArrayIndex = 0; - } - else { - _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size(); - for (const char* reExPath : reExPaths) { - macho_header

* reExMH = _dylibPathToMachHeader[reExPath]; - uint32_t reExIndex = _machHeaderToImageIndex[reExMH]; - _reExportArray.push_back(reExIndex); - } - _reExportArray.push_back(0xFFFF); // mark end of list - } - } - - // build ordered list of initializers - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned index = _machHeaderToImageIndex[mh]; - _bottomUpArray.push_back(index); - for (uint64_t initializer : op->initializerAddresses()) { - //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); - dyld_cache_accelerator_initializer entry; - entry.functionOffset = (uint32_t)(initializer-cacheStartAddress); - entry.imageIndex = _machHeaderToImageIndex[mh]; - _initializers.push_back(entry); - } - } - - // build ordered list of DOF sections - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - assert(op != NULL); - unsigned imageIndex = _machHeaderToImageIndex[mh]; - for (auto& sect : op->dofSections()) { - //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); - dyld_cache_accelerator_dof entry; - entry.sectionAddress = sect->addr(); - entry.sectionSize = (uint32_t)sect->size(); - entry.imageIndex = imageIndex; - _dofSections.push_back(entry); - } - } - - - // register exports trie and weak binding info in each dylib with image extra info - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned index = _machHeaderToImageIndex[mh]; - _extraInfo[index].exportsTrieAddr = op->exportsTrieLinkEditOffset() + linkeditStartAddr; - _extraInfo[index].exportsTrieSize = op->exportsTrieLinkEditSize(); - _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr; - _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize(); - } - - // record location of __DATA/__dyld section in libdyld.dylib - macho_header

* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"]; - LinkeditOptimizer

* libdyldOp = _machHeaderToOptimizer[libdyldMH]; - uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress(); - - // build range table for fast address->image lookups - for (macho_header

* mh : sortedMachHeaders) { - LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; - unsigned imageIndex = _machHeaderToImageIndex[mh]; - for (const macho_segment_command

* segCmd : op->segCmds()) { - if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) - continue; - dyld_cache_range_entry entry; - entry.startAddress = segCmd->vmaddr(); - entry.size = (uint32_t)segCmd->vmsize(); - entry.imageIndex = imageIndex; - _rangeTable.push_back(entry); - } - } - std::sort(_rangeTable.begin(), _rangeTable.end(), - [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool { - return (lRange.startAddress < rRange.startAddress); - }); - - // build trie that maps install names to image index - std::vector dylibEntrys; - for (auto &x : _dylibPathToMachHeader) { - const std::string& path = x.first; - unsigned index = _machHeaderToImageIndex[x.second]; - dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index))); - } - DylibIndexTrie dylibsTrie(dylibEntrys); - dylibsTrie.emit(_trieBytes); - while ( (_trieBytes.size() % 4) != 0 ) - _trieBytes.push_back(0); - - // fill out header - _acceleratorInfoHeader.version = 1; - _acceleratorInfoHeader.imageExtrasCount = (uint32_t)_extraInfo.size(); - _acceleratorInfoHeader.imagesExtrasOffset = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra); - _acceleratorInfoHeader.bottomUpListOffset = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra); - _acceleratorInfoHeader.dylibTrieOffset = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t); - _acceleratorInfoHeader.dylibTrieSize = (uint32_t)_trieBytes.size(); - _acceleratorInfoHeader.initializersOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer); - _acceleratorInfoHeader.initializersCount = (uint32_t)_initializers.size(); - _acceleratorInfoHeader.dofSectionsOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer); - _acceleratorInfoHeader.dofSectionsCount = (uint32_t)_dofSections.size(); - _acceleratorInfoHeader.reExportListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof); - _acceleratorInfoHeader.reExportCount = (uint32_t)_reExportArray.size(); - _acceleratorInfoHeader.depListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t); - _acceleratorInfoHeader.depListCount = (uint32_t)_dependencyArray.size(); - _acceleratorInfoHeader.rangeTableOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry); - _acceleratorInfoHeader.rangeTableCount = (uint32_t)_rangeTable.size(); - _acceleratorInfoHeader.dyldSectionAddr = dyldSectionAddr; -} - - -template -void AcceleratorTables

::DepNode::computeDepth() -{ - if ( _depth != 0 ) - return; - _depth = 1; - for (DepNode* node : _dependents) { - node->computeDepth(); - if ( node->_depth >= _depth ) - _depth = node->_depth + 1; - } -} - -template -uint32_t AcceleratorTables

::totalSize() const -{ - return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14); -} - -template -void AcceleratorTables

::copyTo(uint8_t* buffer) -{ - memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info)); - memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra)); - memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t)); - memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer)); - memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t)); - memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof)); - memcpy(&buffer[_acceleratorInfoHeader.depListOffset], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t)); - memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry)); - memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset], &_trieBytes[0], _trieBytes.size()); -} - - - -template -LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diag) -: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag) +LinkeditOptimizer

::LinkeditOptimizer(const void* containerBuffer, macho_header

* mh, + const char* dylibID, Diagnostics& diag) +: _mh(mh), _dylibID(dylibID), _containerBuffer(containerBuffer), _diagnostics(diag) { const unsigned origLoadCommandsSize = mh->sizeofcmds(); unsigned bytesRemaining = origLoadCommandsSize; @@ -539,9 +222,6 @@ LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, for (uint32_t i = 0; i < cmdCount; ++i) { bool remove = false; switch (cmd->cmd()) { - case LC_ID_DYLIB: - _installName = ((macho_dylib_command

*)cmd)->name(); - break; case LC_SYMTAB: _symTabCmd = (macho_symtab_command

*)cmd; break; @@ -621,6 +301,7 @@ LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, } } break; + case LC_DYLD_CHAINED_FIXUPS: case LC_SEGMENT_SPLIT_INFO: remove = true; break; @@ -643,6 +324,11 @@ LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); } +template +void LinkeditOptimizer

::setStripMode(DylibStripMode stripMode) { + _stripMode = stripMode; +} + /* static void dumpLoadCommands(const uint8_t* mheader) { @@ -746,20 +432,22 @@ void LinkeditOptimizer

::updateLoadCommands(uint32_t mergedLinkeditStartOffset _symTabCmd->set_strsize(sharedSymbolStringsSize); // update dynamic symbol table to have proper offsets into shared symbol table - _dynSymTabCmd->set_ilocalsym(0); - _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount); - _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex); - _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount); - _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex); - _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount); - _dynSymTabCmd->set_tocoff(0); - _dynSymTabCmd->set_ntoc(0); - _dynSymTabCmd->set_modtaboff(0); - _dynSymTabCmd->set_nmodtab(0); - _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset); - _dynSymTabCmd->set_extreloff(0); - _dynSymTabCmd->set_locreloff(0); - _dynSymTabCmd->set_nlocrel(0); + if ( _dynSymTabCmd != nullptr ) { + _dynSymTabCmd->set_ilocalsym(0); + _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount); + _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount); + _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount); + _dynSymTabCmd->set_tocoff(0); + _dynSymTabCmd->set_ntoc(0); + _dynSymTabCmd->set_modtaboff(0); + _dynSymTabCmd->set_nmodtab(0); + _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset); + _dynSymTabCmd->set_extreloff(0); + _dynSymTabCmd->set_locreloff(0); + _dynSymTabCmd->set_nlocrel(0); + } // update dyld info if ( _dyldInfo != nullptr ) { @@ -867,11 +555,27 @@ void LinkeditOptimizer

::copyLocalSymbols(uint8_t* newLinkEditContent, SortedS bool redact, std::vector& localSymbolInfos, std::vector>& unmappedLocalSymbols, SortedStringPool

& localSymbolsStringPool) { - LocalSymbolInfo localInfo; - localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer); + localSymbolInfos.push_back(LocalSymbolInfo()); + + LocalSymbolInfo& localInfo = localSymbolInfos.back(); + localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_containerBuffer); localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size(); localInfo.nlistCount = 0; _newLocalSymbolsStartIndex = symbolIndex; + _newLocalSymbolCount = 0; + + switch (_stripMode) { + case CacheBuilder::DylibStripMode::stripNone: + case CacheBuilder::DylibStripMode::stripExports: + break; + case CacheBuilder::DylibStripMode::stripLocals: + case CacheBuilder::DylibStripMode::stripAll: + return; + } + + if ( _dynSymTabCmd == nullptr ) + return; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()]; @@ -904,7 +608,6 @@ void LinkeditOptimizer

::copyLocalSymbols(uint8_t* newLinkEditContent, SortedS } _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex; localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex; - localSymbolInfos.push_back(localInfo); } @@ -912,6 +615,20 @@ template void LinkeditOptimizer

::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) { _newExportedSymbolsStartIndex = symbolIndex; + _newExportedSymbolCount = 0; + + switch (_stripMode) { + case CacheBuilder::DylibStripMode::stripNone: + case CacheBuilder::DylibStripMode::stripLocals: + break; + case CacheBuilder::DylibStripMode::stripExports: + case CacheBuilder::DylibStripMode::stripAll: + return; + } + + if ( _dynSymTabCmd == nullptr ) + return; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()]; @@ -940,6 +657,20 @@ template void LinkeditOptimizer

::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) { _newImportedSymbolsStartIndex = symbolIndex; + _newImportedSymbolCount = 0; + + if ( _dynSymTabCmd == nullptr ) + return; + + switch (_stripMode) { + case CacheBuilder::DylibStripMode::stripNone: + break; + case CacheBuilder::DylibStripMode::stripLocals: + case CacheBuilder::DylibStripMode::stripExports: + case CacheBuilder::DylibStripMode::stripAll: + return; + } + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); const macho_nlist

* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()]; @@ -964,6 +695,10 @@ template void LinkeditOptimizer

::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset) { _newIndirectSymbolTableOffset = offset; + + if ( _dynSymTabCmd == nullptr ) + return; + const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()]; uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset]; for (uint32_t i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) { @@ -977,7 +712,9 @@ void LinkeditOptimizer

::copyIndirectSymbolTable(uint8_t* newLinkEditContent, } template -void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector*>& optimizers) +void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, + CacheBuilder::UnmappedRegion* localSymbolsRegion, + std::vector*>& optimizers) { // allocate space for new linkedit data uint64_t totalUnoptLinkeditsSize = builder._readOnlyRegion.sizeInUse - builder._nonLinkEditReadOnlySize; @@ -1027,9 +764,11 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector> unmappedLocalSymbols; - if ( builder._options.excludeLocalSymbols ) + if ( unmapLocals ) unmappedLocalSymbols.reserve(0x01000000); std::vector localSymbolInfos; localSymbolInfos.reserve(optimizers.size()); @@ -1039,7 +778,7 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector* op : optimizers) { - op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, builder._options.excludeLocalSymbols, + op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, unmapLocals, localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool); uint32_t x = symbolIndex; op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex); @@ -1081,7 +820,6 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector::mergeLinkedits(CacheBuilder& builder, std::vector tables(cacheHeader, addrWhereMergedLinkWillStart, builder._diagnostics, optimizers); - uint32_t tablesSize = tables.totalSize(); - 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 { - builder._diagnostics.warning("not enough room to add dyld accelerator tables"); - } - } - // overwrite end of un-opt linkedits to create a new unmapped region for local symbols - if ( builder._options.excludeLocalSymbols ) { + if ( unmapLocals ) { 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 @@ -1140,12 +859,10 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector)); // 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; + localSymbolsRegion->buffer = (uint8_t*)localsBuffer; + localSymbolsRegion->bufferSize = localsBufferSize; + localSymbolsRegion->sizeInUse = localsBufferSize; } else { builder._diagnostics.warning("could not allocate local symbols"); @@ -1164,14 +881,16 @@ void LinkeditOptimizer

::mergeLinkedits(CacheBuilder& builder, std::vector -void LinkeditOptimizer

::optimizeLinkedit(CacheBuilder& builder) +void LinkeditOptimizer

::optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer, + CacheBuilder::UnmappedRegion* localSymbolsRegion, + const std::vector>& images) { - DyldSharedCache* cache = (DyldSharedCache*)builder._readExecuteRegion.buffer; // construct a LinkeditOptimizer for each image __block std::vector*> optimizers; - cache->forEachImage(^(const mach_header* mh, const char*) { - optimizers.push_back(new LinkeditOptimizer

(cache, (macho_header

*)mh, builder._diagnostics)); - }); + for (std::tuple image : images) { + optimizers.push_back(new LinkeditOptimizer

(containerBuffer, (macho_header

*)std::get<0>(image), std::get<1>(image), builder._diagnostics)); + optimizers.back()->setStripMode(std::get<2>(image)); + } #if 0 // add optimizer for each branch pool for (uint64_t poolOffset : branchPoolOffsets) { @@ -1180,20 +899,24 @@ void LinkeditOptimizer

::optimizeLinkedit(CacheBuilder& builder) } #endif // merge linkedit info - mergeLinkedits(builder, optimizers); + mergeLinkedits(builder, localSymbolsRegion, optimizers); // delete optimizers for (LinkeditOptimizer

* op : optimizers) delete op; } -void CacheBuilder::optimizeLinkedit() +void CacheBuilder::optimizeLinkedit(UnmappedRegion* localSymbolsRegion, + const std::vector>& images) { - if ( _archLayout->is64 ) { - return LinkeditOptimizer>::optimizeLinkedit(*this); + const void* buffer = (const void*)_fullAllocatedBuffer; + if ( _is64 ) { + return LinkeditOptimizer>::optimizeLinkedit(*this, buffer, + localSymbolsRegion, images); } else { - return LinkeditOptimizer>::optimizeLinkedit(*this); + return LinkeditOptimizer>::optimizeLinkedit(*this, buffer, + localSymbolsRegion, images); } } diff --git a/dyld3/shared-cache/OptimizerObjC.cpp b/dyld3/shared-cache/OptimizerObjC.cpp index faf2330..f41a9c5 100644 --- a/dyld3/shared-cache/OptimizerObjC.cpp +++ b/dyld3/shared-cache/OptimizerObjC.cpp @@ -32,11 +32,12 @@ #include "DyldSharedCache.h" #include "Diagnostics.h" -#include "CacheBuilder.h" +#include "SharedCacheBuilder.h" #include "FileAbstraction.hpp" #include "MachOFileAbstraction.hpp" #include "MachOLoaded.h" #include "MachOAnalyzer.h" +#include "MachOAnalyzerSet.h" #ifndef MH_HAS_OBJC #define MH_HAS_OBJC 0x40000000 @@ -117,27 +118,11 @@ public: _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.raw64 = vmaddr; - assert(ptr.arm64e.authRebase.bind == 0); - if ( ptr.arm64e.authRebase.auth ) { - vmaddr = _cacheUnslideAddr + ptr.arm64e.authRebase.target; - } - else { - vmaddr = ptr.arm64e.unpackTarget(); - } - } return vmaddr; } @@ -164,7 +149,6 @@ private: uint64_t _slide; uint64_t _cacheUnslideAddr; uint8_t* _cacheStart; - bool _chainedFixups; }; @@ -328,7 +312,7 @@ public: { if (cls->isMetaClass(cache)) return; - const char *name = cls->getName(cache); + const char* name = cls->getName(cache); uint64_t name_vmaddr = cache->vmAddrForContent((void*)name); uint64_t cls_vmaddr = cache->vmAddrForContent(cls); uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header)); @@ -348,6 +332,404 @@ public: size_t count() const { return _count; } }; + +/// Builds a map from (install name, class name, method name) to actual IMPs +template +class IMPMapBuilder +{ +private: + typedef typename P::uint_t pint_t; + +public: + + struct MapKey { + std::string_view installName; + std::string_view className; + std::string_view methodName; + bool isInstanceMethod; + + bool operator==(const MapKey& other) const { + return isInstanceMethod == other.isInstanceMethod && + installName == other.installName && + className == other.className && + methodName == other.methodName; + } + + size_t hash() const { + std::size_t seed = 0; + seed ^= std::hash()(installName) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash()(className) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash()(methodName) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= std::hash()(isInstanceMethod) + 0x9e3779b9 + (seed<<6) + (seed>>2); + return seed; + } + }; + + struct MapKeyHasher { + size_t operator()(const MapKey& k) const { + return k.hash(); + } + }; + + std::unordered_map impMap; + bool relativeMethodListSelectorsAreDirect; + + IMPMapBuilder(bool relativeMethodListSelectorsAreDirect) + : relativeMethodListSelectorsAreDirect(relativeMethodListSelectorsAreDirect) { } + + void visitClass(ContentAccessor* cache, + const macho_header

* header, + objc_class_t

* cls) + { + objc_method_list_t

*methodList = cls->getMethodList(cache); + if (methodList == nullptr) return; + + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)header; + bool isInstanceMethod = !cls->isMetaClass(cache); + const char* className = cls->getName(cache); + const char* installName = ma->installName(); + + for (uint32_t n = 0; n < methodList->getCount(); n++) { + // do not clobber an existing entry if any, because categories win + impMap.try_emplace(MapKey{ + .installName = installName, + .className = className, + .methodName = methodList->getStringName(cache, n, relativeMethodListSelectorsAreDirect), + .isInstanceMethod = isInstanceMethod + }, methodList->getImp(n, cache)); + } + } + + void visit(ContentAccessor* cache, const macho_header

* header) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)header; + + // Method lists from categories + PointerSection *> + cats(cache, header, "__DATA", "__objc_catlist"); + for (pint_t i = 0; i < cats.count(); i++) { + objc_category_t

*cat = cats.get(i); + objc_class_t

* cls = cat->getClass(cache); + if (cls == nullptr) + continue; + + objc_method_list_t

*instanceMethods = cat->getInstanceMethods(cache); + if (instanceMethods != nullptr) { + for (uint32_t n = 0; n < instanceMethods->getCount(); n++) { + MapKey k { + .installName = ma->installName(), + .className = cls->getName(cache), + .methodName = instanceMethods->getStringName(cache, n, relativeMethodListSelectorsAreDirect), + .isInstanceMethod = true + }; + //printf("Adding %s %s %s %d cat %s\n", k.installName.data(), k.className.data(), k.methodName.data(), k.isInstanceMethod, k.catName->data()); + impMap[k] = instanceMethods->getImp(n, cache); + } + } + objc_method_list_t

*classMethods = cat->getClassMethods(cache); + if (classMethods != nullptr) { + for (uint32_t n = 0; n < classMethods->getCount(); n++) { + MapKey k { + .installName = ma->installName(), + .className = cls->getName(cache), + .methodName = classMethods->getStringName(cache, n, relativeMethodListSelectorsAreDirect), + .isInstanceMethod = false + }; + //printf("Adding %s %s %s %d cat %s\n", k.installName.data(), k.className.data(), k.methodName.data(), k.isInstanceMethod, k.catName->data()); + impMap[k] = classMethods->getImp(n, cache); + } + } + } + } +}; + +// List of offsets in libobjc that the shared cache optimization needs to use. +template +struct objc_opt_imp_caches_pointerlist_tt { + T selectorStringVMAddrStart; + T selectorStringVMAddrEnd; + T inlinedSelectorsVMAddrStart; + T inlinedSelectorsVMAddrEnd; +}; + +template +class IMPCachesEmitter +{ + typedef typename P::uint_t pint_t; + +private: + Diagnostics& diag; + const IMPMapBuilder

& impMapBuilder; + uint64_t selectorStringVMAddr; + uint8_t*& readOnlyBuffer; + size_t& readOnlyBufferSize; + uint8_t*& readWriteBuffer; + size_t& readWriteBufferSize; + CacheBuilder::ASLR_Tracker& aslrTracker; + + std::map _dylibInfos; + std::map*> _dylibs; + const std::vector inlinedSelectors; + + struct ImpCacheHeader { + int32_t fallback_class_offset; + uint32_t cache_shift : 5; + uint32_t cache_mask : 11; + uint32_t occupied : 14; + uint32_t has_inlines : 1; + uint32_t bit_one : 1; + }; + + struct ImpCacheEntry { + uint32_t selOffset; + uint32_t impOffset; + }; + +public: + + static size_t sizeForImpCacheWithCount(int entries) { + return sizeof(ImpCacheHeader) + entries * sizeof(ImpCacheEntry); + } + + struct ImpCacheContents { + struct bucket_t { + uint32_t sel_offset = 0; + uint64_t imp = 0; + }; + std::vector buckets; + uint64_t occupiedBuckets = 0; + bool hasInlines = false; + + uint64_t capacity() const + { + return buckets.size(); + } + + uint64_t occupied() const { + return occupiedBuckets; + } + + void incrementOccupied() { + ++occupiedBuckets; + } + + void insert(uint64_t slot, uint64_t selOffset, uint64_t imp) { + bucket_t& b = buckets[slot]; + assert(b.imp == 0); + + if (!b.imp) incrementOccupied(); + assert((uint32_t)selOffset == selOffset); + b.sel_offset = (uint32_t)selOffset; + b.imp = imp; + } + + void fillBuckets(const IMPCaches::ClassData* classData, bool metaclass, const IMPMapBuilder

& classRecorder) { + const std::vector & methods = classData->methods; + buckets.resize(classData->modulo()); + for (const IMPCaches::ClassData::Method& method : methods) { + typename IMPMapBuilder

::MapKey k { + .installName = method.installName, + .className = method.className, + .methodName = method.selector->name, + .isInstanceMethod = !metaclass + }; + + pint_t imp = classRecorder.impMap.at(k); + int slot = (method.selector->inProgressBucketIndex >> classData->shift) & classData->mask(); + insert(slot, method.selector->offset, imp); + hasInlines |= (method.wasInlined && !method.fromFlattening); + } + } + + std::pair + write(ContentAccessor* cache, + uint64_t cacheSelectorStringVMAddr, uint64_t clsVMAddr, + uint8_t*& buf, size_t& bufSize, Diagnostics& diags) { + constexpr bool log = false; + uint64_t spaceRequired = sizeof(ImpCacheEntry) * capacity(); + + if (spaceRequired > bufSize) { + diags.error("Not enough space for imp cache"); + return { 0, 0 }; + } + + // Convert from addresses to offsets and write out + ImpCacheEntry* offsetBuckets = (ImpCacheEntry*)buf; + // printf("Buckets: 0x%08llx\n", cache->vmAddrForContent(offsetBuckets)); + for (uint64_t index = 0; index != buckets.size(); ++index) { + bucket_t bucket = buckets[index]; + if (bucket.sel_offset == 0 && bucket.imp == 0) { + // Empty bucket + offsetBuckets[index].selOffset = 0xFFFFFFFF; + offsetBuckets[index].impOffset = 0; + } else { + int64_t selOffset = (int64_t)bucket.sel_offset; + int64_t impOffset = clsVMAddr - bucket.imp; + assert((int32_t)impOffset == impOffset); + assert((int32_t)selOffset == selOffset); + offsetBuckets[index].selOffset = (int32_t)selOffset; + offsetBuckets[index].impOffset = (int32_t)impOffset; + if (log) { + diags.verbose("[IMP Caches] Coder[%lld]: %#08llx (sel: %#08x, imp %#08x) %s\n", index, + cache->vmAddrForOnDiskVMAddr(bucket.imp), + (int32_t)selOffset, (int32_t)impOffset, + (const char*)cache->contentForVMAddr(cacheSelectorStringVMAddr + bucket.sel_offset)); + } + } + } + + buf += spaceRequired; + bufSize -= spaceRequired; + + return { cache->vmAddrForContent(offsetBuckets), (uint64_t)buckets.size() }; + } + }; + + IMPCachesEmitter(Diagnostics& diags, const IMPMapBuilder

& builder, uint64_t selectorStringVMAddr, uint8_t*& roBuf, size_t& roBufSize, uint8_t* &rwBuf, size_t& rwBufSize, const std::vector & dylibInfos, const std::vector*> & dylibs, CacheBuilder::ASLR_Tracker& tracker) + : diag(diags), impMapBuilder(builder), selectorStringVMAddr(selectorStringVMAddr), readOnlyBuffer(roBuf), readOnlyBufferSize(roBufSize), readWriteBuffer(rwBuf), readWriteBufferSize(rwBufSize), aslrTracker(tracker) { + for (const CacheBuilder::DylibInfo& d : dylibInfos) { + _dylibInfos[d.dylibID] = &d; + } + for (const macho_header

* d : dylibs) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) d; + _dylibs[ma->installName()] = d; + } + } + + // Returns true if we should filter this class out from getting an imp cache + bool filter(ContentAccessor* cache, const dyld3::MachOAnalyzer* ma, const objc_class_t

* cls) { + const CacheBuilder::DylibInfo* d = _dylibInfos[ma->installName()]; + IMPCaches::ClassKey key { + .name = cls->getName(cache), + .metaclass = cls->isMetaClass(cache) + }; + return (d->impCachesClassData.find(key) == d->impCachesClassData.end()); + } + + void visitClass(ContentAccessor* cache, + const macho_header

* header, + objc_class_t

* cls) + { + // If we ran out of space then don't try to optimize more + if (diag.hasError()) + return; + + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) header; + if (filter(cache, ma, cls)) { + *cls->getVTableAddress() = 0; + return; + } + + const char* className = cls->getName(cache); + + if (cls->getVTable(cache) != 0) { + diag.error("Class '%s' has non-zero vtable\n", className); + return; + } + + const CacheBuilder::DylibInfo* d = _dylibInfos[ma->installName()]; + IMPCaches::ClassKey key { + .name = cls->getName(cache), + .metaclass = cls->isMetaClass(cache) + }; + IMPCaches::ClassData* data = (d->impCachesClassData.at(key)).get(); +#if 0 + for (const objc_method_t

& method : methods) { + printf(" 0x%llx: 0x%llx (%s)\n", method.getImp(), method.getName(), + (const char*)cache->contentForVMAddr(method.getName())); + } +#endif + + uint64_t clsVMAddr = cache->vmAddrForContent(cls); + + if (data->mask() > 0x7ff) { + diag.verbose("Cache for class %s (%#08llx) is too large (mask: %#x)\n", + className, clsVMAddr, data->mask()); + return; + } + + ImpCacheContents impCache; + impCache.fillBuckets(data, cls->isMetaClass(cache), impMapBuilder); + + constexpr bool log = false; + if (log) { + printf("Writing cache for %sclass %s (%#08llx)\n", cls->isMetaClass(cache) ? "meta" : "", className, clsVMAddr); + } + + struct ImpCacheHeader { + int32_t fallback_class_offset; + uint32_t cache_shift : 5; + uint32_t cache_mask : 11; + uint32_t occupied : 14; + uint32_t has_inlines : 1; + uint32_t bit_one : 1; + }; + pint_t* vtableAddr = cls->getVTableAddress(); + + // the alignment of ImpCaches to 16 bytes is only needed for arm64_32. + ImpCacheHeader* cachePtr = (ImpCacheHeader*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3); + + assert(readOnlyBufferSize > sizeof(ImpCacheHeader)); + + uint64_t occupied = impCache.occupied(); + int64_t fallback_class_offset = *(cls->getSuperClassAddress()) - clsVMAddr; + + if (data->flatteningRootSuperclass) { + // If we are a class being flattened (inheriting all the selectors of + // its superclasses up to and including the flattening root), the fallback class + // should be the first superclass which is not flattened. + + // Find the VMAddr of that superclass, given its segment index and offset + // in the source dylib. + const auto & superclass = *(data->flatteningRootSuperclass); + const macho_header

* d = _dylibs[superclass.installName]; + __block uint64_t superclassVMAddr = 0; + const dyld3::MachOAnalyzer *ma = (const dyld3::MachOAnalyzer *)d; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + if (info.segIndex == superclass.segmentIndex) { + superclassVMAddr = info.vmAddr + superclass.segmentOffset; + stop = true; + } + }); + + assert(superclassVMAddr > 0); + fallback_class_offset = superclassVMAddr - clsVMAddr; + } + + assert((int32_t)fallback_class_offset == fallback_class_offset); + assert((uint32_t)occupied == occupied); + + *cachePtr = (ImpCacheHeader){ + .fallback_class_offset = (int32_t)fallback_class_offset, + .cache_shift = (uint32_t)(data->shift + 7), + .cache_mask = (uint32_t)data->mask(), + .occupied = (uint32_t)occupied, + .has_inlines = impCache.hasInlines, + .bit_one = 1, // obj-c plays HORRENDOUS games here + }; + + // is this right? + int64_t vmaddr = cache->vmAddrForContent(readOnlyBuffer); + assert((pint_t)vmaddr == (uint64_t)vmaddr); + *vtableAddr = (pint_t)cache->vmAddrForContent(readOnlyBuffer); + aslrTracker.add(vtableAddr); + readOnlyBuffer += sizeof(ImpCacheHeader); + readOnlyBufferSize -= sizeof(ImpCacheHeader); + + impCache.write(cache, selectorStringVMAddr, clsVMAddr, readOnlyBuffer, readOnlyBufferSize, diag); + } + + void emitInlinedSelectors(const std::vector selectors) { + // FIXME: this should be in constant memory + for (const IMPCaches::Selector* s : selectors) { + assert(readWriteBufferSize >= sizeof(pint_t)); + *(pint_t*)readWriteBuffer = (pint_t)(selectorStringVMAddr + s->offset); + aslrTracker.add(readWriteBuffer); + readWriteBuffer += sizeof(pint_t); + readWriteBufferSize -= sizeof(pint_t); + } + } +}; + template class ProtocolOptimizer { @@ -387,7 +769,7 @@ public: for (pint_t i = 0; i < protocols.count(); i++) { objc_protocol_t

*proto = protocols.get(i); - const char *name = proto->getName(cache); + const char* name = proto->getName(cache); if (_protocolNames.count(name) == 0) { if (proto->getSize() > sizeof(objc_protocol_t

)) { _diagnostics.error("objc protocol is too big"); @@ -411,7 +793,8 @@ public: uint8_t *& rwdest, size_t& rwremaining, uint8_t *& rodest, size_t& roremaining, CacheBuilder::ASLR_Tracker& aslrTracker, - pint_t protocolClassVMAddr) + pint_t protocolClassVMAddr, + const dyld3::MachOAnalyzerSet::PointerMetaData& PMD) { if (_protocolCount == 0) return NULL; @@ -440,6 +823,12 @@ public: if (!proto->getIsaVMAddr()) { proto->setIsaVMAddr(protocolClassVMAddr); } + + // If the objc runtime signed the Protocol ISA, then we need to too + if ( PMD.authenticated ) { + aslrTracker.setAuthData(proto->getISALocation(), PMD.diversity, PMD.usesAddrDiversity, PMD.key); + } + if (oldSize < sizeof(*proto)) { // Protocol object is old. Populate new fields. proto->setSize(sizeof(objc_protocol_t

)); @@ -519,7 +908,7 @@ template void addObjcSegments(Diagnostics& diag, DyldSharedCache* cache, const mach_header* libobjcMH, uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeAllocated, uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated, - uint32_t objcRwFileOffset) + uint64_t objcRwFileOffset) { // validate there is enough free space to add the load commands const dyld3::MachOAnalyzer* libobjcMA = ((dyld3::MachOAnalyzer*)libobjcMH); @@ -588,6 +977,43 @@ void addObjcSegments(Diagnostics& diag, DyldSharedCache* cache, const mach_heade } } +template static inline void emitIMPCaches(ContentAccessor& cacheAccessor, + std::vector & allDylibs, + std::vector*> & sizeSortedDylibs, + bool relativeMethodListSelectorsAreDirect, + uint64_t selectorStringVMAddr, + uint8_t* optROData, size_t& optRORemaining, + uint8_t* optRWData, size_t& optRWRemaining, + CacheBuilder::ASLR_Tracker& aslrTracker, + const std::vector & inlinedSelectors, + uint8_t* &inlinedSelectorsStart, + uint8_t* &inlinedSelectorsEnd, + Diagnostics& diag, + TimeRecorder& timeRecorder) { + diag.verbose("[IMP caches] computing IMP map\n"); + + IMPMapBuilder

classRecorder(relativeMethodListSelectorsAreDirect); + for (const macho_header

* mh : sizeSortedDylibs) { + ClassWalker> classWalker(classRecorder, ClassWalkerMode::ClassAndMetaclasses); + classWalker.walk(&cacheAccessor, mh); + classRecorder.visit(&cacheAccessor, mh); + } + + timeRecorder.recordTime("compute IMP map"); + diag.verbose("[IMP caches] emitting IMP caches\n"); + + IMPCachesEmitter

impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, aslrTracker); + ClassWalker> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses); + for (const macho_header

* mh : sizeSortedDylibs) { + impEmitterClassWalker.walk(&cacheAccessor, mh); + if (diag.hasError()) + return; + } + + inlinedSelectorsStart = optRWData; + impCachesEmitter.emitInlinedSelectors(inlinedSelectors); + inlinedSelectorsEnd = optRWData; +} template void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::ASLR_Tracker& aslrTracker, @@ -595,7 +1021,11 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS const std::map& missingWeakImports, Diagnostics& diag, uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeUsed, uint64_t objcReadOnlyBufferSizeAllocated, uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated, - uint32_t objcRwFileOffset) + uint64_t objcRwFileOffset, + std::vector & allDylibs, + const std::vector & inlinedSelectors, + bool impCachesSuccess, + TimeRecorder& timeRecorder) { typedef typename P::E E; typedef typename P::uint_t pint_t; @@ -616,6 +1046,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS __block const mach_header* libobjcMH = nullptr; __block const macho_section

*optROSection = nullptr; __block const macho_section

*optPointerListSection = nullptr; + __block const macho_section

*optImpCachesPointerSection = nullptr; __block std::vector*> objcDylibs; cache->forEachImage(^(const mach_header* machHeader, const char* installName) { const macho_header

* mh = (const macho_header

*)machHeader; @@ -623,6 +1054,9 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS libobjcMH = (mach_header*)mh; optROSection = mh->getSection("__TEXT", "__objc_opt_ro"); optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs"); + if ( optPointerListSection == nullptr ) + optPointerListSection = mh->getSection("__AUTH", "__objc_opt_ptrs"); + optImpCachesPointerSection = mh->getSection("__DATA_CONST", "__objc_scoffs"); } if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) { objcDylibs.push_back(mh); @@ -637,6 +1071,9 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS diag.warning("libobjc's pointer list section missing (metadata not optimized)"); return; } + if ( optImpCachesPointerSection == nullptr ) { + diag.warning("libobjc's magical shared cache offsets list section missing (metadata not optimized)"); + } // point optROData into space allocated in dyld cache uint8_t* optROData = objcReadOnlyBuffer + objcReadOnlyBufferSizeUsed; size_t optRORemaining = objcReadOnlyBufferSizeAllocated - objcReadOnlyBufferSizeUsed; @@ -767,7 +1204,11 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS return (uint8_t*)(((uintptr_t)ptr + 0x7) & ~0x7); }; - SelectorOptimizer > selOptimizer(uniq); + // Relative method lists names are initially an offset to a selector reference. + // Eventually we'll update them to offsets directly to the selector string. + bool relativeMethodListSelectorsAreDirect = false; + + SelectorOptimizer > selOptimizer(uniq, relativeMethodListSelectorsAreDirect); selOptimizer.visitCoalescedStrings(coalescedText); for (const macho_header

* mh : sizeSortedDylibs) { LegacySelectorUpdater>::update(&cacheAccessor, mh, uniq); @@ -865,7 +1306,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS // // This is SAFE: modified binaries are still usable as unsorted lists. // This must be done AFTER uniquing selectors. - MethodListSorter

methodSorter; + MethodListSorter

methodSorter(relativeMethodListSelectorsAreDirect); for (const macho_header

* mh : sizeSortedDylibs) { methodSorter.optimize(&cacheAccessor, mh); } @@ -887,10 +1328,26 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS protocolOptimizer.protocolCount()); pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass); + + // Get the pointer metadata from the magic protocolClassVMAddr symbol + // We'll transfer it over to the ISA on all the objc protocols when we set their ISAs + dyld3::MachOAnalyzerSet::PointerMetaData protocolClassPMD; + uint16_t protocolClassAuthDiversity = 0; + bool protocolClassAuthIsAddr = false; + uint8_t protocolClassAuthKey = 0; + if ( aslrTracker.hasAuthData((void*)&optPointerList->protocolClass, &protocolClassAuthDiversity, &protocolClassAuthIsAddr, &protocolClassAuthKey) ) { + protocolClassPMD.diversity = protocolClassAuthDiversity; + protocolClassPMD.high8 = 0; + protocolClassPMD.authenticated = 1; + protocolClassPMD.key = protocolClassAuthKey; + protocolClassPMD.usesAddrDiversity = protocolClassAuthIsAddr; + } + err = protocolOptimizer.writeProtocols(&cacheAccessor, optRWData, optRWRemaining, optROData, optRORemaining, - aslrTracker, protocolClassVMAddr); + aslrTracker, protocolClassVMAddr, + protocolClassPMD); if (err) { diag.warning("%s", err); return; @@ -942,6 +1399,56 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS diag.verbose(" updated % 6ld ivar offsets\n", ivarOffsetOptimizer.optimized()); + // + // Build imp caches + // + // Objc has a magic section of imp cache base pointers. We need these to + // offset everything else from + const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname"); + uint64_t selectorStringVMAddr = methodNames.bufferVMAddr; + uint64_t selectorStringVMSize = methodNames.bufferSize; + uint64_t impCachesVMSize = 0; // We'll calculate this later + + uint64_t optRODataRemainingBeforeImpCaches = optRORemaining; + + timeRecorder.pushTimedSection(); + + uint8_t* inlinedSelectorsStart = optRWData; + uint8_t* inlinedSelectorsEnd = optRWData; + + if (impCachesSuccess) { + emitIMPCaches

(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListSelectorsAreDirect, + selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, + aslrTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, diag, timeRecorder); + } + + uint8_t* alignedROData = alignPointer(optROData); + optRORemaining -= (alignedROData - optROData); + optROData = alignedROData; + + impCachesVMSize = optRODataRemainingBeforeImpCaches - optRORemaining; + timeRecorder.recordTime("emit IMP caches"); + timeRecorder.popTimedSection(); + + diag.verbose("[IMP Caches] Imp caches size: %'lld bytes\n\n", impCachesVMSize); + + // Update the pointers in the pointer list section + if (optImpCachesPointerSection) { + if (optImpCachesPointerSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt)) { + diag.warning("libobjc's pointer list section is too small (metadata not optimized)"); + return; + } + auto *impCachePointers = (objc_opt_imp_caches_pointerlist_tt *)cacheAccessor.contentForVMAddr(optImpCachesPointerSection->addr()); + impCachePointers->selectorStringVMAddrStart = (pint_t)selectorStringVMAddr; + impCachePointers->selectorStringVMAddrEnd = (pint_t)(selectorStringVMAddr + selectorStringVMSize); + impCachePointers->inlinedSelectorsVMAddrStart = (pint_t)cacheAccessor.vmAddrForContent(inlinedSelectorsStart); + impCachePointers->inlinedSelectorsVMAddrEnd = (pint_t)cacheAccessor.vmAddrForContent(inlinedSelectorsEnd); + + aslrTracker.add(&impCachePointers->selectorStringVMAddrStart); + aslrTracker.add(&impCachePointers->selectorStringVMAddrEnd); + aslrTracker.add(&impCachePointers->inlinedSelectorsVMAddrStart); + aslrTracker.add(&impCachePointers->inlinedSelectorsVMAddrEnd); + } // Collect flags. uint32_t headerFlags = 0; @@ -990,7 +1497,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD - if (forProduction) { + { const bool logSelectors = false; uint64_t lohADRPCount = 0; uint64_t lohLDRCount = 0; @@ -1102,19 +1609,41 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS } // anon namespace -void CacheBuilder::optimizeObjC() +size_t IMPCaches::sizeForImpCacheWithCount(int count) { + // The architecture should not be relevant here as it's all offsets and fixed int sizes. + // It was just the most logical place to host this function in. + + size_t size64 = IMPCachesEmitter>::sizeForImpCacheWithCount(count); + size_t size32 = IMPCachesEmitter>::sizeForImpCacheWithCount(count); + assert(size64 == size32); + + return size64; +} + +void SharedCacheBuilder::optimizeObjC(bool impCachesSuccess, const std::vector & inlinedSelectors) { - uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - _readWriteRegion.buffer) + _readWriteRegion.cacheFileOffset); if ( _archLayout->is64 ) - doOptimizeObjC>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, - _coalescedText, _missingWeakImports, - _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated, - _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset); + doOptimizeObjC>((DyldSharedCache*)_readExecuteRegion.buffer, + _options.optimizeStubs, + _aslrTracker, _lohTracker, + _coalescedText, + _missingWeakImports, _diagnostics, + _objcReadOnlyBuffer, + _objcReadOnlyBufferSizeUsed, + _objcReadOnlyBufferSizeAllocated, + _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, + _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder); else - doOptimizeObjC>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, - _coalescedText, _missingWeakImports, - _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated, - _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset); + doOptimizeObjC>((DyldSharedCache*)_readExecuteRegion.buffer, + _options.optimizeStubs, + _aslrTracker, _lohTracker, + _coalescedText, + _missingWeakImports, _diagnostics, + _objcReadOnlyBuffer, + _objcReadOnlyBufferSizeUsed, + _objcReadOnlyBufferSizeAllocated, + _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, + _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder); } static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData) @@ -1127,13 +1656,16 @@ static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData) // The goal here is to allocate space in the dyld shared cache (while it is being laid out) that will contain // the objc structures that previously were in the __objc_opt_ro section. -uint32_t CacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount) +uint32_t SharedCacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount) { return 0xA000 + hashTableSize(selRefCount, 5) + hashTableSize(classDefCount, 12) + hashTableSize(protocolDefCount, 8); } // Space to replace the __objc_opt_rw section. -uint32_t CacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount) +uint32_t SharedCacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount) { - return 8*imageCount + protocolDefCount*12*(_archLayout->is64 ? 8 : 4); + uint8_t pointerSize = _archLayout->is64 ? 8 : 4; + return 8*imageCount + + protocolDefCount*12*pointerSize + + (int)_impCachesBuilder->inlinedSelectors.size() * pointerSize; } diff --git a/dyld3/shared-cache/SharedCacheBuilder.cpp b/dyld3/shared-cache/SharedCacheBuilder.cpp new file mode 100644 index 0000000..d7bd895 --- /dev/null +++ b/dyld3/shared-cache/SharedCacheBuilder.cpp @@ -0,0 +1,4105 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mach-o/dyld_priv.h" +#include "ClosureBuilder.h" +#include "Closure.h" +#include "ClosureFileSystemNull.h" +#include "CodeSigningTypes.h" +#include "MachOFileAbstraction.hpp" +#include "SharedCacheBuilder.h" +#include "RootsChecker.h" +#include "IMPCachesBuilder.hpp" + +#include "FileUtils.h" +#include "StringUtils.h" +#include "Trie.hpp" + +#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 0x100000000ULL +#endif + +#if ARM64_SHARED_REGION_START == 0x7FFF00000000 + #define ARM64_DELTA_MASK 0x00FF000000000000 +#else + #define ARM64_DELTA_MASK 0x00FFFF0000000000 +#endif + +#ifndef ARM64_32_SHARED_REGION_START + #define ARM64_32_SHARED_REGION_START 0x1A000000ULL + #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL +#endif + +#define ARMV7K_CHAIN_BITS 0xC0000000 + +#if BUILDING_UPDATE_DYLD_CACHE_BUILDER + #define DISCONTIGUOUS_RX 0x7FFF20000000ULL +#else + #define DISCONTIGUOUS_RX 0x7FFF20000000ULL // size for MRM builder +#endif +#define DISCONTIGUOUS_RW 0x7FFF80000000ULL +#define DISCONTIGUOUS_RO 0x7FFFC0000000ULL +#define DISCONTIGUOUS_RX_SIZE (DISCONTIGUOUS_RW - DISCONTIGUOUS_RX) +#define DISCONTIGUOUS_RW_SIZE 0x40000000 +#define DISCONTIGUOUS_RO_SIZE 0x3FE00000 + +const SharedCacheBuilder::ArchLayout SharedCacheBuilder::_s_archLayout[] = { + { DISCONTIGUOUS_RX, 0xEFE00000ULL, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K, 14, 2, true, true, true }, + { DISCONTIGUOUS_RX, 0xEFE00000ULL, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K, 14, 2, true, true, true }, + { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K, 12, 0, false, false, true }, + { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, ARM64_DELTA_MASK, "arm64", CS_PAGE_SIZE_4K, 14, 2, false, true, false }, +#if SUPPORT_ARCH_arm64e + { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, ARM64_DELTA_MASK, "arm64e", CS_PAGE_SIZE_16K, 14, 2, false, true, false }, +#endif +#if SUPPORT_ARCH_arm64_32 + { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true }, +#endif + { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K, 14, 4, false, false, true }, + { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x00400000, ARMV7K_CHAIN_BITS, "armv7k", CS_PAGE_SIZE_4K, 14, 4, false, false, true }, + { 0x40000000, 0x40000000, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K, 14, 0, false, false, true } +}; + +// These are functions that are interposed by Instruments.app or ASan +const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols[] = { + "___bzero", + "___cxa_atexit", + "___cxa_throw", + "__longjmp", + "__objc_autoreleasePoolPop", + "_accept", + "_access", + "_asctime", + "_asctime_r", + "_asprintf", + "_atoi", + "_atol", + "_atoll", + "_calloc", + "_chmod", + "_chown", + "_close", + "_confstr", + "_ctime", + "_ctime_r", + "_dispatch_after", + "_dispatch_after_f", + "_dispatch_async", + "_dispatch_async_f", + "_dispatch_barrier_async_f", + "_dispatch_group_async", + "_dispatch_group_async_f", + "_dispatch_source_set_cancel_handler", + "_dispatch_source_set_event_handler", + "_dispatch_sync_f", + "_dlclose", + "_dlopen", + "_dup", + "_dup2", + "_endgrent", + "_endpwent", + "_ether_aton", + "_ether_hostton", + "_ether_line", + "_ether_ntoa", + "_ether_ntohost", + "_fchmod", + "_fchown", + "_fclose", + "_fdopen", + "_fflush", + "_fopen", + "_fork", + "_fprintf", + "_free", + "_freopen", + "_frexp", + "_frexpf", + "_frexpl", + "_fscanf", + "_fstat", + "_fstatfs", + "_fstatfs64", + "_fsync", + "_ftime", + "_getaddrinfo", + "_getattrlist", + "_getcwd", + "_getgrent", + "_getgrgid", + "_getgrgid_r", + "_getgrnam", + "_getgrnam_r", + "_getgroups", + "_gethostbyaddr", + "_gethostbyname", + "_gethostbyname2", + "_gethostent", + "_getifaddrs", + "_getitimer", + "_getnameinfo", + "_getpass", + "_getpeername", + "_getpwent", + "_getpwnam", + "_getpwnam_r", + "_getpwuid", + "_getpwuid_r", + "_getsockname", + "_getsockopt", + "_gmtime", + "_gmtime_r", + "_if_indextoname", + "_if_nametoindex", + "_index", + "_inet_aton", + "_inet_ntop", + "_inet_pton", + "_initgroups", + "_ioctl", + "_lchown", + "_lgamma", + "_lgammaf", + "_lgammal", + "_link", + "_listxattr", + "_localtime", + "_localtime_r", + "_longjmp", + "_lseek", + "_lstat", + "_malloc", + "_malloc_create_zone", + "_malloc_default_purgeable_zone", + "_malloc_default_zone", + "_malloc_destroy_zone", + "_malloc_good_size", + "_malloc_make_nonpurgeable", + "_malloc_make_purgeable", + "_malloc_set_zone_name", + "_malloc_zone_from_ptr", + "_mbsnrtowcs", + "_mbsrtowcs", + "_mbstowcs", + "_memchr", + "_memcmp", + "_memcpy", + "_memmove", + "_memset", + "_mktime", + "_mlock", + "_mlockall", + "_modf", + "_modff", + "_modfl", + "_munlock", + "_munlockall", + "_objc_autoreleasePoolPop", + "_objc_setProperty", + "_objc_setProperty_atomic", + "_objc_setProperty_atomic_copy", + "_objc_setProperty_nonatomic", + "_objc_setProperty_nonatomic_copy", + "_objc_storeStrong", + "_open", + "_opendir", + "_poll", + "_posix_memalign", + "_pread", + "_printf", + "_pthread_attr_getdetachstate", + "_pthread_attr_getguardsize", + "_pthread_attr_getinheritsched", + "_pthread_attr_getschedparam", + "_pthread_attr_getschedpolicy", + "_pthread_attr_getscope", + "_pthread_attr_getstack", + "_pthread_attr_getstacksize", + "_pthread_condattr_getpshared", + "_pthread_create", + "_pthread_getschedparam", + "_pthread_join", + "_pthread_mutex_lock", + "_pthread_mutex_unlock", + "_pthread_mutexattr_getprioceiling", + "_pthread_mutexattr_getprotocol", + "_pthread_mutexattr_getpshared", + "_pthread_mutexattr_gettype", + "_pthread_rwlockattr_getpshared", + "_pwrite", + "_rand_r", + "_read", + "_readdir", + "_readdir_r", + "_readv", + "_readv$UNIX2003", + "_realloc", + "_realpath", + "_recv", + "_recvfrom", + "_recvmsg", + "_remquo", + "_remquof", + "_remquol", + "_scanf", + "_send", + "_sendmsg", + "_sendto", + "_setattrlist", + "_setgrent", + "_setitimer", + "_setlocale", + "_setpwent", + "_shm_open", + "_shm_unlink", + "_sigaction", + "_sigemptyset", + "_sigfillset", + "_siglongjmp", + "_signal", + "_sigpending", + "_sigprocmask", + "_sigwait", + "_snprintf", + "_sprintf", + "_sscanf", + "_stat", + "_statfs", + "_statfs64", + "_strcasecmp", + "_strcat", + "_strchr", + "_strcmp", + "_strcpy", + "_strdup", + "_strerror", + "_strerror_r", + "_strlen", + "_strncasecmp", + "_strncat", + "_strncmp", + "_strncpy", + "_strptime", + "_strtoimax", + "_strtol", + "_strtoll", + "_strtoumax", + "_tempnam", + "_time", + "_times", + "_tmpnam", + "_tsearch", + "_unlink", + "_valloc", + "_vasprintf", + "_vfprintf", + "_vfscanf", + "_vprintf", + "_vscanf", + "_vsnprintf", + "_vsprintf", + "_vsscanf", + "_wait", + "_wait$UNIX2003", + "_wait3", + "_wait4", + "_waitid", + "_waitid$UNIX2003", + "_waitpid", + "_waitpid$UNIX2003", + "_wcslen", + "_wcsnrtombs", + "_wcsrtombs", + "_wcstombs", + "_wordexp", + "_write", + "_writev", + "_writev$UNIX2003", + // always use stubs for C++ symbols that can be overridden + "__ZdaPv", + "__ZdlPv", + "__Znam", + "__Znwm", + + nullptr +}; + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + +// Handles building a list of input files to the SharedCacheBuilder itself. +class CacheInputBuilder { +public: + CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem, + const dyld3::GradedArchs& archs, dyld3::Platform reqPlatform) + : fileSystem(fileSystem), reqArchs(archs), reqPlatform(reqPlatform) { } + + // Loads and maps any MachOs in the given list of files. + void loadMachOs(std::vector& inputFiles, + std::vector& dylibsToCache, + std::vector& otherDylibs, + std::vector& executables, + std::vector& couldNotLoadFiles) { + + std::map dylibInstallNameMap; + for (CacheBuilder::InputFile& inputFile : inputFiles) { + char realerPath[MAXPATHLEN]; + dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath); + if ( (reqPlatform == dyld3::Platform::macOS) && inputFile.diag.hasError() ) { + // Try again with iOSMac + inputFile.diag.clearError(); + loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, dyld3::Platform::iOSMac, realerPath); + } + 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(); + + const char* dylibPath = inputFile.path; + if ( (installName != inputFile.path) && (reqPlatform == dyld3::Platform::macOS) ) { + // We now typically require that install names and paths match. However symlinks may allow us to bring in a path which + // doesn't match its install name. + // For example: + // /usr/lib/libstdc++.6.0.9.dylib is a real file with install name /usr/lib/libstdc++.6.dylib + // /usr/lib/libstdc++.6.dylib is a symlink to /usr/lib/libstdc++.6.0.9.dylib + // So long as we add both paths (with one as an alias) then this will work, even if dylibs are removed from disk + // but the symlink remains. + char resolvedSymlinkPath[PATH_MAX]; + if ( fileSystem.getRealPath(installName.c_str(), resolvedSymlinkPath) ) { + if (!strcmp(resolvedSymlinkPath, inputFile.path)) { + // Symlink is the install name and points to the on-disk dylib + //fprintf(stderr, "Symlink works: %s == %s\n", inputFile.path, installName.c_str()); + dylibPath = installName.c_str(); + } + } + } + + if (!ma->canBePlacedInDyldCache(dylibPath, ^(const char* msg) { + inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg); + })) { + + if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) { + inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg); + }) ) { + fileSystem.unloadFile(loadedFileInfo); + continue; + } + 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()) { + + if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) { + inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg); + }) ) { + fileSystem.unloadFile(loadedFileInfo); + continue; + } + otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile }); + } else if (ma->isDynamicExecutable()) { + + // Let the platform exclude the file before we do anything else. + if (platformExcludesExecutablePath(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); + } + } + } + +private: + + static bool platformExcludesExecutablePath_macOS(const std::string& path) { + // We no longer support ROSP, so skip all paths which start with the special prefix + if ( startsWith(path, "/System/Library/Templates/Data/") ) + return true; + + static const char* sAllowedPrefixes[] = { + "/bin/", + "/sbin/", + "/usr/", + "/System/", + "/Library/Apple/System/", + "/Library/Apple/usr/", + "/System/Applications/Safari.app/", + "/Library/CoreMediaIO/Plug-Ins/DAL/" // temp until plugins moved or closured working + }; + + bool inSearchDir = false; + for (const char* searchDir : sAllowedPrefixes ) { + if ( strncmp(searchDir, path.c_str(), strlen(searchDir)) == 0 ) { + inSearchDir = true; + break; + } + } + + return !inSearchDir; + } + + // 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 false; + case dyld3::Platform::tvOS: + return false; + case dyld3::Platform::watchOS: + return false; + case dyld3::Platform::bridgeOS: + return false; + case dyld3::Platform::iOSMac: + return platformExcludesExecutablePath_macOS(path); + case dyld3::Platform::iOS_simulator: + return false; + case dyld3::Platform::tvOS_simulator: + return false; + case dyld3::Platform::watchOS_simulator: + return false; + case dyld3::Platform::driverKit: + return false; + } + } + + const dyld3::closure::FileSystem& fileSystem; + const dyld3::GradedArchs& reqArchs; + dyld3::Platform reqPlatform; +}; + +SharedCacheBuilder::SharedCacheBuilder(const DyldSharedCache::CreateOptions& options, + const dyld3::closure::FileSystem& fileSystem) + : CacheBuilder(options, fileSystem) { + + std::string targetArch = options.archs->name(); + if ( options.forSimulator && (options.archs == &dyld3::GradedArchs::i386) ) + targetArch = "sim-x86"; + + for (const ArchLayout& layout : _s_archLayout) { + if ( layout.archName == targetArch ) { + _archLayout = &layout; + _is64 = _archLayout->is64; + break; + } + } + + if (!_archLayout) { + _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str()); + } +} + +static void verifySelfContained(const dyld3::closure::FileSystem& fileSystem, + std::vector& dylibsToCache, + std::vector& otherDylibs, + std::vector& couldNotLoadFiles) +{ + // build map of dylibs + __block std::map knownDylibs; + __block std::map 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 }); + } + + // Exclude bad unzippered twins. These are where a zippered binary links + // an unzippered twin + std::unordered_map macOSPathToTwinPath; + for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) { + macOSPathToTwinPath[dylib.mappedFile.runtimePath] = ""; + } + for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) { + if ( startsWith(dylib.mappedFile.runtimePath, "/System/iOSSupport/") ) { + std::string tail = dylib.mappedFile.runtimePath.substr(18); + if ( macOSPathToTwinPath.find(tail) != macOSPathToTwinPath.end() ) + macOSPathToTwinPath[tail] = dylib.mappedFile.runtimePath; + } + } + + __block std::map> badDylibs; + for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) { + if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 ) + continue; + if ( dylib.mappedFile.mh->isZippered() ) { + dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + auto macOSAndTwinPath = macOSPathToTwinPath.find(loadPath); + if ( macOSAndTwinPath != macOSPathToTwinPath.end() ) { + const std::string& twinPath = macOSAndTwinPath->second; + if ( badDylibs.count(twinPath) != 0 ) + return; + knownDylibs.erase(twinPath); + badDylibs[twinPath].insert(std::string("evicting UIKitForMac binary as it is linked by zippered binary '") + dylib.mappedFile.runtimePath + "'"); + } + }); + } + } + + // HACK: Exclude some dylibs and transitive deps for now until we have project fixes + __block std::set badProjects; + badProjects.insert("/System/Library/PrivateFrameworks/TuriCore.framework/Versions/A/TuriCore"); + badProjects.insert("/System/Library/PrivateFrameworks/UHASHelloExtensionPoint-macOS.framework/Versions/A/UHASHelloExtensionPoint-macOS"); + + // check all dependencies to assure every dylib in cache only depends on other dylibs in cache + __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; + if ( badProjects.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 ( badProjects.count(loadPath) != 0 ) { + // We depend on a bad dylib, so add this one to the list too + badProjects.insert(dylib.mappedFile.runtimePath); + badProjects.insert(dylib.mappedFile.mh->installName()); + knownDylibs.erase(dylib.mappedFile.runtimePath); + knownDylibs.erase(dylib.mappedFile.mh->installName()); + badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Depends on bad project '") + loadPath + "'"); + doAgain = true; + return; + } + char resolvedSymlinkPath[PATH_MAX]; + if ( knownDylibs.count(loadPath) == 0 ) { + // The loadPath was embedded when the dylib was built, but we may be in the process of moving + // a dylib with symlinks from old to new paths + // In this case, the realpath will tell us the new location + if ( fileSystem.getRealPath(loadPath, resolvedSymlinkPath) ) { + if ( strcmp(resolvedSymlinkPath, loadPath) != 0 ) { + loadPath = resolvedSymlinkPath; + } + } + } + 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 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); + } + } + }); + } + } + + // 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 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); + }); + } + + // find all dylibs not referenced + 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; + } + } + } + } + + // 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 SharedCacheBuilder::build(std::vector& inputFiles, + std::vector& aliases) { + // First filter down to files which are actually MachO's + CacheInputBuilder cacheInputBuilder(_fileSystem, *_options.archs, _options.platform); + + std::vector dylibsToCache; + std::vector otherDylibs; + std::vector executables; + std::vector couldNotLoadFiles; + cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles); + + verifySelfContained(_fileSystem, 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"; + } + } + } + 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"; + + } + } + } + if (!errorString.empty()) { + _diagnostics.error("%s", errorString.c_str()); + } + } + + 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 SharedCacheBuilder::build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables, + std::vector& aliases) { + + std::vector dylibsToCache; + std::vector otherDylibs; + std::vector 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 SharedCacheBuilder::build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables, + std::vector& aliases) +{ + // 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; + } + + _timeRecorder.pushTimedSection(); + + // 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; + } + + _timeRecorder.recordTime("sort dylibs"); + + bool impCachesSuccess = false; + IMPCaches::HoleMap selectorAddressIntervals; + _impCachesBuilder = new IMPCaches::IMPCachesBuilder(_sortedDylibs, _options.objcOptimizations, _diagnostics, _timeRecorder, _fileSystem); + + // Note, macOS allows install names and paths to mismatch. This is currently not supported by + // IMP caches as we use install names to look up the set of dylibs. + if ( _archLayout->is64 + && (_options.platform != dyld3::Platform::macOS) + && ((_impCachesBuilder->neededClasses.size() > 0) || (_impCachesBuilder->neededMetaclasses.size() > 0))) { + // Build the class map across all dylibs (including cross-image superclass references) + _impCachesBuilder->buildClassesMap(_diagnostics); + + // Determine which methods will end up in each class's IMP cache + impCachesSuccess = _impCachesBuilder->parseDylibs(_diagnostics); + + // Compute perfect hash functions for IMP caches + if (impCachesSuccess) _impCachesBuilder->buildPerfectHashes(selectorAddressIntervals, _diagnostics); + } + + constexpr bool log = false; + if (log) { + for (const auto& p : _impCachesBuilder->selectors.map) { + printf("0x%06x %s\n", p.second->offset, p.second->name); + } + } + + _timeRecorder.recordTime("compute IMP caches"); + + IMPCaches::SelectorMap emptyMap; + IMPCaches::SelectorMap& selectorMap = impCachesSuccess ? _impCachesBuilder->selectors : emptyMap; + // assign addresses for each segment of each dylib in new cache + parseCoalescableSegments(selectorMap, selectorAddressIntervals); + processSelectorStrings(osExecutables, selectorAddressIntervals); + + assignSegmentAddresses(); + std::vector overflowDylibs; + while ( cacheOverflowAmount() != 0 ) { + // IMP caches: we may need to recompute the selector addresses here to be slightly more compact + // if we remove dylibs? This is probably overkill. + + 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(); + _dataRegions.clear(); + _coalescedText.clear(); + + // Re-generate the hole map to remove any cruft that was added when parsing the coalescable text the first time. + // Always clear the hole map, even if IMP caches are off, as it is used by the text coalescer + selectorAddressIntervals.clear(); + if (impCachesSuccess) _impCachesBuilder->computeLowBits(selectorAddressIntervals); + + parseCoalescableSegments(selectorMap, selectorAddressIntervals); + processSelectorStrings(osExecutables, selectorAddressIntervals); + assignSegmentAddresses(); + + _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount); + } + markPaddingInaccessible(); + + // copy all segments into cache + + unsigned long wastedSelectorsSpace = selectorAddressIntervals.totalHoleSize(); + if (wastedSelectorsSpace > 0) { + _diagnostics.verbose("Selector placement for IMP caches wasted %lu bytes\n", wastedSelectorsSpace); + if (log) { + std::cerr << selectorAddressIntervals << std::endl; + } + } + + _timeRecorder.recordTime("layout cache"); + + writeCacheHeader(); + copyRawSegments(); + _timeRecorder.recordTime("copy cached dylibs into buffer"); + + // rebase all dylibs for new location in cache + + _aslrTracker.setDataRegion(firstDataRegion()->buffer, dataRegionsTotalSize()); + if ( !_options.cacheSupportsASLR ) + _aslrTracker.disable(); + adjustAllImagesForNewSegmentLocations(_archLayout->sharedMemoryStart, _aslrTracker, + &_lohTracker, &_coalescedText); + if ( _diagnostics.hasError() ) + return; + + _timeRecorder.recordTime("adjust segments for new split locations"); + + // build ImageArray for dyld3, which has side effect of binding all cached dylibs + buildImageArray(aliases); + if ( _diagnostics.hasError() ) + return; + + _timeRecorder.recordTime("bind all images"); + + // optimize ObjC + DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; + optimizeObjC(impCachesSuccess, _impCachesBuilder->inlinedSelectors); + + delete _impCachesBuilder; + _impCachesBuilder = nullptr; + + if ( _diagnostics.hasError() ) + return; + + _timeRecorder.recordTime("optimize Objective-C"); + + if ( _options.optimizeStubs ) { + __block std::vector> images; + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + images.push_back({ mh, installName }); + }); + + int64_t cacheSlide = (long)dyldCache - dyldCache->unslidLoadAddress(); + uint64_t cacheUnslideAddr = dyldCache->unslidLoadAddress(); + optimizeAwayStubs(images, cacheSlide, cacheUnslideAddr, + dyldCache, _s_neverStubEliminateSymbols); + } + + + // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing) + fipsSign(); + + _timeRecorder.recordTime("do stub elimination"); + + // merge and compact LINKEDIT segments + { + // If we want to remove, not just unmap locals, then set the dylibs themselves to be stripped + DylibStripMode dylibStripMode = DylibStripMode::stripNone; + if ( _options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::strip ) + dylibStripMode = CacheBuilder::DylibStripMode::stripLocals; + + __block std::vector> images; + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + images.push_back({ mh, installName, dylibStripMode }); + }); + optimizeLinkedit(&_localSymbolsRegion, images); + } + + // copy ImageArray to end of read-only region + addImageArray(); + if ( _diagnostics.hasError() ) + return; + + _timeRecorder.recordTime("optimize LINKEDITs"); + + // don't add dyld3 closures to simulator cache or the base system where size is more of an issue + if ( _options.optimizeDyldDlopens ) { + // compute and add dlopen closures for all other dylibs + addOtherImageArray(otherOsDylibsInput, overflowDylibs); + if ( _diagnostics.hasError() ) + return; + } + if ( _options.optimizeDyldLaunches ) { + // compute and add launch closures to end of read-only region + addClosures(osExecutables); + if ( _diagnostics.hasError() ) + return; + } + + // update final readOnly region size + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset); + mappings[dyldCache->header.mappingCount - 1].size = _readOnlyRegion.sizeInUse; + dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingWithSlideOffset); + slidableMappings[dyldCache->header.mappingCount - 1].size = _readOnlyRegion.sizeInUse; + if ( _localSymbolsRegion.sizeInUse != 0 ) { + dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse; + dyldCache->header.localSymbolsSize = _localSymbolsRegion.sizeInUse; + } + + // 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 = DISCONTIGUOUS_RX_SIZE - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region + uint64_t maxSlide1 = DISCONTIGUOUS_RW_SIZE - dataRegionsTotalSize(); + uint64_t maxSlide2 = DISCONTIGUOUS_RO_SIZE - _readOnlyRegion.sizeInUse; + dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2); + } + else { + // branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB + if ( (_archLayout->sharedMemorySize == 0x100000000) && (_readExecuteRegion.sizeInUse < 0x80000000) ) + dyldCache->header.maxSlide = 0x80000000 - _readExecuteRegion.sizeInUse; + else + dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse); + } + + // mark if any input dylibs were built with chained fixups + dyldCache->header.builtFromChainedFixups = _someDylibsUsedChainedFixups; + + _timeRecorder.recordTime("build %lu closures", osExecutables.size()); + // Emit the CF strings without their ISAs being signed + // This must be after addImageArray() as it depends on hasImageIndex(). + // It also has to be before emitting slide info as it adds ASLR entries. + emitContantObjects(); + + _timeRecorder.recordTime("emit constant objects"); + + // 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>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); +#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k + else if ( _archLayout->pointerDeltaMask == 0xC0000000 ) + writeSlideInfoV4>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); +#endif + else + writeSlideInfoV2>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount()); + } + + _timeRecorder.recordTime("compute slide info"); + + // last sanity check on size + if ( cacheOverflowAmount() != 0 ) { + _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse); + return; + } + + // codesignature is part of file, but is not mapped + codeSign(); + if ( _diagnostics.hasError() ) + return; + + _timeRecorder.recordTime("compute UUID and codesign cache file"); + + if (_options.verbose) { + _timeRecorder.logTimings(); + } + + return; +} + +const std::set SharedCacheBuilder::warnings() +{ + return _diagnostics.warnings(); +} + +const std::set SharedCacheBuilder::evictions() +{ + return _evictions; +} + +void SharedCacheBuilder::deleteBuffer() +{ + // Cache buffer + if ( _allocatedBufferSize != 0 ) { + vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _allocatedBufferSize); + _fullAllocatedBuffer = 0; + _allocatedBufferSize = 0; + } + // Local symbols buffer + if ( _localSymbolsRegion.bufferSize != 0 ) { + vm_deallocate(mach_task_self(), (vm_address_t)_localSymbolsRegion.buffer, _localSymbolsRegion.bufferSize); + _localSymbolsRegion.buffer = 0; + _localSymbolsRegion.bufferSize = 0; + } + // Code signatures + if ( _codeSignatureRegion.bufferSize != 0 ) { + vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize); + _codeSignatureRegion.buffer = 0; + _codeSignatureRegion.bufferSize = 0; + } +} + + +void SharedCacheBuilder::makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder) +{ + for (const LoadedMachO& dylib : dylibs) { + _sortedDylibs.push_back({ &dylib, dylib.mappedFile.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()); + + // 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; + + // Sort mac before iOSMac + bool isIOSMacA = strncmp(a.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0; + bool isIOSMacB = strncmp(b.input->mappedFile.runtimePath.c_str(), "/System/iOSSupport/", 19) == 0; + if (isIOSMacA != isIOSMacB) + return !isIOSMacA; + + // Finally sort by path + return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath; + }); +} + +struct DylibAndSize +{ + const CacheBuilder::LoadedMachO* input; + const char* installName; + uint64_t size; +}; + +uint64_t SharedCacheBuilder::cacheOverflowAmount() +{ + if ( _archLayout->sharedRegionsAreDiscontiguous ) { + // for macOS x86_64 cache, need to check each region for overflow + if ( _readExecuteRegion.sizeInUse > DISCONTIGUOUS_RX_SIZE ) + return (_readExecuteRegion.sizeInUse - DISCONTIGUOUS_RX_SIZE); + + uint64_t dataSize = dataRegionsTotalSize(); + if ( dataSize > DISCONTIGUOUS_RW_SIZE ) + return (dataSize - DISCONTIGUOUS_RW_SIZE); + + if ( _readOnlyRegion.sizeInUse > DISCONTIGUOUS_RO_SIZE ) + return (_readOnlyRegion.sizeInUse - DISCONTIGUOUS_RO_SIZE); + } + else { + bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize); + uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress; + if ( alreadyOptimized ) + vmSize += _readOnlyRegion.sizeInUse; + else if ( _options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::unmap ) + vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 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; +} + +size_t SharedCacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector& overflowDylibs) +{ + // build a reverse map of all dylib dependencies + __block std::map> references; + std::map>* referencesPtr = &references; + for (const DylibInfo& dylib : _sortedDylibs) { + // Esnure we have an entry (even if it is empty) + if (references.count(dylib.input->mappedFile.mh->installName()) == 0) { + references[dylib.input->mappedFile.mh->installName()] = std::set(); + }; + dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + references[loadPath].insert(dylib.input->mappedFile.mh->installName()); + }); + } + + // Find the sizes of all the dylibs + std::vector dylibsToSort; + std::vector sortedDylibs; + for (const DylibInfo& dylib : _sortedDylibs) { + const char* installName = dylib.input->mappedFile.mh->installName(); + __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; + }); + dylibsToSort.push_back({ dylib.input, installName, segsSize }); + } + + // Build an ordered list of what to remove. At each step we do following + // 1) Find all dylibs that nothing else depends on + // 2a) If any of those dylibs are not in the order select the largest one of them + // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file + // 3) Remove all entries to the removed file from the reverse dependency map + // 4) Go back to one and repeat until there are no more evictable dylibs + // This results in us always choosing the locally optimal selection, and then taking into account how that impacts + // the dependency graph for subsequent selections + + bool candidateFound = true; + while (candidateFound) { + candidateFound = false; + DylibAndSize candidate; + uint64_t candidateOrder = 0; + for(const auto& dylib : dylibsToSort) { + const auto& i = referencesPtr->find(dylib.installName); + assert(i != referencesPtr->end()); + if (!i->second.empty()) { + continue; + } + const auto& j = _options.dylibOrdering.find(dylib.input->mappedFile.runtimePath); + uint64_t order = 0; + if (j != _options.dylibOrdering.end()) { + order = j->second; + } else { + // Not in the order file, set order sot it goes to the front of the list + order = UINT64_MAX; + } + if (order > candidateOrder || + (order == UINT64_MAX && candidate.size < dylib.size)) { + // The new file is either a lower priority in the order file + // or the same priority as the candidate but larger + candidate = dylib; + candidateOrder = order; + candidateFound = true; + } + } + if (candidateFound) { + sortedDylibs.push_back(candidate); + referencesPtr->erase(candidate.installName); + for (auto& dependent : references) { + (void)dependent.second.erase(candidate.installName); + } + auto j = std::find_if(dylibsToSort.begin(), dylibsToSort.end(), [&candidate](const DylibAndSize& dylib) { + return (strcmp(candidate.installName, dylib.installName) == 0); + }); + if (j != dylibsToSort.end()) { + dylibsToSort.erase(j); + } + } + } + + // build set of dylibs that if removed will allow cache to build + for (DylibAndSize& dylib : sortedDylibs) { + 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; + } + + // 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(); +} + + +void SharedCacheBuilder::writeCacheHeader() +{ + // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes + std::string magic = "dyld_v1"; + magic.append(15 - magic.length() - strlen(_options.archs->name()), ' '); + magic.append(_options.archs->name()); + assert(magic.length() == 15); + + // 1 __TEXT segment, n __DATA segments, and 1 __LINKEDIT segment + const uint32_t mappingCount = 2 + (uint32_t)_dataRegions.size(); + assert(mappingCount <= DyldSharedCache::MaxMappings); + + // fill in header + dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; + memcpy(dyldCacheHeader->magic, magic.c_str(), 16); + dyldCacheHeader->mappingOffset = sizeof(dyld_cache_header); + dyldCacheHeader->mappingCount = mappingCount; + dyldCacheHeader->mappingWithSlideOffset = (uint32_t)(dyldCacheHeader->mappingOffset + mappingCount*sizeof(dyld_cache_mapping_and_slide_info)); + dyldCacheHeader->mappingWithSlideCount = mappingCount; + dyldCacheHeader->imagesOffset = (uint32_t)(dyldCacheHeader->mappingWithSlideOffset + mappingCount*sizeof(dyld_cache_mapping_and_slide_info)); + dyldCacheHeader->imagesCount = (uint32_t)_sortedDylibs.size() + _aliasCount; + dyldCacheHeader->dyldBaseAddress = 0; + dyldCacheHeader->codeSignatureOffset = 0; + dyldCacheHeader->codeSignatureSize = 0; + dyldCacheHeader->slideInfoOffsetUnused = 0; + dyldCacheHeader->slideInfoSizeUnused = 0; + 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 = 0; + dyldCacheHeader->branchPoolsCount = 0; + dyldCacheHeader->imagesTextOffset = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount; + dyldCacheHeader->imagesTextCount = _sortedDylibs.size(); + dyldCacheHeader->patchInfoAddr = 0; + dyldCacheHeader->patchInfoSize = 0; + dyldCacheHeader->otherImageGroupAddrUnused = 0; + dyldCacheHeader->otherImageGroupSizeUnused = 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->builtFromChainedFixups= false; + 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); + assert(_readExecuteRegion.cacheFileOffset == 0); + mappings[0].address = _readExecuteRegion.unslidLoadAddress; + mappings[0].fileOffset = _readExecuteRegion.cacheFileOffset; + mappings[0].size = _readExecuteRegion.sizeInUse; + mappings[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE; + mappings[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE; + for (uint32_t i = 0; i != _dataRegions.size(); ++i) { + if ( i == 0 ) { + assert(_dataRegions[i].cacheFileOffset == _readExecuteRegion.sizeInUse); + } + + assert(_dataRegions[i].initProt != 0); + assert(_dataRegions[i].maxProt != 0); + + mappings[i + 1].address = _dataRegions[i].unslidLoadAddress; + mappings[i + 1].fileOffset = _dataRegions[i].cacheFileOffset; + mappings[i + 1].size = _dataRegions[i].sizeInUse; + mappings[i + 1].maxProt = _dataRegions[i].maxProt; + mappings[i + 1].initProt = _dataRegions[i].initProt; + } + assert(_readOnlyRegion.cacheFileOffset == (_dataRegions.back().cacheFileOffset + _dataRegions.back().sizeInUse)); + mappings[mappingCount - 1].address = _readOnlyRegion.unslidLoadAddress; + mappings[mappingCount - 1].fileOffset = _readOnlyRegion.cacheFileOffset; + mappings[mappingCount - 1].size = _readOnlyRegion.sizeInUse; + mappings[mappingCount - 1].maxProt = VM_PROT_READ; + mappings[mappingCount - 1].initProt = VM_PROT_READ; + + // Add in the new mappings with also have slide info + dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingWithSlideOffset); + slidableMappings[0].address = _readExecuteRegion.unslidLoadAddress; + slidableMappings[0].fileOffset = _readExecuteRegion.cacheFileOffset; + slidableMappings[0].size = _readExecuteRegion.sizeInUse; + slidableMappings[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE; + slidableMappings[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE; + slidableMappings[0].slideInfoFileOffset = 0; + slidableMappings[0].slideInfoFileSize = 0; + slidableMappings[0].flags = 0; + for (uint32_t i = 0; i != _dataRegions.size(); ++i) { + // Work out which flags this mapping has + uint64_t flags = 0; + if ( startsWith(_dataRegions[i].name, "__AUTH") ) + flags |= DYLD_CACHE_MAPPING_AUTH_DATA; + if ( (_dataRegions[i].name == "__AUTH_DIRTY") || (_dataRegions[i].name == "__DATA_DIRTY") ) { + flags |= DYLD_CACHE_MAPPING_DIRTY_DATA; + } else if ( (_dataRegions[i].name == "__AUTH_CONST") || (_dataRegions[i].name == "__DATA_CONST") ) { + flags |= DYLD_CACHE_MAPPING_CONST_DATA; + } + + assert(_dataRegions[i].initProt != 0); + assert(_dataRegions[i].maxProt != 0); + + slidableMappings[i + 1].address = _dataRegions[i].unslidLoadAddress; + slidableMappings[i + 1].fileOffset = _dataRegions[i].cacheFileOffset; + slidableMappings[i + 1].size = _dataRegions[i].sizeInUse; + slidableMappings[i + 1].maxProt = _dataRegions[i].maxProt; + slidableMappings[i + 1].initProt = _dataRegions[i].initProt; + slidableMappings[i + 1].slideInfoFileOffset = _dataRegions[i].slideInfoFileOffset; + slidableMappings[i + 1].slideInfoFileSize = _dataRegions[i].slideInfoFileSize; + slidableMappings[i + 1].flags = flags; + } + slidableMappings[mappingCount - 1].address = _readOnlyRegion.unslidLoadAddress; + slidableMappings[mappingCount - 1].fileOffset = _readOnlyRegion.cacheFileOffset; + slidableMappings[mappingCount - 1].size = _readOnlyRegion.sizeInUse; + slidableMappings[mappingCount - 1].maxProt = VM_PROT_READ; + slidableMappings[mappingCount - 1].initProt = VM_PROT_READ; + slidableMappings[mappingCount - 1].slideInfoFileOffset = 0; + slidableMappings[mappingCount - 1].slideInfoFileSize = 0; + slidableMappings[mappingCount - 1].flags = 0; + + // fill in image table + 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.input->mappedFile.modTime; + images->inode = dylib.input->mappedFile.inode; + } + 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 +/* + for (auto &dylib : _dylibs) { + if (!dylib->installNameAliases.empty()) { + for (const std::string& alias : dylib->installNameAliases) { + images->set_address(_segmentMap[dylib][0].address); + if (_manifest.platform() == "osx") { + images->modTime = dylib->lastModTime; + images->inode = dylib->inode; + } + else { + images->modTime = 0; + images->inode = pathHash(alias.c_str()); + } + images->pathFileOffset = offset; + //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str()); + ::strcpy((char*)&_buffer[offset], alias.c_str()); + offset += alias.size() + 1; + ++images; + } + } + } +*/ + // calculate start of text image array and trailing string pool + dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_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 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 = 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*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset); + assert(stringOffset <= (firstImage->address - mappings[0].address)); +} + +void SharedCacheBuilder::processSelectorStrings(const std::vector& executables, IMPCaches::HoleMap& selectorsHoleMap) { + const bool log = false; + + // We only do this optimisation to reduce the size of the shared cache executable closures + // Skip this is those closures are not being built + if ( !_options.optimizeDyldDlopens || !_options.optimizeDyldLaunches ) + return; + + _selectorStringsFromExecutables = 0; + uint64_t totalBytesPulledIn = 0; + + // Don't do this optimisation on watchOS where the shared cache is too small + if (_options.platform == dyld3::Platform::watchOS) + return; + + // Get the method name coalesced section as that is where we need to put these strings + CacheBuilder::CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData("__objc_methname"); + for (const LoadedMachO& executable : executables) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)executable.loadedFileInfo.fileContent; + + uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize; + + ma->forEachObjCMethodName(^(const char* methodName) { + std::string_view str = methodName; + if (cacheStringSection.stringsToOffsets.find(str) == cacheStringSection.stringsToOffsets.end()) { + int offset = selectorsHoleMap.addStringOfSize((unsigned)str.size() + 1); + cacheStringSection.stringsToOffsets[str] = offset; + + // If we inserted the string past the end then we need to include it in the total + int possibleNewEnd = offset + (int)str.size() + 1; + if (cacheStringSection.bufferSize < (uint32_t)possibleNewEnd) { + cacheStringSection.bufferSize = (uint32_t)possibleNewEnd; + } + // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName); + ++_selectorStringsFromExecutables; + } + }); + + uint64_t sizeAfterProcessing = cacheStringSection.bufferSize; + totalBytesPulledIn += (sizeAfterProcessing - sizeBeforeProcessing); + if ( log && (sizeBeforeProcessing != sizeAfterProcessing) ) { + printf("Pulled in % 6lld bytes of selectors from %s\n", + sizeAfterProcessing - sizeBeforeProcessing, executable.loadedFileInfo.path); + } + } + + _diagnostics.verbose("Pulled in %lld selector strings (%lld bytes) from executables\n", + _selectorStringsFromExecutables, totalBytesPulledIn); +} + +void SharedCacheBuilder::parseCoalescableSegments(IMPCaches::SelectorMap& selectors, IMPCaches::HoleMap& selectorsHoleMap) { + const bool log = false; + + for (DylibInfo& dylib : _sortedDylibs) + _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer, selectors, selectorsHoleMap); + + if (log) { + for (const char* section : CacheCoalescedText::SupportedSections) { + CacheCoalescedText::StringSection& sectionData = _coalescedText.getSectionData(section); + printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section, + sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace); + } + } + + // arm64e needs to convert CF constants to tagged pointers + if ( !strcmp(_archLayout->archName, "arm64e") ) { + // Find the dylib which exports the CFString ISA. It's likely CoreFoundation but it could move + CacheCoalescedText::CFSection& cfStrings = _coalescedText.cfStrings; + for (DylibInfo& dylib : _sortedDylibs) { + const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh; + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + bool foundISASymbol = ma->findExportedSymbol(_diagnostics, cfStrings.isaClassName, false, foundInfo, nullptr); + if ( foundISASymbol ) { + // This dylib exports the ISA, so everyone else should look here for the ISA too. + if ( cfStrings.isaInstallName != nullptr ) { + // Found a duplicate. We can't do anything here + _diagnostics.verbose("Could not optimize CFString's due to duplicate ISA symbols"); + cfStrings.isaInstallName = nullptr; + break; + } else { + cfStrings.isaInstallName = ma->installName(); + cfStrings.isaVMOffset = foundInfo.value; + } + } + } + if ( cfStrings.isaInstallName != nullptr ) { + for (DylibInfo& dylib : _sortedDylibs) { + _coalescedText.parseCFConstants(dylib.input->mappedFile.mh, dylib.textCoalescer); + } + } + } +} + +// This is the new method which will put all __DATA* mappings in to a their own mappings +void SharedCacheBuilder::assignMultipleDataSegmentAddresses(uint64_t& addr, uint32_t totalProtocolDefCount) { + uint64_t nextRegionFileOffset = _readExecuteRegion.sizeInUse; + + const size_t dylibCount = _sortedDylibs.size(); + BLOCK_ACCCESSIBLE_ARRAY(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; + }); + + bool supportsAuthFixups = false; + + // This tracks which segments contain authenticated data, even if their name isn't __AUTH* + std::set authenticatedSegments[dylibCount]; + if ( strcmp(_archLayout->archName, "arm64e") == 0 ) { + supportsAuthFixups = true; + + for (DylibInfo& dylib : _sortedDylibs) { + uint64_t dylibIndex = &dylib - _sortedDylibs.data(); + __block std::set& authSegmentIndices = authenticatedSegments[dylibIndex]; + + // Put all __DATA_DIRTY segments in the __AUTH region first, then we don't need to walk their chains + dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 ) { + authSegmentIndices.insert(segInfo.segIndex); + stop = true; + } + }); + dylib.input->mappedFile.mh->withChainStarts(_diagnostics, 0, + ^(const dyld_chained_starts_in_image *starts) { + dylib.input->mappedFile.mh->forEachFixupChainSegment(_diagnostics, starts, + ^(const dyld_chained_starts_in_segment* segmentInfo, uint32_t segIndex, bool& stopSegment) { + // Skip walking segments we already know are __AUTH, ie, __DATA_DIRTY + if ( authSegmentIndices.count(segIndex) ) + return; + + dylib.input->mappedFile.mh->forEachFixupInSegmentChains(_diagnostics, segmentInfo, false, + ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stopChain) { + uint16_t chainedFixupsFormat = segInfo->pointer_format; + assert( (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E) || (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND) || (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ); + + if ( fixupLoc->arm64e.authRebase.auth ) { + authSegmentIndices.insert(segIndex); + stopChain = true; + return; + } + }); + }); + }); + } + } + + // Categorize each segment in each binary + enum class SegmentType : uint8_t { + skip, // used for non-data segments we should ignore here + data, + dataDirty, + dataConst, + auth, + authDirty, + authConst, + }; + + BLOCK_ACCCESSIBLE_ARRAY(uint64_t, textSegVmAddrs, dylibCount); + BLOCK_ACCCESSIBLE_ARRAY(std::vector, segmentTypes, dylibCount); + + // Just in case __AUTH is used in a non-arm64e binary, we can force it to use data enums + SegmentType authSegment = supportsAuthFixups ? SegmentType::auth : SegmentType::data; + SegmentType authConstSegment = supportsAuthFixups ? SegmentType::authConst : SegmentType::dataConst; + + for (const DylibInfo& dylib : _sortedDylibs) { + uint64_t dylibIndex = &dylib - _sortedDylibs.data(); + __block std::set& authSegmentIndices = authenticatedSegments[dylibIndex]; + __block std::vector& dylibSegmentTypes = segmentTypes[dylibIndex]; + uint64_t &textSegVmAddr = textSegVmAddrs[dylibIndex]; + dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + if ( strcmp(segInfo.segName, "__TEXT") == 0 ) { + textSegVmAddr = segInfo.vmAddr; + } + + // Skip non-DATA segments + if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) ) { + dylibSegmentTypes.push_back(SegmentType::skip); + return; + } + + // If we don't have split seg v2, then all remaining segments must look like __DATA so that they + // stay contiguous + if (!dylib.input->mappedFile.mh->isSplitSegV2()) { + dylibSegmentTypes.push_back(SegmentType::data); + return; + } + + __block bool supportsDataConst = true; + if ( dylib.input->mappedFile.mh->isSwiftLibrary() ) { + uint64_t objcConstSize = 0; + bool containsObjCSection = dylib.input->mappedFile.mh->findSectionContent(segInfo.segName, "__objc_const", objcConstSize); + + // Don't put __objc_const read-only memory as Swift has method lists we can't see + if ( containsObjCSection ) + supportsDataConst = false; + } else if ( !strcmp(dylib.input->mappedFile.mh->installName(), "/System/Library/Frameworks/Foundation.framework/Foundation") || + !strcmp(dylib.input->mappedFile.mh->installName(), "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") ) { + // _NSTheOneTruePredicate is incompatible with __DATA_CONST + supportsDataConst = false; + } else if ( !strcmp(dylib.input->mappedFile.mh->installName(), "/usr/lib/system/libdispatch.dylib") ) { + // rdar://72361509 (Speechrecognitiond crashing on AzulE18E123) + supportsDataConst = false; + } else if ( !strcmp(dylib.input->mappedFile.mh->installName(), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation") || + !strcmp(dylib.input->mappedFile.mh->installName(), "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation") ) { + // rdar://74112547 CF writes to kCFNull constant object + supportsDataConst = false; + } + + // Don't use data const for dylibs containing resolver functions. This will be fixed in ld64 by moving their pointer atoms to __DATA + if ( supportsDataConst && endsWith(segInfo.segName, "_CONST") ) { + dylib.input->mappedFile.mh->forEachExportedSymbol(_diagnostics, + ^(const char *symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char *importName, bool &stop) { + if ( (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) != 0 ) { + _diagnostics.verbose("%s: preventing use of __DATA_CONST due to resolvers\n", dylib.dylibID.c_str()); + supportsDataConst = false; + stop = true; + } + }); + } + + // If we are still allowed to use __DATA_CONST, then make sure that we are not using pointer based method lists. These may not be written in libobjc due + // to uniquing or sorting (as those are done in the builder), but clients can still call setIMP to mutate them. + if ( supportsDataConst && endsWith(segInfo.segName, "_CONST") ) { + uint64_t segStartVMAddr = segInfo.vmAddr; + uint64_t segEndVMAddr = segInfo.vmAddr + segInfo.vmSize; + + auto vmAddrConverter = dylib.input->mappedFile.mh->makeVMAddrConverter(false); + const uint32_t pointerSize = dylib.input->mappedFile.mh->pointerSize(); + + __block bool foundPointerBasedMethodList = false; + auto visitMethodList = ^(uint64_t methodListVMAddr) { + if ( foundPointerBasedMethodList ) + return; + if ( methodListVMAddr == 0 ) + return; + // Ignore method lists in other segments + if ( (methodListVMAddr < segStartVMAddr) || (methodListVMAddr >= segEndVMAddr) ) + return; + auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { }; + bool isRelativeMethodList = false; + dylib.input->mappedFile.mh->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod, &isRelativeMethodList); + if ( !isRelativeMethodList ) + foundPointerBasedMethodList = true; + }; + + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + visitMethodList(objcClass.baseMethodsVMAddr(pointerSize)); + }; + + auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr, + const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + visitMethodList(objcCategory.instanceMethodsVMAddr); + visitMethodList(objcCategory.classMethodsVMAddr); + }; + + // Walk the class list + Diagnostics classDiag; + dylib.input->mappedFile.mh->forEachObjCClass(classDiag, vmAddrConverter, visitClass); + + // Walk the category list + Diagnostics categoryDiag; + dylib.input->mappedFile.mh->forEachObjCCategory(categoryDiag, vmAddrConverter, visitCategory); + + // Note we don't walk protocols as they don't have an IMP to set + + if ( foundPointerBasedMethodList ) { + _diagnostics.verbose("%s: preventing use of read-only %s due to pointer based method list\n", dylib.dylibID.c_str(), segInfo.segName); + supportsDataConst = false; + } + } + + // __AUTH_CONST + if ( strcmp(segInfo.segName, "__AUTH_CONST") == 0 ) { + dylibSegmentTypes.push_back(supportsDataConst ? authConstSegment : authSegment); + return; + } + + // __DATA_CONST + if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0) || (strcmp(segInfo.segName, "__OBJC_CONST") == 0) ) { + if ( authSegmentIndices.count(segInfo.segIndex) ) { + // _diagnostics.verbose("%s: treating authenticated %s as __AUTH_CONST\n", dylib.dylibID.c_str(), segInfo.segName); + dylibSegmentTypes.push_back(supportsDataConst ? SegmentType::authConst : SegmentType::auth); + } else { + dylibSegmentTypes.push_back(supportsDataConst ? SegmentType::dataConst : SegmentType::data); + } + return; + } + + // __DATA_DIRTY + if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 ) { + if ( authSegmentIndices.count(segInfo.segIndex) ) { + dylibSegmentTypes.push_back(SegmentType::authDirty); + } else { + dylibSegmentTypes.push_back(SegmentType::dataDirty); + } + return; + } + + // __AUTH + if ( strcmp(segInfo.segName, "__AUTH") == 0 ) { + dylibSegmentTypes.push_back(authSegment); + return; + } + + // DATA + if ( authSegmentIndices.count(segInfo.segIndex) ) { + // _diagnostics.verbose("%s: treating authenticated %s as __AUTH\n", dylib.dylibID.c_str(), segInfo.segName); + dylibSegmentTypes.push_back(SegmentType::auth); + } else { + dylibSegmentTypes.push_back(SegmentType::data); + } + }); + } + + auto processDylibSegments = ^(SegmentType onlyType, Region& region) { + for (size_t unsortedDylibIndex = 0; unsortedDylibIndex != dylibCount; ++unsortedDylibIndex) { + size_t dylibIndex = unsortedDylibIndex; + if ( (onlyType == SegmentType::dataDirty) || (onlyType == SegmentType::authDirty) ) + dylibIndex = dirtyDataSortIndexes[dylibIndex]; + + DylibInfo& dylib = _sortedDylibs[dylibIndex]; + const std::vector& dylibSegmentTypes = segmentTypes[dylibIndex]; + const uint64_t textSegVmAddr = textSegVmAddrs[dylibIndex]; + + bool forcePageAlignedData = false; + if ( (_options.platform == dyld3::Platform::macOS) && (onlyType == SegmentType::data) ) { + forcePageAlignedData = dylib.input->mappedFile.mh->hasUnalignedPointerFixups(); + //if ( forcePageAlignedData ) + // warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str()); + } + + dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) { + if ( dylibSegmentTypes[segInfo.segIndex] != onlyType ) + return; + + // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them. + __block size_t sizeOfSections = 0; + __block bool foundCoalescedSection = false; + dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stopSection) { + if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0) + return; + if ( dylib.textCoalescer.sectionWasCoalesced(segInfo.segName, sectInfo.sectName)) { + foundCoalescedSection = true; + } else { + sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr; + } + }); + if (!foundCoalescedSection) + sizeOfSections = segInfo.sizeOfSections; + + if ( !forcePageAlignedData ) { + // Pack __DATA segments + addr = align(addr, segInfo.p2align); + } + else { + // Keep __DATA segments 4K or more aligned + addr = align(addr, std::max((int)segInfo.p2align, (int)12)); + } + + size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections); + uint64_t offsetInRegion = addr - region.unslidLoadAddress; + SegmentMappingInfo loc; + loc.srcSegment = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr; + loc.segName = segInfo.segName; + loc.dstSegment = region.buffer + offsetInRegion; + loc.dstCacheUnslidAddress = addr; + loc.dstCacheFileOffset = (uint32_t)(region.cacheFileOffset + offsetInRegion); + loc.dstCacheSegmentSize = (uint32_t)sizeOfSections; + loc.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + dylib.cacheLocation.push_back(loc); + addr += loc.dstCacheSegmentSize; + }); + } + + // align region end + addr = align(addr, _archLayout->sharedRegionAlignP2); + }; + + struct DataRegion { + const char* regionName; + SegmentType dataSegment; + std::optional dirtySegment; + // Note this is temporary as once all platforms/archs support __DATA_CONST, we can move to a DataRegion just for CONST + std::optional dataConstSegment; + bool addCFStrings; + bool addObjCRW; + }; + std::vector dataRegions; + + // We only support __DATA_CONST on arm64(e) for now. + bool supportDataConst = false; + //supportDataConst |= strcmp(_archLayout->archName, "arm64") == 0; + supportDataConst |= strcmp(_archLayout->archName, "arm64e") == 0; + if ( supportDataConst ) { + bool addObjCRWToData = !supportsAuthFixups; + DataRegion dataWriteRegion = { "__DATA", SegmentType::data, SegmentType::dataDirty, {}, false, addObjCRWToData }; + DataRegion dataConstRegion = { "__DATA_CONST", SegmentType::dataConst, {}, {}, true, false }; + DataRegion authWriteRegion = { "__AUTH", SegmentType::auth, SegmentType::authDirty, {}, false, !addObjCRWToData }; + DataRegion authConstRegion = { "__AUTH_CONST", SegmentType::authConst, {}, {}, false, false }; + dataRegions.push_back(dataWriteRegion); + dataRegions.push_back(dataConstRegion); + if ( supportsAuthFixups ) { + dataRegions.push_back(authWriteRegion); + dataRegions.push_back(authConstRegion); + } + } else { + DataRegion dataWriteRegion = { "__DATA", SegmentType::data, SegmentType::dataDirty, SegmentType::dataConst, false, true }; + dataRegions.push_back(dataWriteRegion); + } + + for (DataRegion& dataRegion : dataRegions) + { + Region region; + region.buffer = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart; + region.bufferSize = 0; + region.sizeInUse = 0; + region.unslidLoadAddress = addr; + region.cacheFileOffset = nextRegionFileOffset; + region.name = dataRegion.regionName; + region.initProt = endsWith(dataRegion.regionName, "_CONST") ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE); + region.maxProt = VM_PROT_READ | VM_PROT_WRITE; + + // layout all __DATA_DIRTY segments, sorted (FIXME) + if (dataRegion.dirtySegment.has_value()) + processDylibSegments(*dataRegion.dirtySegment, region); + + // layout all __DATA segments (and other r/w non-dirty, non-const, non-auth) segments + processDylibSegments(dataRegion.dataSegment, region); + + // When __DATA_CONST is not its own DataRegion, we fold it in to the __DATA DataRegion + if (dataRegion.dataConstSegment.has_value()) + processDylibSegments(*dataRegion.dataConstSegment, region); + + // Make space for the cfstrings + if ( (dataRegion.addCFStrings) && (_coalescedText.cfStrings.bufferSize != 0) ) { + // Keep __DATA segments 4K or more aligned + addr = align(addr, 12); + uint64_t offsetInRegion = addr - region.unslidLoadAddress; + + CacheCoalescedText::CFSection& cacheSection = _coalescedText.cfStrings; + cacheSection.bufferAddr = region.buffer + offsetInRegion; + cacheSection.bufferVMAddr = addr; + cacheSection.cacheFileOffset = region.cacheFileOffset + offsetInRegion; + addr += cacheSection.bufferSize; + } + + if ( dataRegion.addObjCRW ) { + // reserve space for objc r/w optimization tables + _objcReadWriteBufferSizeAllocated = align(computeReadWriteObjC((uint32_t)_sortedDylibs.size(), totalProtocolDefCount), 14); + addr = align(addr, 4); // objc r/w section contains pointer and must be at least pointer align + _objcReadWriteBuffer = region.buffer + (addr - region.unslidLoadAddress); + _objcReadWriteFileOffset = (uint32_t)((_objcReadWriteBuffer - region.buffer) + region.cacheFileOffset); + addr += _objcReadWriteBufferSizeAllocated; + + + // align region end + addr = align(addr, _archLayout->sharedRegionAlignP2); + } + + // align DATA region end + uint64_t endDataAddress = addr; + region.bufferSize = endDataAddress - region.unslidLoadAddress; + region.sizeInUse = region.bufferSize; + + _dataRegions.push_back(region); + nextRegionFileOffset = region.cacheFileOffset + region.sizeInUse; + + // Only arm64 and arm64e shared caches have enough space to pad between __DATA and __DATA_CONST + // All other caches are overflowing. + if ( !strcmp(_archLayout->archName, "arm64") || !strcmp(_archLayout->archName, "arm64e") ) + addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2); + } + + // Sanity check that we didn't put the same segment in 2 different ranges + for (DylibInfo& dylib : _sortedDylibs) { + std::unordered_set seenSegmentIndices; + for (SegmentMappingInfo& segmentInfo : dylib.cacheLocation) { + if ( seenSegmentIndices.count(segmentInfo.srcSegmentIndex) != 0 ) { + _diagnostics.error("%s segment %s was duplicated in layout", + dylib.input->mappedFile.mh->installName(), segmentInfo.segName); + return; + } + seenSegmentIndices.insert(segmentInfo.srcSegmentIndex); + } + } +} + +void SharedCacheBuilder::assignSegmentAddresses() +{ + // calculate size of header info and where first dylib's mach_header should start + size_t startOffset = sizeof(dyld_cache_header) + DyldSharedCache::MaxMappings * sizeof(dyld_cache_mapping_info); + startOffset += DyldSharedCache::MaxMappings * sizeof(dyld_cache_mapping_and_slide_info); + 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); + + // HACK!: Rebase v4 assumes that values below 0x8000 are not pointers (encoding as offsets from the cache header). + // If using a minimal cache, we need to pad out the cache header to make sure a pointer doesn't fall within that range +#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k + if ( _options.cacheSupportsASLR && !_archLayout->is64 ) { + if ( _archLayout->pointerDeltaMask == 0xC0000000 ) + startOffset = std::max(startOffset, (size_t)0x8000); + } +#endif + + // assign TEXT segment addresses + _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 + 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; + // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them. + __block size_t sizeOfSections = 0; + __block bool foundCoalescedSection = false; + dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stopSection) { + if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0) + return; + if ( dylib.textCoalescer.sectionWasCoalesced(segInfo.segName, sectInfo.sectName)) { + foundCoalescedSection = true; + } else { + sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr; + } + }); + if (!foundCoalescedSection) + sizeOfSections = segInfo.sizeOfSections; + + // Keep __TEXT segments 4K or more aligned + 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(sizeOfSections, 12); + loc.dstCacheFileSize = (uint32_t)align(sizeOfSections, 12); + loc.copySegmentSize = (uint32_t)sizeOfSections; + loc.srcSegmentIndex = segInfo.segIndex; + dylib.cacheLocation.push_back(loc); + addr += loc.dstCacheSegmentSize; + }); + } + + // reserve space for objc optimization tables and deduped strings + uint64_t objcReadOnlyBufferVMAddr = addr; + _objcReadOnlyBuffer = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress); + + // First the strings as we'll fill in the objc tables later in the optimizer + for (const char* section: CacheCoalescedText::SupportedSections) { + CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(section); + cacheStringSection.bufferAddr = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress); + cacheStringSection.bufferVMAddr = addr; + addr += cacheStringSection.bufferSize; + } + + addr = align(addr, 14); + _objcReadOnlyBufferSizeUsed = addr - objcReadOnlyBufferVMAddr; + + uint32_t totalSelectorRefCount = (uint32_t)_selectorStringsFromExecutables; + uint32_t totalClassDefCount = 0; + uint32_t totalProtocolDefCount = 0; + for (DylibInfo& dylib : _sortedDylibs) { + dyld3::MachOAnalyzer::ObjCInfo info = dylib.input->mappedFile.mh->getObjCInfo(); + totalSelectorRefCount += info.selRefCount; + totalClassDefCount += info.classDefCount; + totalProtocolDefCount += info.protocolDefCount; + } + + // now that shared cache coalesces all selector strings, use that better count + uint32_t coalescedSelectorCount = (uint32_t)_coalescedText.objcMethNames.stringsToOffsets.size(); + if ( coalescedSelectorCount > totalSelectorRefCount ) + totalSelectorRefCount = coalescedSelectorCount; + addr += align(computeReadOnlyObjC(totalSelectorRefCount, totalClassDefCount, totalProtocolDefCount), 14); + + size_t impCachesSize = _impCachesBuilder->totalIMPCachesSize(); + size_t alignedImpCachesSize = align(impCachesSize, 14); + _diagnostics.verbose("Reserving %zd bytes for IMP caches (aligned to %zd)\n", impCachesSize, alignedImpCachesSize); + addr += alignedImpCachesSize; + + _objcReadOnlyBufferSizeAllocated = addr - objcReadOnlyBufferVMAddr; + + // align TEXT region end + uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2); + _readExecuteRegion.bufferSize = endTextAddress - _readExecuteRegion.unslidLoadAddress; + _readExecuteRegion.sizeInUse = _readExecuteRegion.bufferSize; + + + // assign __DATA* addresses + if ( _archLayout->sharedRegionsAreDiscontiguous ) + addr = DISCONTIGUOUS_RW; + else + addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2); + + // __DATA* + assignMultipleDataSegmentAddresses(addr, totalProtocolDefCount); + + // start read-only region + if ( _archLayout->sharedRegionsAreDiscontiguous ) + addr = DISCONTIGUOUS_RO; + else + addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2); + _readOnlyRegion.buffer = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart; + _readOnlyRegion.bufferSize = 0; + _readOnlyRegion.sizeInUse = 0; + _readOnlyRegion.unslidLoadAddress = addr; + _readOnlyRegion.cacheFileOffset = lastDataRegion()->cacheFileOffset + lastDataRegion()->sizeInUse; + + + // reserve space for kernel ASLR slide info at start of r/o region + if ( _options.cacheSupportsASLR ) { + 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)); + // We need one slide info header per data region, plus enough space for that regions pages + // Each region will also be padded to a page-size so that the kernel can wire it. + for (Region& region : _dataRegions) { + uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress; + region.slideInfoBuffer = _readOnlyRegion.buffer + offsetInRegion; + region.slideInfoBufferSizeAllocated = align(slideInfoSize + (region.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage + 0x4000, _archLayout->sharedRegionAlignP2); + region.slideInfoFileOffset = _readOnlyRegion.cacheFileOffset + offsetInRegion; + addr += region.slideInfoBufferSizeAllocated; + } + } + + // layout all read-only (but not LINKEDIT) segments + 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(segInfo.segName, "__LINKEDIT") == 0 ) + return; + + // Keep segments segments 4K or more aligned + 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.dstCacheFileSize = (uint32_t)segInfo.sizeOfSections; + 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), 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(segInfo.segName, "__LINKEDIT") != 0 ) + return; + // Keep segments segments 4K or more aligned + 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.dstCacheFileSize = (uint32_t)copySize; + loc.copySegmentSize = (uint32_t)copySize; + loc.srcSegmentIndex = segInfo.segIndex; + dylib.cacheLocation.push_back(loc); + addr += loc.dstCacheSegmentSize; + }); + } + + // align r/o region end + addr = align(addr, _archLayout->sharedRegionAlignP2); + uint64_t endReadOnlyAddress = addr; + _readOnlyRegion.bufferSize = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress + 0x100000; + _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; + }); + } +} + +// Return the total size of the data regions, including padding between them. +// Note this assumes they are contiguous, or that we don't care about including +// additional space between them. +uint64_t SharedCacheBuilder::dataRegionsTotalSize() const { + const Region* firstRegion = nullptr; + const Region* lastRegion = nullptr; + for (const Region& region : _dataRegions) { + if ( (firstRegion == nullptr) || (region.buffer < firstRegion->buffer) ) + firstRegion = ®ion; + if ( (lastRegion == nullptr) || (region.buffer > lastRegion->buffer) ) + lastRegion = ®ion; + } + return (lastRegion->buffer - firstRegion->buffer) + lastRegion->sizeInUse; +} + + +// Return the total size of the data regions, excluding padding between them +uint64_t SharedCacheBuilder::dataRegionsSizeInUse() const { + size_t size = 0; + for (const Region& dataRegion : _dataRegions) + size += dataRegion.sizeInUse; + return size; +} + +// Return the earliest data region by address +const CacheBuilder::Region* SharedCacheBuilder::firstDataRegion() const { + const Region* firstRegion = nullptr; + for (const Region& region : _dataRegions) { + if ( (firstRegion == nullptr) || (region.buffer < firstRegion->buffer) ) + firstRegion = ®ion; + } + return firstRegion; +} + +// Return the lateset data region by address +const CacheBuilder::Region* SharedCacheBuilder::lastDataRegion() const { + const Region* lastRegion = nullptr; + for (const Region& region : _dataRegions) { + if ( (lastRegion == nullptr) || (region.buffer > lastRegion->buffer) ) + lastRegion = ®ion; + } + return lastRegion; +} +static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, dyld3::MachOAnalyzerSet::PointerMetaData pmd, uint64_t addend) { + dyld_cache_patchable_location patch; + patch.cacheOffset = cacheOff; + patch.high7 = pmd.high8 >> 1; + patch.addend = addend; + patch.authenticated = pmd.authenticated; + patch.usesAddressDiversity = pmd.usesAddrDiversity; + patch.key = pmd.key; + patch.discriminator = pmd.diversity; + // check for truncations + assert(patch.cacheOffset == cacheOff); + assert(patch.addend == addend); + assert((patch.high7 << 1) == pmd.high8); + return patch; +} + +void SharedCacheBuilder::buildImageArray(std::vector& aliases) +{ + typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo; + + // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray() + __block std::vector dylibInfos; + __block std::unordered_map imageNumToML; + DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + cache->forEachImage(^(const mach_header* mh, const char* installName) { + const dyld3::MachOLoaded* ml = (dyld3::MachOLoaded*)mh; + if ( !_someDylibsUsedChainedFixups && ml->hasChainedFixups() ) + _someDylibsUsedChainedFixups = true; + 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())] = ml; + }); + + // Convert symlinks from STL to simple char pointers. + std::vector dylibAliases; + dylibAliases.reserve(aliases.size()); + for (const auto& alias : aliases) + dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() }); + + typedef dyld3::MachOAnalyzerSet::FixupTarget FixupTarget; + typedef dyld3::MachOAnalyzerSet::PointerMetaData PointerMetaData; + + dyld3::closure::ClosureBuilder::DylibFixupHandler handler = ^(const dyld3::MachOLoaded* fixupIn, uint64_t fixupLocRuntimeOffset, + PointerMetaData pmd, const FixupTarget& target) { + uint8_t* fixupLoc = (uint8_t*)fixupIn + fixupLocRuntimeOffset; + uint32_t* fixupLoc32 = (uint32_t*)fixupLoc; + uint64_t* fixupLoc64 = (uint64_t*)fixupLoc; + uint64_t targetSymbolOffsetInCache; + switch ( target.kind ) { + case FixupTarget::Kind::rebase: + // rebasing already done in AdjustDylibSegments, but if input dylib uses chained fixups, target might not fit + if ( _archLayout->is64 ) { + if ( pmd.authenticated ) + _aslrTracker.setAuthData(fixupLoc, pmd.diversity, pmd.usesAddrDiversity, pmd.key); + if ( pmd.high8 ) + _aslrTracker.setHigh8(fixupLoc, pmd.high8); + uint64_t targetVmAddr; + if ( _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVmAddr) ) + *fixupLoc64 = targetVmAddr; + else + *fixupLoc64 = (uint8_t*)target.foundInImage._mh - _readExecuteRegion.buffer + target.offsetInImage + _readExecuteRegion.unslidLoadAddress; + } + else { + uint32_t targetVmAddr; + assert(_aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) && "32-bit archs always store target in side table"); + *fixupLoc32 = targetVmAddr; + } + break; + case FixupTarget::Kind::bindAbsolute: + if ( _archLayout->is64 ) + *fixupLoc64 = target.offsetInImage; + else + *fixupLoc32 = (uint32_t)(target.offsetInImage); + // don't record absolute targets for ASLR + _aslrTracker.remove(fixupLoc); + break; + case FixupTarget::Kind::bindToImage: + targetSymbolOffsetInCache = (uint8_t*)target.foundInImage._mh - _readExecuteRegion.buffer + target.offsetInImage - target.addend; + if ( !target.weakCoalesced || !_aslrTracker.has(fixupLoc) ) { + // this handler is called a second time for weak_bind info, which we ignore when building cache + _aslrTracker.add(fixupLoc); + if ( _archLayout->is64 ) { + if ( pmd.high8 ) + _aslrTracker.setHigh8(fixupLoc, pmd.high8); + if ( pmd.authenticated ) + _aslrTracker.setAuthData(fixupLoc, pmd.diversity, pmd.usesAddrDiversity, pmd.key); + *fixupLoc64 = _archLayout->sharedMemoryStart + targetSymbolOffsetInCache + target.addend; + } + else { + assert(targetSymbolOffsetInCache < (_readOnlyRegion.buffer - _readExecuteRegion.buffer) && "offset not into TEXT or DATA of cache file"); + uint32_t targetVmAddr; + if ( _aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) ) + *fixupLoc32 = targetVmAddr; + else + *fixupLoc32 = (uint32_t)(_archLayout->sharedMemoryStart + targetSymbolOffsetInCache + target.addend); + } + } + _dylibToItsExports[target.foundInImage._mh].insert(targetSymbolOffsetInCache); + if ( target.isWeakDef ) + _dylibWeakExports.insert({ target.foundInImage._mh, targetSymbolOffsetInCache }); + _exportsToUses[targetSymbolOffsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, pmd, target.addend)); + _exportsToName[targetSymbolOffsetInCache] = target.foundSymbolName; + break; + case FixupTarget::Kind::bindMissingSymbol: + // if there are missing symbols, makeDyldCacheImageArray() will error + break; + } + }; + + + // build ImageArray for all dylibs in dyld cache + dyld3::closure::PathOverrides pathOverrides; + dyld3::RootsChecker rootsChecker; + dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, rootsChecker, cache, false, *_options.archs, pathOverrides, + dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, handler); + dyld3::Array dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size()); + const dyld3::Array aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size()); + _imageArray = cb.makeDyldCacheImageArray(dylibs, aliasesArray); + if ( cb.diagnostics().hasError() ) { + _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str()); + return; + } +} + +static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) { + return a.cacheOffset == b.cacheOffset; +} + +void SharedCacheBuilder::addImageArray() +{ + // build trie of dylib paths + __block std::vector 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 trieBytes; + dylibsTrie.emit(trieBytes); + while ( (trieBytes.size() % 4) != 0 ) + trieBytes.push_back(0); + + // build set of functions to never stub-eliminate because tools may need to override them + std::unordered_set alwaysGeneratePatch; + for (const char* const* p=_s_neverStubEliminateSymbols; *p != nullptr; ++p) + alwaysGeneratePatch.insert(*p); + + // Add the patches for the image array. + __block uint64_t numPatchImages = _imageArray->size(); + __block uint64_t numPatchExports = 0; + __block uint64_t numPatchLocations = 0; + __block uint64_t numPatchExportNameBytes = 0; + + auto needsPatch = [&](bool dylibNeedsPatching, const dyld3::MachOLoaded* mh, + CacheOffset offset) -> bool { + if (dylibNeedsPatching) + return true; + if (_dylibWeakExports.find({ mh, offset }) != _dylibWeakExports.end()) + return true; + const std::string& exportName = _exportsToName[offset]; + return alwaysGeneratePatch.find(exportName) != alwaysGeneratePatch.end(); + }; + + // First calculate how much space we need + const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + cache->forEachImage(^(const mach_header* mh, const char* installName) { + const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh; + const std::set& dylibExports = _dylibToItsExports[ml]; + + // On a customer cache, only store patch locations for interposable dylibs and weak binding + bool dylibNeedsPatching = cache->isOverridablePath(installName); + + uint64_t numDylibExports = 0; + for (CacheOffset exportCacheOffset : dylibExports) { + if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) + continue; + std::vector& uses = _exportsToUses[exportCacheOffset]; + uses.erase(std::unique(uses.begin(), uses.end()), uses.end()); + numPatchLocations += uses.size(); + + std::string exportName = _exportsToName[exportCacheOffset]; + numPatchExportNameBytes += exportName.size() + 1; + } + numPatchExports += numDylibExports; + }); + + // Now reserve the space + __block std::vector patchImages; + __block std::vector patchExports; + __block std::vector patchLocations; + __block std::vector patchExportNames; + + patchImages.reserve(numPatchImages); + patchExports.reserve(numPatchExports); + patchLocations.reserve(numPatchLocations); + patchExportNames.reserve(numPatchExportNameBytes); + + // And now fill it with the patch data + cache->forEachImage(^(const mach_header* mh, const char* installName) { + const dyld3::MachOLoaded* ml = (const dyld3::MachOLoaded*)mh; + const std::set& dylibExports = _dylibToItsExports[ml]; + + // On a customer cache, only store patch locations for interposable dylibs and weak binding + bool dylibNeedsPatching = cache->isOverridablePath(installName); + + // Add the patch image which points in to the exports + dyld_cache_image_patches patchImage; + patchImage.patchExportsStartIndex = (uint32_t)patchExports.size(); + patchImage.patchExportsCount = 0; + + // Then add each export which points to a list of locations and a name + for (CacheOffset exportCacheOffset : dylibExports) { + if (!needsPatch(dylibNeedsPatching, ml, exportCacheOffset)) + continue; + ++patchImage.patchExportsCount; + std::vector& uses = _exportsToUses[exportCacheOffset]; + + dyld_cache_patchable_export cacheExport; + cacheExport.cacheOffsetOfImpl = (uint32_t)exportCacheOffset; + cacheExport.patchLocationsStartIndex = (uint32_t)patchLocations.size(); + cacheExport.patchLocationsCount = (uint32_t)uses.size(); + cacheExport.exportNameOffset = (uint32_t)patchExportNames.size(); + patchExports.push_back(cacheExport); + + // Now add the list of locations. + patchLocations.insert(patchLocations.end(), uses.begin(), uses.end()); + + // And add the export name + const std::string& exportName = _exportsToName[exportCacheOffset]; + patchExportNames.insert(patchExportNames.end(), &exportName[0], &exportName[0] + exportName.size() + 1); + } + patchImages.push_back(patchImage); + }); + + while ( (patchExportNames.size() % 4) != 0 ) + patchExportNames.push_back('\0'); + + uint64_t patchInfoSize = sizeof(dyld_cache_patch_info); + patchInfoSize += sizeof(dyld_cache_image_patches) * patchImages.size(); + patchInfoSize += sizeof(dyld_cache_patchable_export) * patchExports.size(); + patchInfoSize += sizeof(dyld_cache_patchable_location) * patchLocations.size(); + patchInfoSize += patchExportNames.size(); + + // check for fit + uint64_t imageArraySize = _imageArray->size(); + size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse; + if ( (imageArraySize+trieBytes.size()+patchInfoSize) > freeSpace ) { + _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)", + _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, patchInfoSize/1024, freeSpace/1024/1024); + return; + } + + // 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()); + + // Also write out the patch info + dyldCache->header.patchInfoAddr = dyldCache->header.dylibsTrieAddr + dyldCache->header.dylibsTrieSize; + dyldCache->header.patchInfoSize = patchInfoSize; + dyld_cache_patch_info patchInfo; + patchInfo.patchTableArrayAddr = dyldCache->header.patchInfoAddr + sizeof(dyld_cache_patch_info); + patchInfo.patchTableArrayCount = patchImages.size(); + patchInfo.patchExportArrayAddr = patchInfo.patchTableArrayAddr + (patchInfo.patchTableArrayCount * sizeof(dyld_cache_image_patches)); + patchInfo.patchExportArrayCount = patchExports.size(); + patchInfo.patchLocationArrayAddr = patchInfo.patchExportArrayAddr + (patchInfo.patchExportArrayCount * sizeof(dyld_cache_patchable_export)); + patchInfo.patchLocationArrayCount = patchLocations.size(); + patchInfo.patchExportNamesAddr = patchInfo.patchLocationArrayAddr + (patchInfo.patchLocationArrayCount * sizeof(dyld_cache_patchable_location)); + patchInfo.patchExportNamesSize = patchExportNames.size(); + ::memcpy(_readOnlyRegion.buffer + dyldCache->header.patchInfoAddr - _readOnlyRegion.unslidLoadAddress, + &patchInfo, sizeof(dyld_cache_patch_info)); + ::memcpy(_readOnlyRegion.buffer + patchInfo.patchTableArrayAddr - _readOnlyRegion.unslidLoadAddress, + &patchImages[0], sizeof(patchImages[0]) * patchImages.size()); + ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportArrayAddr - _readOnlyRegion.unslidLoadAddress, + &patchExports[0], sizeof(patchExports[0]) * patchExports.size()); + ::memcpy(_readOnlyRegion.buffer + patchInfo.patchLocationArrayAddr - _readOnlyRegion.unslidLoadAddress, + &patchLocations[0], sizeof(patchLocations[0]) * patchLocations.size()); + ::memcpy(_readOnlyRegion.buffer + patchInfo.patchExportNamesAddr - _readOnlyRegion.unslidLoadAddress, + &patchExportNames[0], patchExportNames.size()); + + _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size()+patchInfoSize,14); + + // Free the underlying image array buffer + _imageArray->deallocate(); + _imageArray = nullptr; +} + +void SharedCacheBuilder::addOtherImageArray(const std::vector& otherDylibsAndBundles, std::vector& overflowDylibs) +{ + DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + dyld3::closure::PathOverrides pathOverrides; + dyld3::closure::FileSystemNull nullFileSystem; + dyld3::RootsChecker rootsChecker; + dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, rootsChecker, cache, false, *_options.archs, pathOverrides, + dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _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, "staged_system_apps/") ) + 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); + } + + // 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) { + // Sort mac before iOSMac + bool isIOSMacA = strncmp(a.path, "/System/iOSSupport/", 19) == 0; + bool isIOSMacB = strncmp(b.path, "/System/iOSSupport/", 19) == 0; + if (isIOSMacA != isIOSMacB) + return !isIOSMacA; + return strcmp(a.path, b.path) < 0; + }); + + const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size()); + + // build trie of paths + __block std::vector 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 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; + } + + // 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); + + // Free the underlying buffer + otherImageArray->deallocate(); +} + + +void SharedCacheBuilder::addClosures(const std::vector& osExecutables) +{ + const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer; + + __block std::vector osExecutablesDiags; + __block std::vector 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; + } + + // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same + // at runtime as when the shared cache processed it. We must have a code signature to record this information + uint32_t codeSigFileOffset; + uint32_t codeSigSize; + if ( !loadedMachO.mappedFile.mh->hasCodeSignature(codeSigFileOffset, codeSigSize) ) { + return; + } + + dyld3::closure::PathOverrides pathOverrides; + dyld3::RootsChecker rootsChecker; + dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, rootsChecker, dyldCache, false, *_options.archs, pathOverrides, + dyld3::closure::ClosureBuilder::AtPath::all, false, nullptr, _options.platform, nullptr); + bool issetuid = false; + if ( this->_options.platform == dyld3::Platform::macOS || dyld3::MachOFile::isSimulatorPlatform(this->_options.platform) ) + _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid, nullptr); + 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 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) { + closuresSpace += entry.second->size(); + } + 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; + } + DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse; + uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse; + __block std::vector closureEntrys; + uint32_t currentClosureOffset = 0; + for (const auto& entry : closures) { + const dyld3::closure::LaunchClosure* closure = entry.second; + closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset))); + + // Add cdHashes to the trie so that we can look up by cdHash at runtime + closure->topImage()->forEachCDHash(^(const uint8_t *cdHash, bool &stop) { + std::string cdHashStr = "/cdhash/"; + 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); + } + closureEntrys.push_back(DylibIndexTrie::Entry(cdHashStr, DylibIndex(currentClosureOffset))); + }); + + size_t size = closure->size(); + assert((size % 4) == 0); + memcpy(closuresBase+currentClosureOffset, closure, size); + currentClosureOffset += size; + freeSpace -= size; + closure->deallocate(); + } + cache->header.progClosuresSize = currentClosureOffset; + _readOnlyRegion.sizeInUse += currentClosureOffset; + freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse; + // build trie of indexes into closures list + DylibIndexTrie closureTrie(closureEntrys); + std::vector trieBytes; + closureTrie.emit(trieBytes); + while ( (trieBytes.size() % 8) != 0 ) + trieBytes.push_back(0); + if ( trieBytes.size() > freeSpace ) { + _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)", + _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024); + return; + } + memcpy(_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); +} + +void SharedCacheBuilder::emitContantObjects() { + if ( _coalescedText.cfStrings.bufferSize == 0 ) + return; + + assert(_coalescedText.cfStrings.isaInstallName != nullptr); + DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + __block uint64_t targetSymbolOffsetInCache = 0; + __block const dyld3::MachOAnalyzer* targetSymbolMA = nullptr; + __block const dyld3::MachOAnalyzer* libdyldMA = nullptr; + cache->forEachImage(^(const mach_header* mh, const char* installName) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + + if ( strcmp(installName, "/usr/lib/system/libdyld.dylib") == 0 ) { + libdyldMA = ma; + } + + if ( targetSymbolOffsetInCache != 0 ) + return; + if ( strcmp(installName, _coalescedText.cfStrings.isaInstallName) != 0 ) + return; + dyld3::MachOAnalyzer::FoundSymbol foundInfo; + bool foundSymbol = ma->findExportedSymbol(_diagnostics, _coalescedText.cfStrings.isaClassName, + false, foundInfo, nullptr); + if ( foundSymbol ) { + targetSymbolOffsetInCache = (uint8_t*)ma - _readExecuteRegion.buffer + foundInfo.value; + targetSymbolMA = ma; + } + }); + if ( targetSymbolOffsetInCache == 0 ) { + _diagnostics.error("Could not find export of '%s' in '%s'", _coalescedText.cfStrings.isaClassName, + _coalescedText.cfStrings.isaInstallName); + return; + } + if ( libdyldMA == nullptr ) { + _diagnostics.error("Could not libdyld.dylib in shared cache"); + return; + } + + // If all binds to this symbol were via CF constants, then we'll never have seen the ISA patch export + // os add it now just in case + _dylibToItsExports[targetSymbolMA].insert(targetSymbolOffsetInCache); + _exportsToName[targetSymbolOffsetInCache] = _coalescedText.cfStrings.isaClassName; + + // CFString's have so far just been memcpy'ed from the source dylib to the shared cache. + // We now need to rewrite their ISAs to be rebases to the ___CFConstantStringClassReference class + const uint64_t cfStringAtomSize = (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize; + assert( (_coalescedText.cfStrings.bufferSize % cfStringAtomSize) == 0); + for (uint64_t bufferOffset = 0; bufferOffset != _coalescedText.cfStrings.bufferSize; bufferOffset += cfStringAtomSize) { + uint8_t* atomBuffer = _coalescedText.cfStrings.bufferAddr + bufferOffset; + // The ISA fixup is at an offset of 0 in to the atom + uint8_t* fixupLoc = atomBuffer; + // We purposefully want to remove the pointer authentication from the ISA so + // just use an empty pointer metadata + dyld3::Loader::PointerMetaData pmd; + uint64_t addend = 0; + _exportsToUses[targetSymbolOffsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, pmd, addend)); + *(uint64_t*)fixupLoc = _archLayout->sharedMemoryStart + targetSymbolOffsetInCache; + _aslrTracker.add(fixupLoc); + } + + // Set the ranges in the libdyld in the shared cache. At runtime we can use these to quickly check if a given address + // is a valid constant + typedef std::pair ObjCConstantRange; + std::pair sharedCacheRanges = cache->getObjCConstantRange(); + uint64_t numRanges = sharedCacheRanges.second / sizeof(ObjCConstantRange); + dyld3::Array rangeArray((ObjCConstantRange*)sharedCacheRanges.first, numRanges, numRanges); + + if ( numRanges > dyld_objc_string_kind ) { + rangeArray[dyld_objc_string_kind].first = (const uint8_t*)_coalescedText.cfStrings.bufferVMAddr; + rangeArray[dyld_objc_string_kind].second = rangeArray[dyld_objc_string_kind].first + _coalescedText.cfStrings.bufferSize; + _aslrTracker.add(&rangeArray[dyld_objc_string_kind].first); + _aslrTracker.add(&rangeArray[dyld_objc_string_kind].second); + } + + // Update the __SHARED_CACHE range in libdyld to contain the cf/objc constants + libdyldMA->forEachLoadCommand(_diagnostics, ^(const load_command* cmd, bool& stop) { + // We don't handle 32-bit as this is only needed for pointer authentication + assert(cmd->cmd != LC_SEGMENT); + if ( cmd->cmd == LC_SEGMENT_64 ) { + segment_command_64* seg = (segment_command_64*)cmd; + if ( strcmp(seg->segname, "__SHARED_CACHE") == 0 ) { + // Update the range of this segment, and any sections inside + seg->vmaddr = _coalescedText.cfStrings.bufferVMAddr; + seg->vmsize = _coalescedText.cfStrings.bufferSize; + seg->fileoff = _coalescedText.cfStrings.cacheFileOffset; + seg->fileoff = _coalescedText.cfStrings.bufferSize; + section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64)); + section_64* const sectionsEnd = §ionsStart[seg->nsects]; + for (section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( !strcmp(sect->sectname, "__cfstring") ) { + sect->addr = _coalescedText.cfStrings.bufferVMAddr; + sect->size = _coalescedText.cfStrings.bufferSize; + sect->offset = (uint32_t)_coalescedText.cfStrings.cacheFileOffset; + } + } + stop = true; + } + } + }); +} + + +bool SharedCacheBuilder::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); + const uint32_t mappingsCount = cacheHeader->mappingCount; + // Check the sizes of all the regions are correct + assert(_readExecuteRegion.sizeInUse == mappings[0].size); + for (uint32_t i = 0; i != _dataRegions.size(); ++i) { + assert(_dataRegions[i].sizeInUse == mappings[i + 1].size); + } + assert(_readOnlyRegion.sizeInUse == mappings[mappingsCount - 1].size); + + // Check the file offsets of all the regions are correct + assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset); + for (uint32_t i = 0; i != _dataRegions.size(); ++i) { + assert(_dataRegions[i].cacheFileOffset == mappings[i + 1].fileOffset); + } + assert(_readOnlyRegion.cacheFileOffset == mappings[mappingsCount - 1].fileOffset); + assert(_codeSignatureRegion.sizeInUse == cacheHeader->codeSignatureSize); + assert(cacheHeader->codeSignatureOffset == _readOnlyRegion.cacheFileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse); + + // Make sure the slidable mappings have the same ranges as the original mappings + const dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset); + assert(cacheHeader->mappingCount == cacheHeader->mappingWithSlideCount); + for (uint32_t i = 0; i != cacheHeader->mappingCount; ++i) { + assert(mappings[i].address == slidableMappings[i].address); + assert(mappings[i].size == slidableMappings[i].size); + assert(mappings[i].fileOffset == slidableMappings[i].fileOffset); + assert(mappings[i].maxProt == slidableMappings[i].maxProt); + assert(mappings[i].initProt == slidableMappings[i].initProt); + } + + // Now that we know everything is correct, actually copy the data + cacheSizeCallback(_readExecuteRegion.sizeInUse+dataRegionsSizeInUse()+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse); + bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset); + for (uint32_t i = 0; i != _dataRegions.size(); ++i) { + fullyWritten &= copyCallback(_dataRegions[i].buffer, _dataRegions[i].sizeInUse, mappings[i + 1].fileOffset); + } + fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[cacheHeader->mappingCount - 1].fileOffset); + if ( _localSymbolsRegion.sizeInUse != 0 ) { + assert(cacheHeader->localSymbolsOffset == mappings[cacheHeader->mappingCount - 1].fileOffset+_readOnlyRegion.sizeInUse); + fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset); + } + fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset); + return fullyWritten; +} + + +void SharedCacheBuilder::writeFile(const std::string& path) +{ + std::string pathTemplate = path + "-XXXXXX"; + size_t templateLen = strlen(pathTemplate.c_str())+2; + BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen); + strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen); + int fd = mkstemp(pathTemplateSpace); + if ( fd != -1 ) { + auto cacheSizeCallback = ^(uint64_t size) { + // set final cache file size (may help defragment file) + ::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; + }; + // TOCTOU: verify path is still a realpath (not changed) + char tempPath[MAXPATHLEN]; + if ( ::fcntl(fd, F_GETPATH, tempPath) == 0 ) { + size_t tempPathLen = strlen(tempPath); + if ( tempPathLen > 7 ) + tempPath[tempPathLen-7] = '\0'; // remove trailing -xxxxxx + if ( path != tempPath ) { + _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), tempPath); + ::close(fd); + return; + } + } + else { + _diagnostics.error("unable to fcntl(fd, F_GETPATH) on output file"); + ::close(fd); + return; + } + bool fullyWritten = writeCache(cacheSizeCallback, copyCallback); + if ( fullyWritten ) { + ::fchmod(fd, S_IRUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "r--r--r--" + // TOCTOU: verify path is still a realpath (not changed) + // For MRM bringup, dyld installs symlinks from: + // dyld_shared_cache_x86_64 -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64 + // dyld_shared_cache_x86_64h -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64h + // We don't want to follow that symlink when we install the cache, but instead write over it + auto lastSlash = path.find_last_of("/"); + if ( lastSlash != std::string::npos ) { + std::string directoryPath = path.substr(0, lastSlash); + + char resolvedPath[PATH_MAX]; + ::realpath(directoryPath.c_str(), resolvedPath); + // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer + if ( directoryPath != resolvedPath ) { + _diagnostics.error("output directory file path changed from: '%s' to: '%s'", directoryPath.c_str(), resolvedPath); + return; + } + } + if ( ::rename(pathTemplateSpace, path.c_str()) == 0) { + ::close(fd); + return; // success + } else { + _diagnostics.error("could not rename file '%s' to: '%s'", pathTemplateSpace, path.c_str()); + } + } + else { + _diagnostics.error("could not write file %s", pathTemplateSpace); + } + ::close(fd); + ::unlink(pathTemplateSpace); + } + else { + _diagnostics.error("could not open file %s", pathTemplateSpace); + } +} + +void SharedCacheBuilder::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 SharedCacheBuilder::writeMapFile(const std::string& path) +{ + std::string mapContent = getMapFileBuffer(); + safeSave(mapContent.c_str(), mapContent.size(), path); +} + +std::string SharedCacheBuilder::getMapFileBuffer() const +{ + const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + return cache->mapFile(); +} + +std::string SharedCacheBuilder::getMapFileJSONBuffer(const std::string& cacheDisposition) const +{ + const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + return cache->generateJSONMap(cacheDisposition.c_str()); +} + +void SharedCacheBuilder::markPaddingInaccessible() +{ + // region between RX and RW + uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse; + uint8_t* endPad1 = firstDataRegion()->buffer; + ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0); + + // region between RW and RO + const Region* lastRegion = lastDataRegion(); + uint8_t* startPad2 = lastRegion->buffer+lastRegion->sizeInUse; + uint8_t* endPad2 = _readOnlyRegion.buffer; + ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0); +} + + +void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) { + for (const DylibInfo& dylibInfo : _sortedDylibs) + callback(dylibInfo.dylibID); +} + + +void SharedCacheBuilder::forEachCacheSymlink(void (^callback)(const std::string& path)) +{ + const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer; + const dyld3::closure::ImageArray* images = cache->cachedDylibsImageArray(); + if ( images == nullptr ) + return; + + // Aliases we folded in to the cache are in the cache dylib closures + images->forEachImage(^(const dyld3::closure::Image *image, bool &stop) { + image->forEachAlias(^(const char *aliasPath, bool &stop) { + callback(aliasPath); + }); + }); +} + + +uint64_t SharedCacheBuilder::pathHash(const char* path) +{ + uint64_t sum = 0; + for (const char* s=path; *s != '\0'; ++s) + sum += sum*4 + *s; + return sum; +} + + +void SharedCacheBuilder::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; + } + }); + }); +} + + +void SharedCacheBuilder::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; + } + + // 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 SharedCacheBuilder::codeSign() +{ + uint8_t dscHashType; + uint8_t dscHashSize; + uint32_t dscDigestFormat; + bool agile = false; + + // select which codesigning hash + switch (_options.codeSigningDigestMode) { + case DyldSharedCache::Agile: + agile = true; + // Fall through to SHA1, because the main code directory remains SHA1 for compatibility. + [[clang::fallthrough]]; + case DyldSharedCache::SHA1only: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + dscHashType = CS_HASHTYPE_SHA1; + dscHashSize = CS_HASH_SIZE_SHA1; + dscDigestFormat = kCCDigestSHA1; +#pragma clang diagnostic pop + break; + case DyldSharedCache::SHA256only: + dscHashType = CS_HASHTYPE_SHA256; + dscHashSize = CS_HASH_SIZE_SHA256; + dscDigestFormat = kCCDigestSHA256; + break; + default: + _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.", + _options.codeSigningDigestMode); + return; + } + + std::string cacheIdentifier = "com.apple.dyld.cache."; + cacheIdentifier += _options.archs->name(); + if ( _options.dylibsRemovedDuringMastering ) { + if ( _options.optimizeStubs ) + cacheIdentifier += ".release"; + else + cacheIdentifier += ".development"; + } + // get pointers into shared cache buffer + size_t inBbufferSize = _readExecuteRegion.sizeInUse+dataRegionsSizeInUse()+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse; + + const uint16_t pageSize = _archLayout->csPageSize; + + // layout code signature contents + uint32_t blobCount = agile ? 4 : 3; + size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0 + uint32_t slotCount = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize); + uint32_t xSlotCount = CSSLOT_REQUIREMENTS; + size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg); + size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount; + size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount; + size_t cdSize = hashOffset + (slotCount * dscHashSize); + size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0; + size_t reqsSize = 12; + size_t cmsSize = sizeof(CS_Blob); + size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex); + size_t cd256Offset = cdOffset + cdSize; + size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile + size_t cmsOffset = reqsOffset + reqsSize; + size_t sbSize = cmsOffset + cmsSize; + size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned + + // 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(_codeSignatureRegion.buffer); + sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE); + sb->length = htonl(sbSize); + sb->count = htonl(blobCount); + sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY); + sb->index[0].offset = htonl(cdOffset); + sb->index[1].type = htonl(CSSLOT_REQUIREMENTS); + sb->index[1].offset = htonl(reqsOffset); + sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE); + sb->index[2].offset = htonl(cmsOffset); + if ( agile ) { + sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0); + sb->index[3].offset = htonl(cd256Offset); + } + + // fill in empty requirements + CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset); + reqs->magic = htonl(CSMAGIC_REQUIREMENTS); + reqs->length = htonl(sizeof(CS_RequirementsBlob)); + reqs->data = 0; + + // initialize fixed fields of Code Directory + CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset); + cd->magic = htonl(CSMAGIC_CODEDIRECTORY); + cd->length = htonl(cdSize); + cd->version = htonl(0x20400); // supports exec segment + cd->flags = htonl(kSecCodeSignatureAdhoc); + cd->hashOffset = htonl(hashOffset); + cd->identOffset = htonl(idOffset); + cd->nSpecialSlots = htonl(xSlotCount); + cd->nCodeSlots = htonl(slotCount); + cd->codeLimit = htonl(inBbufferSize); + cd->hashSize = dscHashSize; + cd->hashType = dscHashType; + cd->platform = 0; // not platform binary + cd->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE); + cd->spare2 = 0; // unused (must be zero) + cd->scatterOffset = 0; // not supported anymore + cd->teamOffset = 0; // no team ID + cd->spare3 = 0; // unused (must be zero) + cd->codeLimit64 = 0; // falls back to codeLimit + + // executable segment info + 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()); + + // add special slot hashes + uint8_t* hashSlot = (uint8_t*)cd + hashOffset; + uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize]; + CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot); + + CS_CodeDirectory* cd256; + uint8_t* hash256Slot; + uint8_t* reqsHash256Slot; + if ( agile ) { + // Note that the assumption here is that the size up to the hashes is the same as for + // sha1 code directory, and that they come last, after everything else. + + cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset); + cd256->magic = htonl(CSMAGIC_CODEDIRECTORY); + cd256->length = htonl(cd256Size); + cd256->version = htonl(0x20400); // supports exec segment + cd256->flags = htonl(kSecCodeSignatureAdhoc); + cd256->hashOffset = htonl(hash256Offset); + cd256->identOffset = htonl(idOffset); + cd256->nSpecialSlots = htonl(xSlotCount); + cd256->nCodeSlots = htonl(slotCount); + cd256->codeLimit = htonl(inBbufferSize); + cd256->hashSize = CS_HASH_SIZE_SHA256; + cd256->hashType = CS_HASHTYPE_SHA256; + cd256->platform = 0; // not platform binary + cd256->pageSize = __builtin_ctz(pageSize); // log2(CS_PAGE_SIZE); + cd256->spare2 = 0; // unused (must be zero) + cd256->scatterOffset = 0; // not supported anymore + cd256->teamOffset = 0; // no team ID + cd256->spare3 = 0; // unused (must be zero) + cd256->codeLimit64 = 0; // falls back to codeLimit + + // executable segment info + cd256->execSegBase = cd->execSegBase; + cd256->execSegLimit = cd->execSegLimit; + cd256->execSegFlags = cd->execSegFlags; + + // initialize dynamic fields of Code Directory + strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str()); + + // add special slot hashes + hash256Slot = (uint8_t*)cd256 + hash256Offset; + reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256]; + CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot); + } + else { + cd256 = NULL; + hash256Slot = NULL; + reqsHash256Slot = NULL; + } + + // fill in empty CMS blob for ad-hoc signing + CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset); + cms->magic = htonl(CSMAGIC_BLOBWRAPPER); + cms->length = htonl(sizeof(CS_Blob)); + + + // alter header of cache to record size and location of code signature + // do this *before* hashing each page + dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer; + cache->codeSignatureOffset = inBbufferSize; + cache->codeSignatureSize = sigSize; + + struct SlotRange { + uint64_t start = 0; + uint64_t end = 0; + const uint8_t* buffer = nullptr; + }; + std::vector regionSlots; + // __TEXT + regionSlots.push_back({ 0, (_readExecuteRegion.sizeInUse / pageSize), _readExecuteRegion.buffer }); + // __DATA + for (const Region& dataRegion : _dataRegions) { + // The first data region starts at the end of __TEXT, and subsequent regions are + // after the previous __DATA region. + uint64_t previousEnd = regionSlots.back().end; + uint64_t numSlots = dataRegion.sizeInUse / pageSize; + regionSlots.push_back({ previousEnd, previousEnd + numSlots, dataRegion.buffer }); + } + // __LINKEDIT + { + uint64_t previousEnd = regionSlots.back().end; + uint64_t numSlots = _readOnlyRegion.sizeInUse / pageSize; + regionSlots.push_back({ previousEnd, previousEnd + numSlots, _readOnlyRegion.buffer }); + } + // local symbols + if ( _localSymbolsRegion.sizeInUse != 0 ) { + uint64_t previousEnd = regionSlots.back().end; + uint64_t numSlots = _localSymbolsRegion.sizeInUse / pageSize; + regionSlots.push_back({ previousEnd, previousEnd + numSlots, _localSymbolsRegion.buffer }); + } + + auto codeSignPage = ^(size_t i) { + // move to correct region + for (const SlotRange& slotRange : regionSlots) { + if ( (i >= slotRange.start) && (i < slotRange.end) ) { + const uint8_t* code = slotRange.buffer + ((i - slotRange.start) * pageSize); + + CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize)); + + if ( agile ) { + CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256)); + } + return; + } + } + assert(0 && "Out of range slot"); + }; + + // compute hashes + dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) { + codeSignPage(i); + }); + + // Now that we have a code signature, compute a cache UUID by hashing the code signature blob + { + uint8_t* uuidLoc = cache->uuid; + assert(uuid_is_null(uuidLoc)); + static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE_4K == 0, "uuid is expected in the first page of the cache"); + uint8_t fullDigest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256((const void*)cd, (unsigned)cdSize, fullDigest); + memcpy(uuidLoc, fullDigest, 16); + // 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, because we modified it by setting uuid in header + codeSignPage(0); + } + + // hash of entire code directory (cdHash) uses same hash as each page + uint8_t fullCdHash[dscHashSize]; + CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash); + // Note: cdHash is defined as first 20 bytes of hash + memcpy(_cdHashFirst, fullCdHash, 20); + if ( agile ) { + uint8_t fullCdHash256[CS_HASH_SIZE_SHA256]; + CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256); + // Note: cdHash is defined as first 20 bytes of hash, even for sha256 + memcpy(_cdHashSecond, fullCdHash256, 20); + } + else { + memset(_cdHashSecond, 0, 20); + } +} + +const bool SharedCacheBuilder::agileSignature() +{ + return _options.codeSigningDigestMode == DyldSharedCache::Agile; +} + +static const std::string cdHash(uint8_t hash[20]) +{ + char buff[48]; + for (int i = 0; i < 20; ++i) + sprintf(&buff[2*i], "%2.2x", hash[i]); + return buff; +} + +const std::string SharedCacheBuilder::cdHashFirst() +{ + return cdHash(_cdHashFirst); +} + +const std::string SharedCacheBuilder::cdHashSecond() +{ + return cdHash(_cdHashSecond); +} + +const std::string SharedCacheBuilder::uuid() const +{ + dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer; + uuid_string_t uuidStr; + uuid_unparse(cache->uuid, uuidStr); + return uuidStr; +} + +void SharedCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) { + for (const DylibInfo& dylibInfo : _sortedDylibs) { + // The shared cache builder doesn't use per-dylib errors right now + // so just share the global diagnostics + callback(dylibInfo, _diagnostics); + } +} + + + +template +bool SharedCacheBuilder::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 (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n", + (long)lastValue, 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); + uint8_t highByte; + if ( _aslrTracker.hasHigh8(lastLoc, &highByte) ) { + uint64_t tbi = (uint64_t)highByte << 56; + newLastValue |= tbi; + } + 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]; + pint_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; + } + pint_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 +void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& 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

(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 +void SharedCacheBuilder::writeSlideInfoV2(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions) +{ + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + const uint32_t pageSize = _aslrTracker.pageSize(); + const uint8_t* firstDataRegionBuffer = firstDataRegion()->buffer; + for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) { + Region& dataRegion = _dataRegions[dataRegionIndex]; + + // fill in fixed info + assert(dataRegion.slideInfoFileOffset != 0); + assert((dataRegion.sizeInUse % pageSize) == 0); + unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize; + dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)dataRegion.slideInfoBuffer; + info->version = 2; + info->page_size = pageSize; + info->delta_mask = _archLayout->pointerDeltaMask; + info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0; + + // set page starts and extras for each page + std::vector pageStarts; + std::vector pageExtras; + pageStarts.reserve(dataPageCount); + + const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4)); + uint8_t* pageContent = dataRegion.buffer; + unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize; + assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions); + const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion); + const bool* bitmapForPage = bitmapForRegion; + for (unsigned i=0; i < dataPageCount; ++i) { + //warning("page[%d]", i); + addPageStartsV2

(pageContent, bitmapForPage, info, pageStarts, pageExtras); + if ( _diagnostics.hasError() ) { + return; + } + pageContent += pageSize; + bitmapForPage += (sizeof(bool)*(pageSize/4)); + } + + // 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); + dataRegion.slideInfoFileSize = slideInfoSize; + if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) { + _diagnostics.error("kernel slide info overflow buffer"); + } + // Update the mapping entry on the cache header + const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; + dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset); + slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize; + //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size()); + } +} + +#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k +// fits in to int16_t +static bool smallValue(uint64_t value) +{ + uint32_t high = (value & 0xFFFF8000); + return (high == 0) || (high == 0xFFFF8000); +} + +template +bool SharedCacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* 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 ( 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); + //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; + } + 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 ( 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); + 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 ( 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); + P::setP(*prevLoc, newValue); + + return true; +} + + +template +void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info, + std::vector& pageStarts, std::vector& 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_SLIDE4_PAGE_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_SLIDE4_PAGE_NO_REBASE ) { + // found first rebase location in page + startValue = i; + } + else if ( !makeRebaseChainV4

(pageContent, lastLocationOffset, offset, info) ) { + // can't record all rebasings in one chain + 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 >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) { + _diagnostics.error("rebase overflow in v4 page extras"); + return; + } + pageExtras.push_back(startValue); + startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_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_SLIDE4_PAGE_USE_EXTRA ) { + // add end bit to extras + pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END; + } + } + pageStarts.push_back(startValue); +} + + + +template +void SharedCacheBuilder::writeSlideInfoV4(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions) +{ + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + const uint32_t pageSize = _aslrTracker.pageSize(); + const uint8_t* firstDataRegionBuffer = firstDataRegion()->buffer; + for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) { + Region& dataRegion = _dataRegions[dataRegionIndex]; + + // fill in fixed info + assert(dataRegion.slideInfoFileOffset != 0); + assert((dataRegion.sizeInUse % pageSize) == 0); + unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize; + dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)dataRegion.slideInfoBuffer; + info->version = 4; + info->page_size = pageSize; + info->delta_mask = _archLayout->pointerDeltaMask; + info->value_add = info->value_add = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0; + + // set page starts and extras for each page + std::vector pageStarts; + std::vector pageExtras; + pageStarts.reserve(dataPageCount); + const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4)); + uint8_t* pageContent = dataRegion.buffer; + unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize; + assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions); + const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion); + const bool* bitmapForPage = bitmapForRegion; + for (unsigned i=0; i < dataPageCount; ++i) { + addPageStartsV4

(pageContent, bitmapForPage, info, pageStarts, pageExtras); + if ( _diagnostics.hasError() ) { + return; + } + pageContent += pageSize; + bitmapForPage += (sizeof(bool)*(pageSize/4)); + } + // fill in computed info + 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_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 < 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); + dataRegion.slideInfoFileSize = slideInfoSize; + if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) { + _diagnostics.error("kernel slide info overflow buffer"); + } + // Update the mapping entry on the cache header + const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; + dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset); + slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize; + //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size()); + } +} +#endif + +/* +void CacheBuilder::writeSlideInfoV1() +{ + // build one 128-byte bitmap per page (4096) of DATA + uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset; + uint8_t* const dataEnd = dataStart + regions[1].size; + const long bitmapSize = (dataEnd - dataStart)/(4*8); + uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1); + for (void* p : _pointersForASLR) { + if ( (p < dataStart) || ( p > dataEnd) ) + terminate("DATA pointer for sliding, out of range\n"); + long offset = (long)((uint8_t*)p - dataStart); + if ( (offset % 4) != 0 ) + terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset); + long byteIndex = offset / (4*8); + long bitInByte = (offset % 32) >> 2; + bitmap[byteIndex] |= (1 << bitInByte); + } + + // allocate worst case size block of all slide info + const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes. + const unsigned toc_count = (unsigned)bitmapSize/entry_size; + dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset); + slideInfo->version = 1; + slideInfo->toc_offset = sizeof(dyld_cache_slide_info); + slideInfo->toc_count = toc_count; + slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128); + slideInfo->entries_count = 0; + slideInfo->entries_size = entry_size; + // append each unique entry + const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap; + dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset()); + int entry_count = 0; + for (int i=0; i < toc_count; ++i) { + const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i]; + // see if it is same as one already added + bool found = false; + for (int j=0; j < entry_count; ++j) { + if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) { + slideInfo->set_toc(i, j); + found = true; + break; + } + } + if ( !found ) { + // append to end + memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size); + slideInfo->set_toc(i, entry_count++); + } + } + slideInfo->entries_count = entry_count; + ::free((void*)bitmap); + + _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2); +} + +*/ + + +void SharedCacheBuilder::setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next) +{ + assert(targetVMAddr > _readExecuteRegion.unslidLoadAddress); + assert(targetVMAddr < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse); + dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp; + uint16_t diversity; + bool hasAddrDiv; + uint8_t key; + if ( _aslrTracker.hasAuthData(loc, &diversity, &hasAddrDiv, &key) ) { + // if base cache address cannot fit into target, then use offset + tmp.arm64e.authRebase.target = _readExecuteRegion.unslidLoadAddress; + if ( tmp.arm64e.authRebase.target != _readExecuteRegion.unslidLoadAddress ) + targetVMAddr -= _readExecuteRegion.unslidLoadAddress; + loc->arm64e.authRebase.target = targetVMAddr; + loc->arm64e.authRebase.diversity = diversity; + loc->arm64e.authRebase.addrDiv = hasAddrDiv; + loc->arm64e.authRebase.key = key; + loc->arm64e.authRebase.next = next; + loc->arm64e.authRebase.bind = 0; + loc->arm64e.authRebase.auth = 1; + assert(loc->arm64e.authRebase.target == targetVMAddr && "target truncated"); + assert(loc->arm64e.authRebase.next == next && "next location truncated"); + } + else { + uint8_t highByte = 0; + _aslrTracker.hasHigh8(loc, &highByte); + // if base cache address cannot fit into target, then use offset + tmp.arm64e.rebase.target = _readExecuteRegion.unslidLoadAddress; + if ( tmp.arm64e.rebase.target != _readExecuteRegion.unslidLoadAddress ) + targetVMAddr -= _readExecuteRegion.unslidLoadAddress; + loc->arm64e.rebase.target = targetVMAddr; + loc->arm64e.rebase.high8 = highByte; + loc->arm64e.rebase.next = next; + loc->arm64e.rebase.bind = 0; + loc->arm64e.rebase.auth = 0; + assert(loc->arm64e.rebase.target == targetVMAddr && "target truncated"); + assert(loc->arm64e.rebase.next == next && "next location truncated"); + } +} + +uint16_t SharedCacheBuilder::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; + } + dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);; + if ( lastLoc != nullptr ) { + // convert vmaddr based pointers to arm64e dyld cache chains + setPointerContentV3(lastLoc, lastLoc->raw64, loc - lastLoc); + } + lastLoc = loc; + } + } + if ( lastLoc != nullptr ) { + // convert vmaddr based pointers to arm64e dyld cache chain, and mark end of chain + setPointerContentV3(lastLoc, lastLoc->raw64, 0); + } + return result; +} + + +void SharedCacheBuilder::writeSlideInfoV3(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions) +{ + const uint32_t pageSize = _aslrTracker.pageSize(); + const uint8_t* firstDataRegionBuffer = firstDataRegion()->buffer; + for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) { + Region& dataRegion = _dataRegions[dataRegionIndex]; + // fprintf(stderr, "writeSlideInfoV3: %s 0x%llx->0x%llx\n", dataRegion.name.c_str(), dataRegion.cacheFileOffset, dataRegion.cacheFileOffset + dataRegion.sizeInUse); + // fill in fixed info + assert(dataRegion.slideInfoFileOffset != 0); + assert((dataRegion.sizeInUse % pageSize) == 0); + unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize; + dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)dataRegion.slideInfoBuffer; + info->version = 3; + info->page_size = pageSize; + info->page_starts_count = dataPageCount; + info->auth_value_add = _archLayout->sharedMemoryStart; + + // fill in per-page starts + const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4)); + uint8_t* pageContent = dataRegion.buffer; + unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize; + assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions); + const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion); + const bool* bitmapForPage = bitmapForRegion; + //for (unsigned i=0; i < dataPageCount; ++i) { + dispatch_apply(dataPageCount, DISPATCH_APPLY_AUTO, ^(size_t i) { + info->page_starts[i] = pageStartV3(pageContent + (i * pageSize), pageSize, bitmapForPage + (i * bitmapEntriesPerPage)); + }); + + // update region with final size + dataRegion.slideInfoFileSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2); + if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) { + _diagnostics.error("kernel slide info overflow buffer"); + } + // Update the mapping entry on the cache header + const dyld_cache_header* cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer; + dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset); + slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize; + } +} diff --git a/dyld3/shared-cache/SharedCacheBuilder.h b/dyld3/shared-cache/SharedCacheBuilder.h new file mode 100644 index 0000000..053a3e0 --- /dev/null +++ b/dyld3/shared-cache/SharedCacheBuilder.h @@ -0,0 +1,173 @@ +/* + * 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 SharedCacheBuilder_h +#define SharedCacheBuilder_h + +#include "CacheBuilder.h" +#include "DyldSharedCache.h" +#include "ClosureFileSystem.h" +#include "IMPCachesBuilder.hpp" + +class SharedCacheBuilder : public CacheBuilder { +public: + SharedCacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem); + + void build(std::vector& inputFiles, + std::vector& aliases); + void build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables, + std::vector& aliases); + void build(const std::vector& dylibsToCache, + const std::vector& otherOsDylibs, + const std::vector& osExecutables, + std::vector& aliases); + + void writeFile(const std::string& path); + void writeBuffer(uint8_t*& buffer, uint64_t& size); + void writeMapFile(const std::string& path); + std::string getMapFileBuffer() const; + std::string getMapFileJSONBuffer(const std::string& cacheDisposition) const; + void deleteBuffer(); + const std::set warnings(); + const std::set evictions(); + const bool agileSignature(); + const std::string cdHashFirst(); + const std::string cdHashSecond(); + const std::string uuid() const; + + void forEachCacheDylib(void (^callback)(const std::string& path)); + void forEachCacheSymlink(void (^callback)(const std::string& path)); + + void forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) override final; + +private: + + void writeSlideInfoV1(); + + template void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount); + template bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); + template void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& pageExtras); + + void writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun); + uint16_t pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]); + void setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc, uint64_t targetVMAddr, size_t next); + + template void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount); + template bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info); + template void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info, + std::vector& pageStarts, std::vector& pageExtras); + + struct ArchLayout + { + uint64_t sharedMemoryStart; + uint64_t sharedMemorySize; + uint64_t sharedRegionPadding; + uint64_t pointerDeltaMask; + const char* archName; + uint16_t csPageSize; + uint8_t sharedRegionAlignP2; + uint8_t slideInfoBytesPerPage; + bool sharedRegionsAreDiscontiguous; + bool is64; + bool useValueAdd; + }; + + static const ArchLayout _s_archLayout[]; + static const char* const _s_neverStubEliminateSymbols[]; + + void makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder); + void processSelectorStrings(const std::vector& executables, IMPCaches::HoleMap& selectorsHoleMap); + void parseCoalescableSegments(IMPCaches::SelectorMap& selectorMap, IMPCaches::HoleMap& selectorsHoleMap); + void assignSegmentAddresses(); + void assignMultipleDataSegmentAddresses(uint64_t& addr, uint32_t totalProtocolDefCount); + + uint64_t dataRegionsTotalSize() const; + uint64_t dataRegionsSizeInUse() const; + + // Return the earliest data region by address + const Region* firstDataRegion() const; + + // Return the lateset data region by address + const Region* lastDataRegion() const; + + uint64_t cacheOverflowAmount(); + size_t evictLeafDylibs(uint64_t reductionTarget, std::vector& overflowDylibs); + + void fipsSign(); + void codeSign(); + uint64_t pathHash(const char* path); + void writeCacheHeader(); + void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName); + void addImageArray(); + void buildImageArray(std::vector& aliases); + void addOtherImageArray(const std::vector&, std::vector& overflowDylibs); + void addClosures(const std::vector&); + void markPaddingInaccessible(); + + bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset)); + + // implemented in OptimizerObjC.cpp + void optimizeObjC(bool impCachesSuccess, const std::vector & inlinedSelectors); + uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount); + uint32_t computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount); + + void emitContantObjects(); + + typedef std::unordered_map InstallNameToMA; + + typedef uint64_t CacheOffset; + + std::vector _sortedDylibs; + std::vector _dataRegions; // 1 or more __DATA regions. + UnmappedRegion _codeSignatureRegion; + std::set _evictions; + const ArchLayout* _archLayout = nullptr; + uint32_t _aliasCount = 0; + uint8_t* _objcReadOnlyBuffer = nullptr; + uint64_t _objcReadOnlyBufferSizeUsed = 0; + uint64_t _objcReadOnlyBufferSizeAllocated = 0; + uint8_t* _objcReadWriteBuffer = nullptr; + uint64_t _objcReadWriteBufferSizeAllocated = 0; + uint64_t _objcReadWriteFileOffset = 0; + uint64_t _selectorStringsFromExecutables = 0; + InstallNameToMA _installNameToCacheDylib; + std::unordered_map _dataDirtySegsOrder; + std::map _missingWeakImports; + const dyld3::closure::ImageArray* _imageArray = nullptr; + uint8_t _cdHashFirst[20]; + uint8_t _cdHashSecond[20]; + bool _someDylibsUsedChainedFixups = false; + std::unordered_map> _dylibToItsExports; + std::set> _dylibWeakExports; + std::unordered_map> _exportsToUses; + std::unordered_map _exportsToName; + IMPCaches::IMPCachesBuilder* _impCachesBuilder; +}; + + + +#endif /* SharedCacheBuilder_h */ diff --git a/dyld3/shared-cache/StringUtils.h b/dyld3/shared-cache/StringUtils.h index 4bab986..b88f8fe 100644 --- a/dyld3/shared-cache/StringUtils.h +++ b/dyld3/shared-cache/StringUtils.h @@ -82,7 +82,7 @@ inline void putHexByte(uint8_t value, char*& p) putHexNibble(value & 0x0F, p); } -inline uint8_t hexCharToUInt(const char hexByte, uint8_t& value) { +inline bool hexCharToUInt(const char hexByte, uint8_t& value) { if (hexByte >= '0' && hexByte <= '9') { value = hexByte - '0'; return true; @@ -122,20 +122,21 @@ inline uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte) { return retval; } -inline bool hexToBytes(const char* startHexByte, uint32_t length, uint8_t buffer[]) { - if (startHexByte == nullptr) - return false; - const char *currentHexByte = startHexByte; - for (uint32_t i = 0; i < length; ++i) { - uint8_t value; - if (!hexCharToUInt(currentHexByte[i], value)) { +inline bool hexStringToBytes(const char* hexString, uint8_t buffer[], unsigned bufferMaxSize, unsigned& bufferLenUsed) +{ + bufferLenUsed = 0; + bool high = true; + for (const char* s=hexString; *s != '\0'; ++s) { + if ( bufferLenUsed > bufferMaxSize ) return false; - } - if (i%2 == 0) { - buffer[i/2] = value << 4; - } else { - buffer[(i-1)/2] |= value; - } + uint8_t value; + if ( !hexCharToUInt(*s, value) ) + return false; + if ( high ) + buffer[bufferLenUsed] = value << 4; + else + buffer[bufferLenUsed++] |= value; + high = !high; } return true; } diff --git a/launch-cache/dsc_extractor.cpp b/dyld3/shared-cache/dsc_extractor.cpp similarity index 73% rename from launch-cache/dsc_extractor.cpp rename to dyld3/shared-cache/dsc_extractor.cpp index 95a5fa1..a8d1ab3 100644 --- a/launch-cache/dsc_extractor.cpp +++ b/dyld3/shared-cache/dsc_extractor.cpp @@ -45,13 +45,13 @@ #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 "MachOAnalyzer.h" +#include "SupportedArchs.h" +#include "Trie.hpp" #include #include @@ -88,23 +88,17 @@ typedef std::unordered_map, CStringHash, CStr class NotReExportSymbol { public: NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} - bool operator()(const mach_o::trie::Entry &entry) const { - bool result = isSymbolReExport(entry); - if (result) { - // Xcode 6 leaks in dyld_shared_cache_extract_dylibs - ::free((void*)entry.name); - const_cast(&entry)->name = NULL; - } - return result; + bool operator()(const ExportInfoTrie::Entry &entry) const { + return isSymbolReExport(entry); } private: - bool isSymbolReExport(const mach_o::trie::Entry &entry) const { - if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + bool isSymbolReExport(const ExportInfoTrie::Entry &entry) const { + if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) return true; - if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) + if ( (entry.info.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 ) + if ( _reexportDeps.count((int)entry.info.other) != 0 ) return true; return false; } @@ -122,134 +116,119 @@ class LinkeditOptimizer { typedef typename A::P::uint_t pint_t; private: - macho_segment_command

* linkEditSegCmd = NULL; - macho_symtab_command

* symtab = NULL; - macho_dysymtab_command

* dynamicSymTab = NULL; - macho_linkedit_data_command

* functionStarts = NULL; - macho_linkedit_data_command

* dataInCode = NULL; + macho_segment_command

* linkEditSegCmd = nullptr; + symtab_command* symtab = nullptr; + dysymtab_command* dynamicSymTab = nullptr; + linkedit_data_command* functionStarts = nullptr; + linkedit_data_command* dataInCode = nullptr; uint32_t exportsTrieOffset = 0; uint32_t exportsTrieSize = 0; std::set reexportDeps; - + public: - - void optimize_loadcommands(macho_header* mh) + + void optimize_loadcommands(dyld3::MachOAnalyzer* 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 - + 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

* const cmds = (macho_load_command

*)((uint8_t*)mh + sizeof(macho_header

)); - const uint32_t cmdCount = mh->ncmds(); - const macho_load_command

* cmd = cmds; - int depIndex = 0; - for (uint32_t i = 0; i < cmdCount; ++i) { - bool remove = false; - switch ( cmd->cmd() ) { - case macho_segment_command

::CMD: - { - // update segment/section file offsets - macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + __block uint64_t cumulativeFileSize = 0; + __block int depIndex = 0; + Diagnostics diag; + mh->forEachLoadCommand(diag, ^(const load_command* cmd, bool &stop) { + switch ( cmd->cmd ) { + case macho_segment_command

::CMD: { + auto segCmd = (macho_segment_command

*)cmd; segCmd->set_fileoff(cumulativeFileSize); segCmd->set_filesize(segCmd->vmsize()); - macho_section

* const sectionsStart = (macho_section

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

)); - macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { - if ( sect->offset() != 0 ) - sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr())); + + auto const sectionsStart = (macho_section

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

)); + auto const sectionsEnd = §ionsStart[segCmd->nsects()]; + for (auto 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 ) { + 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

* dyldInfo = (macho_dyld_info_command

*)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; + } break; + case LC_DYLD_INFO_ONLY: { + // zero out all dyld info. lldb only uses symbol table + auto dyldInfo = (dyld_info_command*)cmd; + exportsTrieOffset = dyldInfo->export_off; + exportsTrieSize = dyldInfo->export_size; + dyldInfo->rebase_off = 0; + dyldInfo->rebase_size = 0; + dyldInfo->bind_off = 0; + dyldInfo->bind_size = 0; + dyldInfo->weak_bind_off = 0; + dyldInfo->weak_bind_size = 0; + dyldInfo->lazy_bind_off = 0; + dyldInfo->lazy_bind_size = 0; + dyldInfo->export_off = 0; + dyldInfo->export_size = 0; + } break; + case LC_DYLD_EXPORTS_TRIE: { + // don't put export trie into extracted dylib. lldb only uses symbol table + linkedit_data_command* exportsTrie = (linkedit_data_command*)cmd; + exportsTrieOffset = exportsTrie->dataoff; + exportsTrieSize = exportsTrie->datasize; + exportsTrie->dataoff = 0; + exportsTrie->datasize = 0; + } break; case LC_SYMTAB: - symtab = (macho_symtab_command

*)cmd; + symtab = (symtab_command*)cmd; break; case LC_DYSYMTAB: - dynamicSymTab = (macho_dysymtab_command

*)cmd; + dynamicSymTab = (dysymtab_command*)cmd; break; case LC_FUNCTION_STARTS: - functionStarts = (macho_linkedit_data_command

*)cmd; + functionStarts = (linkedit_data_command*)cmd; break; case LC_DATA_IN_CODE: - dataInCode = (macho_linkedit_data_command

*)cmd; + dataInCode = (linkedit_data_command*)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 ) { + depIndex++; + if ( cmd->cmd == LC_REEXPORT_DYLIB ) { reexportDeps.insert(depIndex); } break; + default: + break; + } + }); + + mh->removeLoadCommand(diag, ^(const load_command* cmd, bool& remove, bool &stop) { + switch ( cmd->cmd ) { case LC_SEGMENT_SPLIT_INFO: // dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO remove = true; + stop = true; + break; + default: break; } - uint32_t cmdSize = cmd->cmdsize(); - macho_load_command

* nextCmd = (macho_load_command

*)(((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 &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 ) { + if ( linkEditSegCmd == nullptr ) { fprintf(stderr, "__LINKEDIT not found\n"); return -1; } - if ( symtab == NULL ) { + if ( symtab == nullptr ) { fprintf(stderr, "LC_SYMTAB not found\n"); return -1; } - if ( dynamicSymTab == NULL ) { + if ( dynamicSymTab == nullptr ) { fprintf(stderr, "LC_DYSYMTAB not found\n"); return -1; } @@ -258,10 +237,10 @@ public: uint32_t functionStartsSize = 0; if ( functionStarts != NULL ) { // copy function starts from original cache file to new mapped dylib file - functionStartsSize = functionStarts->datasize(); + functionStartsSize = functionStarts->datasize; new_linkedit_data.insert(new_linkedit_data.end(), - (char*)mapped_cache + functionStarts->dataoff(), - (char*)mapped_cache + functionStarts->dataoff() + functionStartsSize); + (char*)mapped_cache + functionStarts->dataoff, + (char*)mapped_cache + functionStarts->dataoff + functionStartsSize); } // pointer align @@ -272,62 +251,46 @@ public: uint32_t dataInCodeSize = 0; if ( dataInCode != NULL ) { // copy data-in-code info from original cache file to new mapped dylib file - dataInCodeSize = dataInCode->datasize(); + dataInCodeSize = dataInCode->datasize; new_linkedit_data.insert(new_linkedit_data.end(), - (char*)mapped_cache + dataInCode->dataoff(), - (char*)mapped_cache + dataInCode->dataoff() + dataInCodeSize); + (char*)mapped_cache + dataInCode->dataoff, + (char*)mapped_cache + dataInCode->dataoff + dataInCodeSize); } - std::vector exports; + std::vector 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); + ExportInfoTrie::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* header = (dyldCacheHeader*)mapped_cache; - macho_nlist

* localNlists = NULL; - uint32_t localNlistCount = 0; - const char* localStrings = NULL; - const char* localStringsEnd = NULL; - if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) { - dyldCacheLocalSymbolsInfo* localInfo = (dyldCacheLocalSymbolsInfo*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset()); - dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset()); - macho_nlist

* allLocalNlists = (macho_nlist

*)(((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; - } + const DyldSharedCache* cache = (DyldSharedCache*)mapped_cache; + macho_nlist

* allLocalNlists = (macho_nlist

*)cache->getLocalNlistEntries(); + __block macho_nlist

* localNlists = nullptr; + __block uint32_t localNlistCount = 0; + cache->forEachLocalSymbolEntry(^(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop){ + if (dylibOffset == textOffsetInCache) { + localNlists = &allLocalNlists[nlistStartIndex]; + localNlistCount = nlistCount; + stop = true; } - } + }); // compute number of symbols in new symbol table - const macho_nlist

* const mergedSymTabStart = (macho_nlist

*)(((uint8_t*)mapped_cache) + symtab->symoff()); - const macho_nlist

* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()]; - uint32_t newSymCount = symtab->nsyms(); - if ( localNlists != NULL ) { - newSymCount = localNlistCount; - for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { - // skip any locals in cache - if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) - continue; - ++newSymCount; - } + const macho_nlist

* mergedSymTabStart = (macho_nlist

*)(((uint8_t*)mapped_cache) + symtab->symoff); + const macho_nlist

* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms]; + uint32_t newSymCount = symtab->nsyms; + if ( localNlistCount != 0 ) { + // if we are recombining with unmapped locals, recompute new total size + newSymCount = localNlistCount + dynamicSymTab->nextdefsym + dynamicSymTab->nundefsym; } // 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()]; + const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff; + const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize]; // First count how many entries we need std::vector> newSymTab; @@ -337,10 +300,31 @@ public: // first pool entry is always empty string newSymNames.push_back('\0'); + // local symbols are first in dylibs, if this cache has unmapped locals, insert them all first + uint32_t undefSymbolShift = 0; + if ( localNlistCount != 0 ) { + const char* localStrings = cache->getLocalStrings(); + undefSymbolShift = localNlistCount - dynamicSymTab->nlocalsym; + // update load command to reflect new count of locals + dynamicSymTab->ilocalsym = (uint32_t)newSymTab.size(); + dynamicSymTab->nlocalsym = localNlistCount; + // copy local symbols + for (uint32_t i=0; i < localNlistCount; ++i) { + const char* localName = &localStrings[localNlists[i].n_strx()]; + if ( localName > localStrings + cache->getLocalStringsSize() ) + localName = ""; + macho_nlist

t = localNlists[i]; + t.set_n_strx((uint32_t)newSymNames.size()); + newSymNames.insert(newSymNames.end(), + localName, + localName + (strlen(localName) + 1)); + newSymTab.push_back(t); + } + // now start copying symbol table from start of externs instead of start of locals + mergedSymTabStart = &mergedSymTabStart[dynamicSymTab->iextdefsym]; + } + // copy full symbol table from cache (skipping locals if they where elsewhere) for (const macho_nlist

* 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

t = *s; t.set_n_strx((uint32_t)newSymNames.size()); const char* symName = &mergedStringPoolStart[s->n_strx()]; @@ -352,7 +336,7 @@ public: newSymTab.push_back(t); } // recreate N_INDR symbols in extracted dylibs for debugger - for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { macho_nlist

t; memset(&t, 0, sizeof(t)); t.set_n_strx((uint32_t)newSymNames.size()); @@ -360,34 +344,17 @@ public: 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; + it->name.c_str(), + it->name.c_str() + (it->name.size() + 1)); + const char* importName = it->info.importName.c_str(); if ( *importName == '\0' ) - importName = it->name; + importName = it->name.c_str(); 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 = ""; - macho_nlist

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"); @@ -413,12 +380,17 @@ public: const uint64_t newIndSymTabOffset = new_linkedit_data.size(); - // Copy indirect symbol table - const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff()); + // Copy (and adjust) 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())); - + (char*)(mergedIndSymTab + dynamicSymTab->nindirectsyms)); + if ( undefSymbolShift != 0 ) { + uint32_t* newIndSymTab = (uint32_t*)&new_linkedit_data[newIndSymTabOffset]; + for (int i=0; i < dynamicSymTab->nindirectsyms; ++i) { + newIndSymTab[i] += undefSymbolShift; + } + } const uint64_t newStringPoolOffset = new_linkedit_data.size(); // pointer align string pool size @@ -429,31 +401,25 @@ public: // update load commands if ( functionStarts != NULL ) { - functionStarts->set_dataoff((uint32_t)(newFunctionStartsOffset + linkEditSegCmd->fileoff())); - functionStarts->set_datasize(functionStartsSize); + functionStarts->dataoff = (uint32_t)(newFunctionStartsOffset + linkEditSegCmd->fileoff()); + functionStarts->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) ); - - // Xcode 6 leaks in dyld_shared_cache_extract_dylibs - for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { - ::free((void*)(it->name)); + dataInCode->dataoff = (uint32_t)(newDataInCodeOffset + linkEditSegCmd->fileoff()); + dataInCode->datasize = dataInCodeSize; } + symtab->nsyms = newSymCount; + symtab->symoff = (uint32_t)(newSymTabOffset + linkEditSegCmd->fileoff()); + symtab->stroff = (uint32_t)(newStringPoolOffset + linkEditSegCmd->fileoff()); + symtab->strsize = (uint32_t)newSymNames.size(); + dynamicSymTab->extreloff = 0; + dynamicSymTab->nextrel = 0; + dynamicSymTab->locreloff = 0; + dynamicSymTab->nlocrel = 0; + dynamicSymTab->indirectsymoff = (uint32_t)(newIndSymTabOffset + linkEditSegCmd->fileoff()); + linkEditSegCmd->set_filesize(symtab->stroff + symtab->strsize - linkEditSegCmd->fileoff()); + linkEditSegCmd->set_vmsize((linkEditSegCmd->filesize() + 4095) & (-4096)); return 0; } @@ -518,7 +484,7 @@ void dylib_maker(const void* mapped_cache, std::vector &dylib_data, con new_linkedit_data.reserve(1 << 20); LinkeditOptimizer linkeditOptimizer; - macho_header

* mh = (macho_header

*)&new_dylib_data.front(); + dyld3::MachOAnalyzer* mh = (dyld3::MachOAnalyzer*)&new_dylib_data.front(); linkeditOptimizer.optimize_loadcommands(mh); linkeditOptimizer.optimize_linkedit(new_linkedit_data, textOffsetInCache, mapped_cache); @@ -646,9 +612,9 @@ static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) { // 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> 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)); + for (uint32_t i = 0; i != dyldSharedCache->header.mappingCount; ++i) { + sharedCacheRegions.emplace_back(std::make_pair(mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size)); + } if (dyldSharedCache->header.localSymbolsSize) sharedCacheRegions.emplace_back(std::make_pair(dyldSharedCache->header.localSymbolsOffset, dyldSharedCache->header.localSymbolsOffset + dyldSharedCache->header.localSymbolsSize)); size_t inBbufferSize = 0; @@ -734,7 +700,14 @@ static int sharedCacheIsValid(const void* mapped_cache, uint64_t 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)) ) + bool isDataPage = false; + for (unsigned mappingIndex = 1; mappingIndex != (dyldSharedCache->header.mappingCount - 1); ++mappingIndex) { + if ( (fileOffset >= mappings[mappingIndex].fileOffset) && (fileOffset < (mappings[mappingIndex].fileOffset + mappings[mappingIndex].size)) ) { + isDataPage = true; + break; + } + } + if ( isDataPage ) continue; CCDigest(dscDigestFormat, (uint8_t*)mapped_cache + fileOffset, (size_t)csPageSize, cdHashBuffer); diff --git a/launch-cache/dsc_extractor.h b/dyld3/shared-cache/dsc_extractor.h similarity index 100% rename from launch-cache/dsc_extractor.h rename to dyld3/shared-cache/dsc_extractor.h diff --git a/dyld3/shared-cache/dsc_iterator.cpp b/dyld3/shared-cache/dsc_iterator.cpp new file mode 100644 index 0000000..f20cf7b --- /dev/null +++ b/dyld3/shared-cache/dsc_iterator.cpp @@ -0,0 +1,96 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + + +#include "dsc_iterator.h" +#define NO_ULEB +#include "DyldSharedCache.h" +#include "MachOAnalyzer.h" + + +static void forEachDylibInCache(const void* shared_cache_file, void (^handler)(const dyld_cache_image_info* cachedDylibInfo, bool isAlias)) +{ + const dyld_cache_header* header = (dyld_cache_header*)shared_cache_file; + const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)shared_cache_file + header->imagesOffset); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)shared_cache_file + header->mappingOffset); + if ( mappings[0].fileOffset != 0 ) + return; + uint64_t firstImageOffset = 0; + uint64_t firstRegionAddress = mappings[0].address; + for (uint32_t i=0; i < header->imagesCount; ++i) { + uint64_t offset = dylibs[i].address - firstRegionAddress; + if ( firstImageOffset == 0 ) + firstImageOffset = offset; + // skip over aliases + bool isAlias = (dylibs[i].pathFileOffset < firstImageOffset); + handler(&dylibs[i], isAlias); + } +} + + +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { + const dyld_cache_header* header = (dyld_cache_header*)shared_cache_file; + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)shared_cache_file + header->mappingOffset); + const uint64_t unslideLoadAddress = mappings[0].address; + + __block uint32_t index = 0; + __block int result = 0; + forEachDylibInCache(shared_cache_file, ^(const dyld_cache_image_info* cachedDylibInfo, bool isAlias) { + uint64_t imageCacheOffset = cachedDylibInfo->address - unslideLoadAddress; + const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)((uint8_t*)shared_cache_file + imageCacheOffset); + const char* dylibPath = (char*)shared_cache_file + cachedDylibInfo->pathFileOffset; + + dyld_shared_cache_dylib_info dylibInfo; + uuid_t uuid; + dylibInfo.version = 2; + dylibInfo.machHeader = ma; + dylibInfo.path = dylibPath; + dylibInfo.modTime = cachedDylibInfo->modTime; + dylibInfo.inode = cachedDylibInfo->inode; + dylibInfo.isAlias = isAlias; + ma->getUuid(uuid); + dylibInfo.uuid = &uuid; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + if ( info.fileSize > info.vmSize ) { + stop = true; + return; + } + dyld_shared_cache_segment_info segInfo; + segInfo.version = 2; + segInfo.name = info.segName; + segInfo.fileOffset = info.fileOffset; + segInfo.fileSize = info.vmSize; + segInfo.address = info.vmAddr; + segInfo.addressOffset = info.vmAddr - unslideLoadAddress; + callback(&dylibInfo, &segInfo); + }); + index++; + }); + return result; +} diff --git a/launch-cache/dsc_iterator.h b/dyld3/shared-cache/dsc_iterator.h similarity index 64% rename from launch-cache/dsc_iterator.h rename to dyld3/shared-cache/dsc_iterator.h index d0ea94d..00fb835 100644 --- a/launch-cache/dsc_iterator.h +++ b/dyld3/shared-cache/dsc_iterator.h @@ -50,35 +50,17 @@ struct dyld_shared_cache_segment_info { }; typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; - #ifdef __cplusplus extern "C" { -#endif - - +#endif // Given a pointer and size of an in-memory copy of a dyld shared cache file, // this routine will call the callback block once for each segment in each dylib // in the shared cache file. // Returns -1 if there was an error, otherwise 0. -extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); - - -// -// The following iterator functions are deprecated: -// -typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); -typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide); -typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData); -typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData); - -extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); -extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); -extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); -extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated)); - - #ifdef __cplusplus } -#endif +#endif + diff --git a/dyld3/shared-cache/dyld_cache_format.h b/dyld3/shared-cache/dyld_cache_format.h index d5033e2..fd74a22 100644 --- a/dyld3/shared-cache/dyld_cache_format.h +++ b/dyld3/shared-cache/dyld_cache_format.h @@ -38,8 +38,8 @@ struct dyld_cache_header uint64_t dyldBaseAddress; // base address of dyld when cache was built uint64_t codeSignatureOffset; // file offset of code signature blob uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) - uint64_t slideInfoOffset; // file offset of kernel slid info - uint64_t slideInfoSize; // size of kernel slid info + uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info + uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info uint64_t localSymbolsOffset; // file offset of where local symbols are stored uint64_t localSymbolsSize; // size of local symbols information uint8_t uuid[16]; // unique value for each shared cache file @@ -76,6 +76,8 @@ struct dyld_cache_header 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 + uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info + uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries }; // Uncomment this and check the build errors for the current mapping offset to check against when adding new fields. @@ -90,6 +92,24 @@ struct dyld_cache_mapping_info { uint32_t initProt; }; +// Contains the flags for the dyld_cache_mapping_and_slide_info flgs field +enum { + DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 0U, + DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1U, + DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2U, +}; + +struct dyld_cache_mapping_and_slide_info { + uint64_t address; + uint64_t size; + uint64_t fileOffset; + uint64_t slideInfoFileOffset; + uint64_t slideInfoFileSize; + uint64_t flags; + uint32_t maxProt; + uint32_t initProt; +}; + struct dyld_cache_image_info { uint64_t address; @@ -474,7 +494,8 @@ struct dyld_cache_patchable_export struct dyld_cache_patchable_location { uint64_t cacheOffset : 32, - addend : 12, // +/- 2048 + high7 : 7, + addend : 5, // 0..31 authenticated : 1, usesAddressDiversity : 1, key : 2, @@ -482,8 +503,12 @@ struct dyld_cache_patchable_location }; +// This is the location of the macOS shared cache on macOS 11.0 and later +#define MACOSX_MRM_DYLD_SHARED_CACHE_DIR "/System/Library/dyld/" + +// This is old define for the old location of the dyld cache +#define MACOSX_DYLD_SHARED_CACHE_DIR MACOSX_MRM_DYLD_SHARED_CACHE_DIR -#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" #define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" #if !TARGET_OS_SIMULATOR #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" diff --git a/dyld3/shared-cache/dyld_closure_util.cpp b/dyld3/shared-cache/dyld_closure_util.cpp index edbc704..6038177 100644 --- a/dyld3/shared-cache/dyld_closure_util.cpp +++ b/dyld3/shared-cache/dyld_closure_util.cpp @@ -48,6 +48,7 @@ #include "ClosureBuilder.h" #include "ClosurePrinter.h" #include "ClosureFileSystemPhysical.h" +#include "RootsChecker.h" using dyld3::closure::ImageArray; using dyld3::closure::Image; @@ -81,15 +82,16 @@ static const DyldSharedCache* mapCacheFile(const char* path) } const dyld_cache_header* header = (dyld_cache_header*)firstPage; const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset); + const dyld_cache_mapping_info* lastMapping = &mappings[header->mappingCount - 1]; - size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address); + size_t vmSize = (size_t)(lastMapping->address + lastMapping->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) { + for (int i=0; i < header->mappingCount; ++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) { @@ -273,13 +275,8 @@ int main(int argc, const char* argv[]) 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 size_t cacheLength; dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength); -#endif } dyld3::Platform platform = dyldCache->platform(); const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true); @@ -291,11 +288,13 @@ int main(int argc, const char* argv[]) 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()); + if ( auto others = dyldCache->otherOSImageArray() ) + imagesArrays.push_back(others); dyld3::closure::FileSystemPhysical fileSystem(fsRootPath, fsOverlayPath); + dyld3::RootsChecker rootsChecker; ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none; - ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr); + ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr); if (forceInvalidFormatVersion) builder.setDyldCacheInvalidFormatVersion(); @@ -325,13 +324,13 @@ int main(int argc, const char* argv[]) else { Diagnostics diag; char realerPath[MAXPATHLEN]; - dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, platform, realerPath); + dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, builder.platform(), realerPath); li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent); } } ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths; - ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, platform, nullptr); + ClosureBuilder dlopenBuilder(nextNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, builder.platform(), nullptr); if (forceInvalidFormatVersion) dlopenBuilder.setDyldCacheInvalidFormatVersion(); @@ -373,7 +372,8 @@ int main(int argc, const char* argv[]) if ( closure != nullptr ) { STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3); imagesArrays.push_back(dyldCache->cachedDylibsImageArray()); - imagesArrays.push_back(dyldCache->otherOSImageArray()); + if ( auto others = dyldCache->otherOSImageArray() ) + imagesArrays.push_back(others); imagesArrays.push_back(closure->images()); dyld3::closure::printClosureAsJSON(closure, imagesArrays, verboseFixups, printRaw, dyldCache); } @@ -400,7 +400,8 @@ int main(int argc, const char* argv[]) 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()); + if ( auto others = dyldCache->otherOSImageArray() ) + imagesArrays.push_back(others); dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups); } else { diff --git a/dyld3/shared-cache/dyld_shared_cache_builder.mm b/dyld3/shared-cache/dyld_shared_cache_builder.mm index 4841dc7..2ec8d1f 100644 --- a/dyld3/shared-cache/dyld_shared_cache_builder.mm +++ b/dyld3/shared-cache/dyld_shared_cache_builder.mm @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -59,11 +60,13 @@ #include #include +#include +#include +#include +#include -#include "Manifest.h" #include "Diagnostics.h" #include "DyldSharedCache.h" -#include "BuilderUtils.h" #include "FileUtils.h" #include "JSONReader.h" #include "JSONWriter.h" @@ -100,18 +103,18 @@ int runCommandAndWait(Diagnostics& diags, const char* args[]) return res; } -void processRoots(Diagnostics& diags, std::set& roots, const char *tempRootsDir) +void processRoots(Diagnostics& diags, std::list& roots, const char *tempRootsDir) { - std::set processedRoots; - struct stat sb; - int res = 0; - const char* args[8]; + std::list processedRoots; + struct stat sb; + int res = 0; + const char* args[8]; for (const auto& root : roots) { res = stat(root.c_str(), &sb); if (res == 0 && S_ISDIR(sb.st_mode)) { - processedRoots.insert(root); + processedRoots.push_back(root); continue; } @@ -149,13 +152,6 @@ void processRoots(Diagnostics& diags, std::set& roots, const char * args[3] = (char*)"-C"; args[4] = tempRootDir; args[5] = nullptr; - } else if (endsWith(root, ".xar")) { - args[0] = (char*)"/usr/bin/xar"; - args[1] = (char*)"-xf"; - args[2] = (char*)root.c_str(); - args[3] = (char*)"-C"; - args[4] = tempRootDir; - args[5] = nullptr; } else if (endsWith(root, ".zip")) { args[0] = (char*)"/usr/bin/ditto"; args[1] = (char*)"-xk"; @@ -176,13 +172,13 @@ void processRoots(Diagnostics& diags, std::set& roots, const char * return; } - processedRoots.insert(tempRootDir); + processedRoots.push_back(tempRootDir); } roots = processedRoots; } -void writeRootList(const std::string& dstRoot, const std::set& roots) +void writeRootList(const std::string& dstRoot, const std::list& roots) { if (roots.size() == 0) return; @@ -199,45 +195,36 @@ void writeRootList(const std::string& dstRoot, const std::set& root ::fclose(froots); } -BOMCopierCopyOperation filteredCopyExcludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size) -{ - std::string absolutePath = &path[1]; - void *userData = BOMCopierUserData(copier); - std::set *cachePaths = (std::set*)userData; - if (cachePaths->count(absolutePath)) { - return BOMCopierSkipFile; - } - return BOMCopierContinue; -} +struct FilteredCopyOptions { + Diagnostics* diags = nullptr; + std::set* cachePaths = nullptr; + std::set* dylibsFoundInRoots = nullptr; +}; BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size) { std::string absolutePath = &path[1]; - void *userData = BOMCopierUserData(copier); - std::set *cachePaths = (std::set*)userData; - for (const std::string& cachePath : *cachePaths) { - if (startsWith(cachePath, absolutePath)) - return BOMCopierContinue; + const FilteredCopyOptions *userData = (const FilteredCopyOptions*)BOMCopierUserData(copier); + + // Don't copy from the artifact if the dylib is actally in a -root + if ( userData->dylibsFoundInRoots->count(absolutePath) != 0 ) { + userData->diags->verbose("Skipping copying dylib from shared cache artifact as it is in a -root: '%s'\n", absolutePath.c_str()); + return BOMCopierSkipFile; } - if (cachePaths->count(absolutePath)) { + + for (const std::string& cachePath : *userData->cachePaths) { + if (startsWith(cachePath, absolutePath)) { + userData->diags->verbose("Copying dylib from shared cache artifact: '%s'\n", absolutePath.c_str()); + return BOMCopierContinue; + } + } + if (userData->cachePaths->count(absolutePath)) { + userData->diags->verbose("Copying dylib from shared cache artifact: '%s'\n", absolutePath.c_str()); 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 Disposition stringToDisposition(Diagnostics& diags, const std::string& str) { if (diags.hasError()) return Unknown; @@ -252,37 +239,12 @@ static Disposition stringToDisposition(Diagnostics& diags, const std::string& st return Unknown; } -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 "UIKitForMac"; - case iOS_simulator: - return "iOS_simulator"; - case tvOS_simulator: - return "tvOS_simulator"; - case watchOS_simulator: - return "watchOS_simulator"; - } -} - static Platform stringToPlatform(Diagnostics& diags, const std::string& str) { if (diags.hasError()) return unknown; if (str == "unknown") return unknown; - if (str == "macOS") + if ( (str == "macOS") || (str == "osx") ) return macOS; if (str == "iOS") return iOS; @@ -305,23 +267,6 @@ static Platform stringToPlatform(Diagnostics& diags, const std::string& str) { return unknown; } -static std::string fileFlagsToString(FileFlags fileFlags) { - switch (fileFlags) { - case NoFlags: - return "NoFlags"; - case MustBeInCache: - return "MustBeInCache"; - case ShouldBeExcludedFromCacheIfUnusedLeaf: - return "ShouldBeExcludedFromCacheIfUnusedLeaf"; - case RequiredClosure: - return "RequiredClosure"; - case DylibOrderFile: - return "DylibOrderFile"; - case DirtyDataOrderFile: - return "DirtyDataOrderFile"; - } -} - static FileFlags stringToFileFlags(Diagnostics& diags, const std::string& str) { if (diags.hasError()) return NoFlags; @@ -337,48 +282,37 @@ static FileFlags stringToFileFlags(Diagnostics& diags, const std::string& str) { return DylibOrderFile; if (str == "DirtyDataOrderFile") return DirtyDataOrderFile; + if (str == "ObjCOptimizationsFile") + return ObjCOptimizationsFile; return NoFlags; } -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); - } - return buildOptionsNode; -} - struct SharedCacheBuilderOptions { - Diagnostics diags; - std::set 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 buildAllPath; - std::string configuration; - std::string resultPath; - std::string baselineDifferenceResultPath; - std::string baselineCacheMapPath; - bool baselineCopyRoots = false; + Diagnostics diags; + std::list roots; + std::string dylibCacheDir; + std::string artifactDir; + std::string release; + bool emitDevCaches = true; + bool emitCustomerCaches = true; + bool emitElidedDylibs = true; + bool listConfigs = false; + bool copyRoots = false; + bool debug = false; + bool useMRM = false; + std::string dstRoot; + std::string emitJSONPath; + std::string buildAllPath; + std::string resultPath; + std::string baselineDifferenceResultPath; + std::list baselineCacheMapPaths; + bool baselineCopyRoots = false; + bool emitMapFiles = false; + std::set cmdLineArchs; }; static void loadMRMFiles(Diagnostics& diags, - SharedCacheBuilder* sharedCacheBuilder, + MRMSharedCacheBuilder* sharedCacheBuilder, const std::vector>& inputFiles, std::vector>& mappedFiles, const std::set& baselineCacheFiles) { @@ -430,11 +364,53 @@ static void unloadMRMFiles(std::vector>& mappedFi ::munmap((void*)mappedFile.first, mappedFile.second); } -static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) { +static ssize_t write64(int fildes, const void *buf, size_t nbyte) +{ + unsigned char* uchars = (unsigned char*)buf; + ssize_t total = 0; + + while (nbyte) + { + /* + * If we were writing socket- or stream-safe code we'd chuck the + * entire buf to write(2) and then gracefully re-request bytes that + * didn't get written. But write(2) will return EINVAL if you ask it to + * write more than 2^31-1 bytes. So instead we actually need to throttle + * the input to write. + * + * Historically code using write(2) to write to disk will assert that + * that all of the requested bytes were written. It seems harmless to + * re-request bytes as one does when writing to streams, with the + * compromise that we will return immediately when write(2) returns 0 + * bytes written. + */ + size_t limit = 0x7FFFFFFF; + size_t towrite = nbyte < limit ? nbyte : limit; + ssize_t wrote = write(fildes, uchars, towrite); + if (-1 == wrote) + { + return -1; + } + else if (0 == wrote) + { + break; + } + else + { + nbyte -= wrote; + uchars += wrote; + total += wrote; + } + } + + return total; +} + +static bool writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) { if (!cacheBuildSuccess) { uint64_t errorCount = 0; if (const char* const* errors = getErrors(sharedCacheBuilder, &errorCount)) { - for (uint64 i = 0, e = errorCount; i != e; ++i) { + for (uint64_t i = 0, e = errorCount; i != e; ++i) { const char* errorMessage = errors[i]; fprintf(stderr, "ERROR: %s\n", errorMessage); } @@ -444,7 +420,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa // Now emit each cache we generated, or the errors for them. uint64_t cacheResultCount = 0; if (const CacheResult* const* cacheResults = getCacheResults(sharedCacheBuilder, &cacheResultCount)) { - for (uint64 i = 0, e = cacheResultCount; i != e; ++i) { + for (uint64_t i = 0, e = cacheResultCount; i != e; ++i) { const CacheResult& result = *(cacheResults[i]); // Always print the warnings if we have roots, even if there are errors if ( (result.numErrors == 0) || !options.roots.empty() ) { @@ -462,7 +438,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa } if (!cacheBuildSuccess) { - exit(-1); + return false; } // If we built caches, then write everything out. @@ -470,7 +446,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa if (cacheBuildSuccess && !options.dstRoot.empty()) { uint64_t fileResultCount = 0; if (const FileResult* const* fileResults = getFileResults(sharedCacheBuilder, &fileResultCount)) { - for (uint64 i = 0, e = fileResultCount; i != e; ++i) { + for (uint64_t i = 0, e = fileResultCount; i != e; ++i) { const FileResult& result = *(fileResults[i]); switch (result.behavior) { @@ -491,7 +467,7 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa int fd = mkstemp(pathTemplateSpace); if ( fd != -1 ) { ::ftruncate(fd, result.size); - uint64_t writtenSize = pwrite(fd, result.data, result.size, 0); + uint64_t writtenSize = write64(fd, result.data, result.size); 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) { @@ -512,383 +488,60 @@ static void writeMRMResults(bool cacheBuildSuccess, SharedCacheBuilder* sharedCa } } } - } -} -static void reportUnknownConfiguration(const std::string& configuration, dyld3::Manifest& manifest) { - fprintf(stderr, "** Unknown config '%s' for build %s.\n", - configuration.c_str(), manifest.build().c_str()); - - // Look for available configurations that the user might have meant. - // Substring match: print configs that contain the user's string. - // Regex match: if user wants "N61OS" then match .*N.*6.*1.*O.*S.* - - std::string patternString = ".*"; - for (auto c : configuration) { - if (isalnum(c)) { // filter regex special characters - patternString += c; - patternString += ".*"; - } - } - std::regex pattern(patternString); - - std::vector likelyConfigs{}; - std::vector allConfigs{}; - manifest.forEachConfiguration([&](const std::string& configName) { - allConfigs.push_back(configName); - if (!configuration.empty()) { - if (configName.find(configuration) != std::string::npos || std::regex_match(configName, pattern)) { - likelyConfigs.push_back(configName); - } - } - }); - - if (!likelyConfigs.empty()) { - fprintf(stderr, "\nDid you mean:\n"); - for (auto configName: likelyConfigs) { - fprintf(stderr, "%s\n", configName.c_str()); + // Give up if we couldn't write the caches + if (!cacheBuildSuccess) { + return false; } } - fprintf(stderr, "\nAvailable configurations:\n"); - for (auto configName : allConfigs) { - fprintf(stderr, "%s\n", configName.c_str()); - } -} + // Emit the map files + if ( options.emitMapFiles && !options.dstRoot.empty() ) { + uint64_t cacheResultCount = 0; + if (const CacheResult* const* cacheResults = getCacheResults(sharedCacheBuilder, &cacheResultCount)) { + for (uint64_t i = 0, e = cacheResultCount; i != e; ++i) { + const CacheResult& result = *(cacheResults[i]); + std::string_view jsonData = result.mapJSON; + if ( jsonData.empty() ) + continue; -static void buildCacheFromPListManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options) { - // Get the list of configurations, without fetching all of the files. - auto manifest = dyld3::Manifest(diags, options.dylibCacheDir + "/Manifest.plist", false); - - if (manifest.build().empty()) { - fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", options.dylibCacheDir.c_str()); - exit(-1); - } - - // List configurations if requested. - if (options.listConfigs) { - manifest.forEachConfiguration([](const std::string& configName) { - printf("%s\n", configName.c_str()); - }); - // If we weren't passed a configuration then exit - if (options.configuration.empty()) - exit(0); - } - - // Stop if the requested configuration is unavailable. - if (!manifest.filterForConfig(options.configuration)) { - reportUnknownConfiguration(options.configuration, manifest); - exit(-1); - } - - // Now finish initializing the manifest. - // This step is slow so we defer it until after the checks above. - fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str()); - manifest.populate(options.roots); - - (void)mkpath_np((options.dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755); - bool cacheBuildSuccess = false; - if (options.useMRM) { - - std::ofstream jsonFile; - if (!options.emitJSONPath.empty()) { - jsonFile.open(options.emitJSONPath, std::ofstream::out); - if (!jsonFile.is_open()) { - diags.verbose("can't open file '%s'\n", options.emitJSONPath.c_str()); - return; - } - } - dyld3::json::Node buildInvocationNode; - - buildInvocationNode.map["version"].value = "1"; - - // Find the archs for the configuration we want. - __block std::set validArchs; - manifest.configuration(options.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", - options.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 = options.configuration.c_str(); - buildOptions.disposition = Disposition::Unknown; - buildOptions.platform = (Platform)manifest.platform(); - buildOptions.archs = archs; - buildOptions.numArchs = validArchs.size(); - buildOptions.verboseDiagnostics = options.debug; - buildOptions.isLocallyBuiltCache = true; - - __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions); - buildInvocationNode.map["buildOptions"] = getBuildOptionsNode(buildOptions); - - std::set requiredBinaries = { - "/usr/lib/libSystem.B.dylib" - }; - - // Get the file data for every MachO in the BOM. - __block dyld3::json::Node filesNode; - __block std::vector> mappedFiles; - manifest.forEachMachO(options.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 = fileFlagsToString(fileFlags); - filesNode.array.push_back(fileNode); - }); - - __block dyld3::json::Node symlinksNode; - manifest.forEachSymlink(options.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 = fileFlagsToString(FileFlags::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 = fileFlagsToString(FileFlags::DirtyDataOrderFile); - filesNode.array.push_back(fileNode); - } - } - - buildInvocationNode.map["files"] = filesNode; - - if (jsonFile.is_open()) { - dyld3::json::printJSON(buildInvocationNode, 0, jsonFile); - jsonFile.close(); - } - - cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder); - - writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options); - - destroySharedCacheBuilder(sharedCacheBuilder); - - for (auto mappedFile : mappedFiles) - ::munmap((void*)mappedFile.first, mappedFile.second); - } else { - manifest.calculateClosure(); - - cacheBuildSuccess = build(diags, manifest, options.dstRoot, false, options.debug, false, false, options.emitDevCaches, true); - } - - if (!cacheBuildSuccess) { - exit(-1); - } - - // Compare this cache to the baseline cache and see if we have any roots to copy over - if (!options.baselineDifferenceResultPath.empty() || options.baselineCopyRoots) { - std::set baselineDylibs = manifest.resultsForConfiguration(options.configuration); - - std::set newDylibs; - std::map missingDylibReasons; - manifest.forEachConfiguration([&manifest, &newDylibs, &missingDylibReasons](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)); - } else { - missingDylibReasons[manifest.installNameForUUID(dylib.first)] = dylib.second.exclusionInfo; - } - } - } - }); - - // Work out the set of dylibs in the old cache but not the new one - std::map dylibsMissingFromNewCache; - if (options.baselineCopyRoots || !options.baselineDifferenceResultPath.empty()) { - for (const std::string& baselineDylib : baselineDylibs) { - if (!newDylibs.count(baselineDylib)) { - auto reasonIt = missingDylibReasons.find(baselineDylib); - if (reasonIt != missingDylibReasons.end()) - dylibsMissingFromNewCache[baselineDylib] = reasonIt->second; - else - dylibsMissingFromNewCache[baselineDylib] = ""; - } - } - - if (!dylibsMissingFromNewCache.empty()) { - // Work out which dylibs are missing from the new cache, but are not - // coming from the -root which already has them on disk - std::set pathsNotInRoots; - for (std::pair dylibMissingFromNewCache : dylibsMissingFromNewCache) { - const std::string& dylibInstallName = dylibMissingFromNewCache.first; - bool foundInRoot = false; - for (auto& root : options.roots) { - struct stat sb; - std::string filePath = root + "/" + dylibInstallName; - if (!stat(filePath.c_str(), &sb)) { - foundInRoot = true; + const std::string path = options.dstRoot + "/System/Library/dyld/" + result.loggingPrefix + ".json"; + 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, jsonData.size()); + uint64_t writtenSize = write64(fd, jsonData.data(), jsonData.size()); + if ( writtenSize == jsonData.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 } } - if (!foundInRoot) - pathsNotInRoots.insert(dylibInstallName); - } - - BOMCopier copier = BOMCopierNewWithSys(BomSys_default()); - BOMCopierSetUserData(copier, (void*)&pathsNotInRoots); - BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths); - std::string dylibCacheRootDir = realFilePath(options.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(), options.dstRoot.c_str()); - BOMCopierFree(copier); - - for (std::pair dylibMissingFromNewCache : dylibsMissingFromNewCache) { - if (dylibMissingFromNewCache.second.empty()) - diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.first.c_str()); - else - diags.verbose("Dylib missing from new cache: '%s' because '%s'\n", - dylibMissingFromNewCache.first.c_str(), dylibMissingFromNewCache.second.c_str()); - } - } - } - - if (!options.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* dylibsFromRoots = [NSMutableArray array]; - for (auto& root : options.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)]; + 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; } } + } - // Work out the set of dylibs in the new cache but not in the baseline cache. - NSMutableArray* dylibsMissingFromBaselineCache = [NSMutableArray array]; - for (const std::string& newDylib : newDylibs) { - if (!baselineDylibs.count(newDylib)) - [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)]; - } - - // If a dylib which was cached is no longer eligible, say why - NSMutableArray* dylibsReasonsMissingFromNewCache = [NSMutableArray array]; - for (std::pair dylibMissingFromNewCache : dylibsMissingFromNewCache) { - NSMutableDictionary* reasonDict = [[NSMutableDictionary alloc] init]; - reasonDict[@"path"] = cppToObjStr(dylibMissingFromNewCache.first); - reasonDict[@"reason"] = cppToObjStr(dylibMissingFromNewCache.second); - [dylibsReasonsMissingFromNewCache addObject:reasonDict]; - } - - NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init]; - cacheDict[@"root-paths-in-cache"] = dylibsFromRoots; - cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache; - cacheDict[@"baseline-paths-evicted-from-cache"] = dylibsReasonsMissingFromNewCache; - - NSError* error = nil; - NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict - format:NSPropertyListBinaryFormat_v1_0 - options:0 - error:&error]; - (void)[outData writeToFile:cppToObjStr(options.baselineDifferenceResultPath) atomically:YES]; + // Give up if we couldn't write the cache maps + if (!cacheBuildSuccess) { + return false; } } - if (options.copyRoots) { - std::set 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) { - cachePaths.insert(manifest.installNameForUUID(dylib.first)); - } - } - } - }); - - BOMCopier copier = BOMCopierNewWithSys(BomSys_default()); - BOMCopierSetUserData(copier, (void*)&cachePaths); - BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths); - for (auto& root : options.roots) { - BOMCopierCopy(copier, root.c_str(), options.dstRoot.c_str()); - } - BOMCopierFree(copier); - } - - int err = sync_volume_np(options.dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT); - if (err) { - fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err)); - } - - // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after - // everything is written. - - if (!options.resultPath.empty()) { - manifest.write(options.resultPath); - } + return true; } static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options, @@ -927,50 +580,90 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil diags.error("Build options archs node is not an array\n"); return; } + std::set jsonArchs; const char* archs[archsNode.array.size()]; - uint64_t archIndex = 0; + uint64_t numArchs = 0; for (const dyld3::json::Node& archNode : archsNode.array) { - archs[archIndex++] = dyld3::json::parseRequiredString(diags, archNode).c_str(); + const char* archName = dyld3::json::parseRequiredString(diags, archNode).c_str(); + jsonArchs.insert(archName); + if ( options.cmdLineArchs.empty() || options.cmdLineArchs.count(archName) ) { + archs[numArchs++] = archName; + } + } + + // Check that the command line archs are in the JSON list + if ( !options.cmdLineArchs.empty() ) { + for (const std::string& cmdLineArch : options.cmdLineArchs) { + if ( !jsonArchs.count(cmdLineArch) ) { + std::string validArchs = ""; + for (const std::string& jsonArch : jsonArchs) { + if ( !validArchs.empty() ) { + validArchs += ", "; + } + validArchs += jsonArch; + } + diags.error("Command line -arch '%s' is not valid for this device. Valid archs are (%s)\n", cmdLineArch.c_str(), validArchs.c_str()); + return; + } + } } // Parse the rest of the options node. - BuildOptions_v1 buildOptions; + BuildOptions_v2 buildOptions; buildOptions.version = dyld3::json::parseRequiredInt(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "version")); buildOptions.updateName = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "updateName")).c_str(); buildOptions.deviceName = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "deviceName")).c_str(); buildOptions.disposition = stringToDisposition(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "disposition"))); buildOptions.platform = stringToPlatform(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "platform"))); buildOptions.archs = archs; - buildOptions.numArchs = archsNode.array.size(); + buildOptions.numArchs = numArchs; buildOptions.verboseDiagnostics = options.debug; buildOptions.isLocallyBuiltCache = true; - if (diags.hasError()) - return; - - // Override the disposition if we don't want certaion caches. - if (!options.emitDevCaches) { - switch (buildOptions.disposition) { - case Unknown: - // Nothing we can do here as we can't assume what caches are built here. - break; - case InternalDevelopment: - // This builds both caches, but we don't want dev - buildOptions.disposition = Customer; - break; - case Customer: - // This is already only the customer cache - break; - case InternalMinDevelopment: - diags.error("Cannot request no dev cache for InternalMinDevelopment as that is already only a dev cache\n"); - break; - } + // optimizeForSize was added in version 2 + buildOptions.optimizeForSize = false; + if ( buildOptions.version >= 2 ) { + buildOptions.optimizeForSize = dyld3::json::parseRequiredBool(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "optimizeForSize")); } if (diags.hasError()) return; - struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions); + // Override the disposition if we don't want certaion caches. + switch (buildOptions.disposition) { + case Unknown: + // Nothing we can do here as we can't assume what caches are built here. + break; + case InternalDevelopment: + if (!options.emitDevCaches && !options.emitCustomerCaches) { + diags.error("both -no_customer_cache and -no_development_cache passed\n"); + break; + } + if (!options.emitDevCaches) { + // This builds both caches, but we don't want dev + buildOptions.disposition = Customer; + } + if (!options.emitCustomerCaches) { + // This builds both caches, but we don't want customer + buildOptions.disposition = InternalMinDevelopment; + } + break; + case Customer: + if (!options.emitCustomerCaches) { + diags.error("Cannot request no customer cache for Customer as that is already only a customer cache\n"); + } + break; + case InternalMinDevelopment: + if (!options.emitDevCaches) { + diags.error("Cannot request no dev cache for InternalMinDevelopment as that is already only a dev cache\n"); + } + break; + } + + if (diags.hasError()) + return; + + struct MRMSharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder((const BuildOptions_v1*)&buildOptions); // Parse the files if (filesNode.array.empty()) { @@ -979,8 +672,9 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil } std::vector> inputFiles; + std::set dylibsFoundInRoots; for (const dyld3::json::Node& fileNode : filesNode.array) { - const std::string& path = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "path")).c_str(); + std::string path = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "path")).c_str(); FileFlags fileFlags = stringToFileFlags(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "flags"))); // We can optionally have a sourcePath entry which is the path to get the source content from instead of the install path @@ -1000,6 +694,8 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil sourcePath = path; } + std::string buildPath = sourcePath; + // Check if one of the -root's has this path bool foundInOverlay = false; for (const std::string& overlay : options.roots) { @@ -1007,8 +703,9 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil std::string filePath = overlay + path; if (!stat(filePath.c_str(), &sb)) { foundInOverlay = true; - diags.verbose("Taking '%s' from overlay instead of dylib cache\n", path.c_str()); + diags.verbose("Taking '%s' from overlay '%s' instead of dylib cache\n", path.c_str(), overlay.c_str()); inputFiles.push_back({ filePath, path, fileFlags }); + dylibsFoundInRoots.insert(path); break; } } @@ -1017,17 +714,15 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil continue; // Build paths are relative to the build artifact root directory. - std::string buildPath; switch (fileFlags) { case NoFlags: case MustBeInCache: case ShouldBeExcludedFromCacheIfUnusedLeaf: case RequiredClosure: - buildPath = "." + sourcePath; - break; case DylibOrderFile: case DirtyDataOrderFile: - buildPath = "." + sourcePath; + case ObjCOptimizationsFile: + buildPath = "." + buildPath; break; } inputFiles.push_back({ buildPath, path, fileFlags }); @@ -1036,10 +731,10 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil if (diags.hasError()) return; - // Parse the baseline from the map if we have it - std::set baselineDylibs; - if ( !options.baselineCacheMapPath.empty() ) { - dyld3::json::Node mapNode = dyld3::json::readJSON(diags, options.baselineCacheMapPath.c_str()); + // Parse the baseline from the map(s) if we have it + std::set unionBaselineDylibs; + for (const std::string& baselineCacheMapPath : options.baselineCacheMapPaths) { + dyld3::json::Node mapNode = dyld3::json::readJSON(diags, baselineCacheMapPath.c_str()); if (diags.hasError()) return; @@ -1077,12 +772,12 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil diags.error("Image path node is not a string\n"); return; } - baselineDylibs.insert(pathNode.value); + unionBaselineDylibs.insert(pathNode.value); } } std::vector> mappedFiles; - loadMRMFiles(diags, sharedCacheBuilder, inputFiles, mappedFiles, baselineDylibs); + loadMRMFiles(diags, sharedCacheBuilder, inputFiles, mappedFiles, unionBaselineDylibs); if (diags.hasError()) return; @@ -1094,7 +789,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil return; } for (const dyld3::json::Node& symlinkNode : symlinksNode->array) { - const std::string& fromPath = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "path")).c_str(); + std::string fromPath = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "path")).c_str(); const std::string& toPath = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "target")).c_str(); addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str()); } @@ -1104,34 +799,48 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil return; // Don't create a directory if we are skipping writes, which means we have no dstRoot set - if (!options.dstRoot.empty()) - (void)mkpath_np((options.dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755); + if (!options.dstRoot.empty()) { + if ( buildOptions.platform == macOS ) { + (void)mkpath_np((options.dstRoot + MACOSX_MRM_DYLD_SHARED_CACHE_DIR).c_str(), 0755); + } else { + (void)mkpath_np((options.dstRoot + IPHONE_DYLD_SHARED_CACHE_DIR).c_str(), 0755); + } + } // Actually build the cache. bool cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder); // Compare this cache to the baseline cache and see if we have any roots to copy over if (!options.baselineDifferenceResultPath.empty() || options.baselineCopyRoots) { - std::set newDylibs; + std::set dylibsInNewCaches; + std::set simulatorSupportDylibs; if (cacheBuildSuccess) { uint64_t fileResultCount = 0; if (const char* const* fileResults = getFilesToRemove(sharedCacheBuilder, &fileResultCount)) { for (uint64_t i = 0; i != fileResultCount; ++i) - newDylibs.insert(fileResults[i]); + dylibsInNewCaches.insert(fileResults[i]); + } + if ( buildOptions.platform == Platform::macOS ) { + // macOS has to leave the simulator support binaries on disk + // It won't put them in the result of getFilesToRemove() so we need to manually add them + simulatorSupportDylibs.insert("/usr/lib/system/libsystem_kernel.dylib"); + simulatorSupportDylibs.insert("/usr/lib/system/libsystem_platform.dylib"); + simulatorSupportDylibs.insert("/usr/lib/system/libsystem_pthread.dylib"); } } if (options.baselineCopyRoots) { - // Work out the set of dylibs in the old cache but not the new one - std::set dylibsMissingFromNewCache; - for (const std::string& baselineDylib : baselineDylibs) { - if (!newDylibs.count(baselineDylib)) - dylibsMissingFromNewCache.insert(baselineDylib); + // Work out the set of dylibs in the old caches but not the new ones + std::set dylibsMissingFromNewCaches; + for (const std::string& baselineDylib : unionBaselineDylibs) { + if ( !dylibsInNewCaches.count(baselineDylib) && !simulatorSupportDylibs.count(baselineDylib)) + dylibsMissingFromNewCaches.insert(baselineDylib); } - if (!dylibsMissingFromNewCache.empty()) { + if (!dylibsMissingFromNewCaches.empty()) { BOMCopier copier = BOMCopierNewWithSys(BomSys_default()); - BOMCopierSetUserData(copier, (void*)&dylibsMissingFromNewCache); + FilteredCopyOptions userData = { &diags, &dylibsMissingFromNewCaches, &dylibsFoundInRoots }; + BOMCopierSetUserData(copier, (void*)&userData); BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths); std::string dylibCacheRootDir = realFilePath(options.dylibCacheDir); if (dylibCacheRootDir == "") { @@ -1141,7 +850,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil BOMCopierCopy(copier, dylibCacheRootDir.c_str(), options.dstRoot.c_str()); BOMCopierFree(copier); - for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCache) { + for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCaches) { diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.c_str()); } } @@ -1155,7 +864,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil // Work out the set of dylibs in the cache and taken from the -root NSMutableArray* dylibsFromRoots = [NSMutableArray array]; for (auto& root : options.roots) { - for (const std::string& dylibInstallName : newDylibs) { + for (const std::string& dylibInstallName : dylibsInNewCaches) { struct stat sb; std::string filePath = root + "/" + dylibInstallName; if (!stat(filePath.c_str(), &sb)) { @@ -1166,8 +875,8 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil // Work out the set of dylibs in the new cache but not in the baseline cache. NSMutableArray* dylibsMissingFromBaselineCache = [NSMutableArray array]; - for (const std::string& newDylib : newDylibs) { - if (!baselineDylibs.count(newDylib)) + for (const std::string& newDylib : dylibsInNewCaches) { + if (!unionBaselineDylibs.count(newDylib)) [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)]; } @@ -1184,11 +893,15 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil } } - writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options); + bool wroteCaches = writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options); destroySharedCacheBuilder(sharedCacheBuilder); unloadMRMFiles(mappedFiles); + + if (!wroteCaches) { + exit(-1); + } } int main(int argc, const char* argv[]) @@ -1215,19 +928,26 @@ int main(int argc, const char* argv[]) fprintf(stderr, "-root path doesn't exist: %s\n", argv[i]); exit(-1); } - options.roots.insert(realpath); + if ( std::find(options.roots.begin(), options.roots.end(), realpath) == options.roots.end() ) { + // Push roots on to the front so that each -root overrides previous entries + options.roots.push_front(realpath); + } } else if (strcmp(arg, "-copy_roots") == 0) { options.copyRoots = true; } else if (strcmp(arg, "-dylib_cache") == 0) { options.dylibCacheDir = realPath(argv[++i]); } else if (strcmp(arg, "-artifact") == 0) { options.artifactDir = realPath(argv[++i]); - } else if (strcmp(arg, "-no_development_cache") == 0) { - options.emitDevCaches = false; } else if (strcmp(arg, "-no_overflow_dylibs") == 0) { options.emitElidedDylibs = false; + } else if (strcmp(arg, "-no_development_cache") == 0) { + options.emitDevCaches = false; } else if (strcmp(arg, "-development_cache") == 0) { options.emitDevCaches = true; + } else if (strcmp(arg, "-no_customer_cache") == 0) { + options.emitCustomerCaches = false; + } else if (strcmp(arg, "-customer_cache") == 0) { + options.emitCustomerCaches = true; } else if (strcmp(arg, "-overflow_dylibs") == 0) { options.emitElidedDylibs = true; } else if (strcmp(arg, "-mrm") == 0) { @@ -1249,18 +969,25 @@ int main(int argc, const char* argv[]) } else if (strcmp(arg, "-baseline_copy_roots") == 0) { options.baselineCopyRoots = true; } else if (strcmp(arg, "-baseline_cache_map") == 0) { - options.baselineCacheMapPath = realPath(argv[++i]); + std::string path = realPath(argv[++i]); + if ( !path.empty() ) + options.baselineCacheMapPaths.push_back(path); + } else if (strcmp(arg, "-arch") == 0) { + if ( ++i < argc ) { + options.cmdLineArchs.insert(argv[i]); + } + else { + fprintf(stderr, "-arch missing architecture name"); + return 1; + } } else { //usage(); fprintf(stderr, "unknown option: %s\n", arg); exit(-1); } } else { - if (!options.configuration.empty()) { - fprintf(stderr, "You may only specify one configuration\n"); - exit(-1); - } - options.configuration = argv[i]; + fprintf(stderr, "unknown option: %s\n", arg); + exit(-1); } } (void)options.emitElidedDylibs; // not implemented yet @@ -1283,8 +1010,8 @@ int main(int argc, const char* argv[]) exit(-1); } - if (options.configuration.empty() && jsonManifestPath.empty() && options.buildAllPath.empty()) { - fprintf(stderr, "Must specify a configuration OR a -json_manifest path OR a -build_all path\n"); + if (jsonManifestPath.empty() && options.buildAllPath.empty()) { + fprintf(stderr, "Must specify a -json_manifest path OR a -build_all path\n"); exit(-1); } @@ -1293,10 +1020,6 @@ int main(int argc, const char* argv[]) fprintf(stderr, "Cannot combine -dst_root and -build_all\n"); exit(-1); } - if (!options.configuration.empty()) { - fprintf(stderr, "Cannot combine configuration and -build_all\n"); - exit(-1); - } if (!jsonManifestPath.empty()) { fprintf(stderr, "Cannot combine -json_manifest and -build_all\n"); exit(-1); @@ -1309,7 +1032,7 @@ int main(int argc, const char* argv[]) fprintf(stderr, "Cannot combine -baseline_copy_roots and -build_all\n"); exit(-1); } - if (!options.baselineCacheMapPath.empty()) { + if (!options.baselineCacheMapPaths.empty()) { fprintf(stderr, "Cannot combine -baseline_cache_map and -build_all\n"); exit(-1); } @@ -1319,8 +1042,8 @@ int main(int argc, const char* argv[]) exit(-1); } - if (options.configuration.empty() && jsonManifestPath.empty()) { - fprintf(stderr, "Must specify a configuration OR -json_manifest path OR -list_configs\n"); + if (jsonManifestPath.empty()) { + fprintf(stderr, "Must specify a -json_manifest path OR -list_configs\n"); exit(-1); } } @@ -1336,22 +1059,22 @@ int main(int argc, const char* argv[]) fprintf(stderr, "Cannot use -results with -json_manifest\n"); exit(-1); } - if (!options.baselineDifferenceResultPath.empty() && options.baselineCacheMapPath.empty()) { + if (!options.baselineDifferenceResultPath.empty() && options.baselineCacheMapPaths.empty()) { fprintf(stderr, "Must use -baseline_cache_map with -baseline_diff_results when using -json_manifest\n"); exit(-1); } - if (options.baselineCopyRoots && options.baselineCacheMapPath.empty()) { + if (options.baselineCopyRoots && options.baselineCacheMapPaths.empty()) { fprintf(stderr, "Must use -baseline_cache_map with -baseline_copy_roots when using -json_manifest\n"); exit(-1); } } else { - if (!options.baselineCacheMapPath.empty()) { + if (!options.baselineCacheMapPaths.empty()) { fprintf(stderr, "Cannot use -baseline_cache_map without -json_manifest\n"); exit(-1); } } - if (!options.baselineCacheMapPath.empty()) { + if (!options.baselineCacheMapPaths.empty()) { if (options.baselineDifferenceResultPath.empty() && options.baselineCopyRoots) { fprintf(stderr, "Must use -baseline_cache_map with -baseline_diff_results or -baseline_copy_roots\n"); exit(-1); @@ -1465,10 +1188,8 @@ int main(int argc, const char* argv[]) if (requiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); } }); - } else if (!jsonManifestPath.empty()) { - buildCacheFromJSONManifest(diags, options, jsonManifestPath); } else { - buildCacheFromPListManifest(diags, options); + buildCacheFromJSONManifest(diags, options, jsonManifestPath); } const char* args[8]; diff --git a/src/dyld2.cpp.orig b/dyld3/shared-cache/dyld_shared_cache_builder.mm-strings similarity index 100% rename from src/dyld2.cpp.orig rename to dyld3/shared-cache/dyld_shared_cache_builder.mm-strings diff --git a/dyld3/shared-cache/dyld_shared_cache_util.cpp b/dyld3/shared-cache/dyld_shared_cache_util.cpp new file mode 100644 index 0000000..3a40178 --- /dev/null +++ b/dyld3/shared-cache/dyld_shared_cache_util.cpp @@ -0,0 +1,1793 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "ClosureBuilder.h" +#include "DyldSharedCache.h" +#include "ClosureFileSystemPhysical.h" +#include "JSONWriter.h" +#include "Trie.hpp" +#include "dsc_extractor.h" + +#include "objc-shared-cache.h" + +#if TARGET_OS_OSX +#define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle" +#else +#define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle" +#endif + +using dyld3::closure::ClosureBuilder; +using dyld3::closure::FileSystemPhysical; + +// 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) ) { + 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); + const dyld_cache_mapping_info* lastMapping = &mappings[header->mappingCount - 1]; + + size_t vmSize = (size_t)(lastMapping->address + lastMapping->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 < header->mappingCount; ++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); + + return (DyldSharedCache*)result; +} + +enum Mode { + modeNone, + modeList, + modeMap, + modeDependencies, + modeSlideInfo, + modeVerboseSlideInfo, + modeTextInfo, + modeLinkEdit, + modeLocalSymbols, + modeJSONMap, + modeJSONDependents, + modeSectionSizes, + modeStrings, + modeInfo, + modeSize, + modeObjCProtocols, + modeObjCImpCaches, + modeObjCClasses, + modeObjCSelectors, + modeExtract, + modePatchTable, + modeListDylibsWithSection +}; + +struct Options { + Mode mode; + const char* dependentsOfPath; + const char* extractionDir; + const char* segmentName; + const char* sectionName; + bool printUUIDs; + bool printVMAddrs; + bool printDylibVersions; + bool printInodes; +}; + + +void usage() { + fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract [ shared-cache-file ] \n"); +} + +static void checkMode(Mode mode) { + if ( mode != modeNone ) { + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n"); + usage(); + exit(1); + } +} + +struct SegmentInfo +{ + uint64_t vmAddr; + uint64_t vmSize; + const char* installName; + const char* segName; +}; + +static void buildSegmentInfo(const DyldSharedCache* dyldCache, std::vector& segInfos) +{ + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + segInfos.push_back({info.vmAddr, info.vmSize, installName, info.segName}); + }); + }); + + std::sort(segInfos.begin(), segInfos.end(), [](const SegmentInfo& l, const SegmentInfo& r) -> bool { + return l.vmAddr < r.vmAddr; + }); +} + +static void printSlideInfoForDataRegion(const DyldSharedCache* dyldCache, uint64_t dataStartAddress, uint64_t dataSize, + const uint8_t* dataPagesStart, + const dyld_cache_slide_info* slideInfoHeader, bool verboseSlideInfo) { + + 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 dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset); + const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset); + for(int i=0; i < slideInfoHeader->toc_count; ++i) { + printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]); + const dyld_cache_slide_info_entry* entry = &entries[tocs[i]]; + for(int j=0; j < slideInfoHeader->entries_size; ++j) + printf("%02X", entry->bits[j]); + printf("\n"); + } + } + else if ( slideInfoHeader->version == 2 ) { + const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(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 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 = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value); + pageOffset += delta; + } + }; + if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + printf("page[% 5d]: no rebasing\n", i); + } + else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + printf("page[% 5d]: ", i); + int j=(start & 0x3FFF); + bool done = false; + do { + uint16_t aStart = extras[j]; + printf("start=0x%04X ", aStart & 0x3FFF); + if ( verboseSlideInfo ) { + 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 ); + printf("\n"); + } + else { + printf("page[% 5d]: start=0x%04X\n", i, starts[i]); + if ( verboseSlideInfo ) { + 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 dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(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 uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add); + for (int i=0; i < slideInfo->page_starts_count; ++i) { + uint16_t delta = slideInfo->page_starts[i]; + if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) { + printf("page[% 5d]: no rebasing\n", i); + continue; + } + + printf("page[% 5d]: start=0x%04X\n", i, delta); + if ( !verboseSlideInfo ) + continue; + + delta = delta/sizeof(uint64_t); // initial offset is byte based + const uint8_t* pageStart = dataPagesStart + (i * slideInfo->page_size); + const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart; + do { + loc += delta; + delta = loc->plain.offsetToNextPointer; + dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr; + ptr.raw64 = *((uint64_t*)loc); + if ( loc->auth.authenticated ) { + uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase; + uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target); + printf(" [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n", + i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue, + ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false", + ptr.arm64e.keyName()); + } + else { + uint64_t targetValue = ptr.arm64e.unpackTarget(); + printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - 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; + } + }; + 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 ( verboseSlideInfo ) { + 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 ( verboseSlideInfo ) { + uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i)); + uint16_t pageStartOffset = start*4; + rebaseChainV4(page, pageStartOffset); + } + } + } + } +} + + +static void findImageAndSegment(const DyldSharedCache* dyldCache, const std::vector& segInfos, uint32_t cacheOffset, SegmentInfo* found) +{ + const uint64_t locVmAddr = dyldCache->unslidLoadAddress() + cacheOffset; + const SegmentInfo target = { locVmAddr, 0, NULL, NULL }; + const auto lowIt = std::lower_bound(segInfos.begin(), segInfos.end(), target, + [](const SegmentInfo& l, const SegmentInfo& r) -> bool { + return l.vmAddr+l.vmSize < r.vmAddr+r.vmSize; + }); + *found = *lowIt; +} + + +int main (int argc, const char* argv[]) { + + const char* sharedCachePath = nullptr; + + Options options; + options.mode = modeNone; + options.printUUIDs = false; + options.printVMAddrs = false; + options.printDylibVersions = false; + options.printInodes = false; + 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] == '-') { + if (strcmp(opt, "-list") == 0) { + checkMode(options.mode); + options.mode = modeList; + } + else if (strcmp(opt, "-dependents") == 0) { + checkMode(options.mode); + options.mode = modeDependencies; + options.dependentsOfPath = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -depdendents requires an argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-linkedit") == 0) { + checkMode(options.mode); + options.mode = modeLinkEdit; + } + else if (strcmp(opt, "-info") == 0) { + checkMode(options.mode); + options.mode = modeInfo; + } + else if (strcmp(opt, "-slide_info") == 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, "-text_info") == 0) { + checkMode(options.mode); + options.mode = modeTextInfo; + } + else if (strcmp(opt, "-local_symbols") == 0) { + checkMode(options.mode); + options.mode = modeLocalSymbols; + } + else if (strcmp(opt, "-strings") == 0) { + if (options.mode != modeStrings) + checkMode(options.mode); + options.mode = modeStrings; + printStrings = true; + } + else if (strcmp(opt, "-sections") == 0) { + checkMode(options.mode); + options.mode = modeSectionSizes; + } + 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; + } + else if (strcmp(opt, "-json-map") == 0) { + checkMode(options.mode); + options.mode = modeJSONMap; + } + else if (strcmp(opt, "-json-dependents") == 0) { + checkMode(options.mode); + options.mode = modeJSONDependents; + } + else if (strcmp(opt, "-size") == 0) { + checkMode(options.mode); + options.mode = modeSize; + } + else if (strcmp(opt, "-objc-protocols") == 0) { + checkMode(options.mode); + options.mode = modeObjCProtocols; + } + else if (strcmp(opt, "-objc-imp-caches") == 0) { + checkMode(options.mode); + options.mode = modeObjCImpCaches; + } + else if (strcmp(opt, "-objc-classes") == 0) { + checkMode(options.mode); + options.mode = modeObjCClasses; + } + else if (strcmp(opt, "-objc-selectors") == 0) { + checkMode(options.mode); + options.mode = modeObjCSelectors; + } + else if (strcmp(opt, "-extract") == 0) { + checkMode(options.mode); + options.mode = modeExtract; + options.extractionDir = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -extract requires a directory argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-uuid") == 0) { + options.printUUIDs = true; + } + else if (strcmp(opt, "-inode") == 0) { + options.printInodes = true; + } + else if (strcmp(opt, "-versions") == 0) { + options.printDylibVersions = true; + } + else if (strcmp(opt, "-vmaddr") == 0) { + options.printVMAddrs = true; + } + else if (strcmp(opt, "-patch_table") == 0) { + options.mode = modePatchTable; + } + else if (strcmp(opt, "-list_dylibs_with_section") == 0) { + options.mode = modeListDylibsWithSection; + options.segmentName = argv[++i]; + options.sectionName = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -list_dylibs_with_section requires a segment and section name\n"); + usage(); + exit(1); + } + } + else { + fprintf(stderr, "Error: unrecognized option %s\n", opt); + usage(); + exit(1); + } + } + else { + sharedCachePath = opt; + } + } + + if ( options.mode == modeNone ) { + fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); + usage(); + exit(1); + } + + if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) { + if ( options.printUUIDs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); + + if ( options.printVMAddrs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); + + if ( options.printDylibVersions && (options.mode != modeDependencies) ) + fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); + + if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { + fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); + usage(); + exit(1); + } + } + + const DyldSharedCache* dyldCache = nullptr; + if ( sharedCachePath != nullptr ) { + dyldCache = mapCacheFile(sharedCachePath); + // mapCacheFile prints an error if something goes wrong, so just return in that case. + if ( dyldCache == nullptr ) + return 1; + } + else { + size_t cacheLength; + dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength); + if (dyldCache == nullptr) { + fprintf(stderr, "Could not get in-memory shared cache\n"); + return 1; + } + if ( options.mode == modeObjCClasses ) { + fprintf(stderr, "Cannot use -objc-classes with a live cache. Please run with a path to an on-disk cache file\n"); + return 1; + } + } + + if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) { + if ( !dyldCache->hasSlideInfo() ) { + fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); + exit(1); + } + + const bool verboseSlideInfo = (options.mode == modeVerboseSlideInfo); + dyldCache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, + uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) { + printSlideInfoForDataRegion(dyldCache, mappingStartAddress, mappingSize, mappingPagesStart, + slideInfoHeader, verboseSlideInfo); + }); + return 0; + } + else if ( options.mode == modeInfo ) { + const dyld_cache_header* header = &dyldCache->header; + uuid_string_t uuidString; + uuid_unparse_upper(header->uuid, uuidString); + printf("uuid: %s\n", uuidString); + + dyld3::Platform platform = dyldCache->platform(); + printf("platform: %s\n", dyld3::MachOFile::platformName(platform)); + printf("built by: %s\n", header->locallyBuiltCache ? "local machine" : "B&I"); + printf("cache type: %s\n", header->cacheType ? "production" : "development"); + printf("image count: %u\n", header->imagesCount); + if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) { + printf("branch pool count: %u\n", header->branchPoolsCount); + } + if ( dyldCache->hasSlideInfo() ) { + uint32_t pageSize = 0x4000; // fix me for intel + uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize); + uint32_t entropyBits = 0; + if ( possibleSlideValues > 1 ) + entropyBits = __builtin_clz(possibleSlideValues - 1); + printf("ASLR entropy: %u-bits\n", entropyBits); + } + printf("mappings:\n"); + dyldCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, + uint32_t initProt, uint32_t maxProt, uint64_t flags) { + std::string mappingName = ""; + if ( maxProt & VM_PROT_EXECUTE ) { + mappingName = "__TEXT"; + } else if ( maxProt & VM_PROT_WRITE ) { + // Start off with __DATA or __AUTH + if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA ) + mappingName = "__AUTH"; + else + mappingName = "__DATA"; + // Then add one of "", _DIRTY, or _CONST + if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA ) + mappingName += "_DIRTY"; + else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA ) + mappingName += "_CONST"; + } + else if ( maxProt & VM_PROT_READ ) { + mappingName = "__LINKEDIT"; + } else { + mappingName = "*unknown*"; + } + uint64_t fileOffset = (uint8_t*)content - (uint8_t*)dyldCache; + printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappingName.c_str(), size / (1024*1024), fileOffset, fileOffset + size, vmAddr, vmAddr + size); + }); + if ( header->codeSignatureOffset != 0 ) { + uint64_t size = header->codeSignatureSize; + uint64_t csAddr = dyldCache->getCodeSignAddress(); + if ( size != 0 ) + printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + "code sign", size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size); + } + dyldCache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, + uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) { + + printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n", + slideInfoSize/1024, slideInfoOffset, slideInfoOffset + slideInfoSize); + }); + if ( header->localSymbolsOffset != 0 ) + printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n", + header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize); + } + else if ( options.mode == modeTextInfo ) { + const dyld_cache_header* header = &dyldCache->header; + printf("dylib text infos (count=%llu):\n", header->imagesTextCount); + dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) { + uuid_string_t uuidString; + uuid_unparse_upper(dylibUUID, uuidString); + printf(" 0x%09llX -> 0x%09llX <%s> %s\n", loadAddressUnslid, loadAddressUnslid + textSegmentSize, uuidString, installName); + }); + } + else if ( options.mode == modeLocalSymbols ) { + if ( !dyldCache->hasLocalSymbolsInfo() ) { + fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n"); + exit(1); + } + const bool is64 = (strstr(dyldCache->archName(), "64") != NULL); + const uint32_t nlistFileOffset = (uint32_t)((uint8_t*)dyldCache->getLocalNlistEntries() - (uint8_t*)dyldCache); + const uint32_t nlistCount = dyldCache->getLocalNlistCount(); + const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12; + const char* localStrings = dyldCache->getLocalStrings(); + const uint32_t stringsFileOffset = (uint32_t)((uint8_t*)localStrings - (uint8_t*)dyldCache); + const uint32_t stringsSize = dyldCache->getLocalStringsSize(); + + printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize); + printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize); + + __block uint32_t entriesCount = 0; + dyldCache->forEachLocalSymbolEntry(^(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool &stop) { + const char* imageName = dyldCache->getIndexedImagePath(entriesCount); + printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", nlistStartIndex, nlistCount, imageName); +#if 0 + if ( is64 ) { + const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset); + for (int e = 0; e < nlistCount; ++e) { + const nlist_64* entry = &symTab[nlistStartIndex + e]; + printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &localStrings[entry->n_un.n_strx]); + printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value); + } + } +#endif + entriesCount++; + }); + printf("local symbols by dylib (count=%d):\n", entriesCount); + } + else if ( options.mode == modeJSONMap ) { + std::string buffer = dyldCache->generateJSONMap("unknown"); + printf("%s\n", buffer.c_str()); + } + else if ( options.mode == modeJSONDependents ) { + std::cout << dyldCache->generateJSONDependents(); + } + 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 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 == modeSectionSizes ) { + __block std::map sectionSizes; + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { + std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName; + sectionSizes[section] += sectInfo.sectSize; + }); + }); + for (const auto& keyAndValue : sectionSizes) { + printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str()); + } + } + else if ( options.mode == modeObjCProtocols ) { + if ( dyldCache->objcOpt() == nullptr ) { + fprintf(stderr, "Error: could not get optimized objc\n"); + return 1; + } + objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2(); + if ( protocols == nullptr ) { + fprintf(stderr, "Error: could not get optimized objc protocols\n"); + return 1; + } + + for (uint64_t index = 0; index != protocols->capacity; ++index) { + const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index]; + if ( clshi.clsOffset == 0 ) { + fprintf(stderr, "[% 5lld]\n", index); + continue; + } + const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]); + if ( !clshi.isDuplicate() ) { + fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name); + continue; + } + + // class appears in more than one header + uint32_t count = clshi.duplicateCount(); + fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n", + index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name); + + const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()]; + for (uint32_t i = 0; i < count; i++) { + fprintf(stderr, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset); + } + } + } + else if ( options.mode == modeObjCClasses ) { + using dyld3::json::Node; + using dyld3::json::NodeValueType; + using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo; + const bool rebased = false; + + std::string instancePrefix("-"); + std::string classPrefix("+"); + + auto getString = ^const char *(const dyld3::MachOAnalyzer* ma, uint64_t nameVMAddr){ + dyld3::MachOAnalyzer::PrintableStringResult result; + const char* name = ma->getPrintableString(nameVMAddr, result); + if (result == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) + return name; + return nullptr; + }; + + // Build a map of class vm addrs to their names so that categories know the + // name of the class they are attaching to + __block std::unordered_map classVMAddrToName; + __block std::unordered_map metaclassVMAddrToName; + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + const uint32_t pointerSize = ma->pointerSize(); + + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) { + if (isMetaClass) + metaclassVMAddrToName[classVMAddr] = className; + else + classVMAddrToName[classVMAddr] = className; + } + }; + + Diagnostics diag; + + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased); + ma->forEachObjCClass(diag, vmAddrConverter, visitClass); + }); + + // These are used only for the on-disk binaries we analyze + __block std::vector onDiskChainedFixupBindTargets; + __block std::unordered_map onDiskClassVMAddrToName; + __block std::unordered_map onDiskMetaclassVMAddrToName; + + auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + __block Node propertiesNode; + auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) { + // Get the name && attributes + auto propertyName = getString(ma, property.nameVMAddr); + auto propertyAttributes = getString(ma, property.attributesVMAddr); + + if (!propertyName || !propertyAttributes) + return; + + Node propertyNode; + propertyNode.map["name"] = Node{propertyName}; + propertyNode.map["attributes"] = Node{propertyAttributes}; + propertiesNode.array.push_back(propertyNode); + }; + ma->forEachObjCProperty(propertiesVMAddr, vmAddrConverter, visitProperty); + return propertiesNode.array.empty() ? std::optional() : propertiesNode; + }; + + auto getClassProtocols = ^(const dyld3::MachOAnalyzer* ma, uint64_t protocolsVMAddr, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + __block Node protocolsNode; + + auto visitProtocol = ^(uint64_t protocolVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& protocol) { + if (const char *name = getString(ma, protocol.nameVMAddr)) { + protocolsNode.array.push_back(Node{name}); + } + }; + + ma->forEachObjCProtocol(protocolsVMAddr, vmAddrConverter, visitProtocol); + + return protocolsNode.array.empty() ? std::optional() : protocolsNode; + }; + + auto getProtocols = ^(const dyld3::MachOAnalyzer* ma, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + __block Node protocols; + + auto getMethods = ^(const dyld3::MachOAnalyzer* ma, uint64_t methodListVMAddr, const std::string &prefix, Node &node){ + auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + if (auto name = getString(ma, method.nameVMAddr)) { + node.array.push_back(Node{prefix + name}); + } + }; + + ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod); + }; + + auto visitProtocol = ^(Diagnostics& diag, uint64_t protoVMAddr, + const dyld3::MachOAnalyzer::ObjCProtocol& objcProto) { + const char* protoName = getString(ma, objcProto.nameVMAddr); + if (!protoName) + return; + + Node entry; + entry.map["protocolName"] = Node{protoName}; + + if ( objcProto.protocolsVMAddr != 0 ) { + __block Node protocols; + + auto visitProtocol = ^(uint64_t protocolRefVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol &protocol) { + if (auto name = getString(ma, protocol.nameVMAddr)) { + protocols.array.push_back(Node{name}); + } + }; + + ma->forEachObjCProtocol(objcProto.protocolsVMAddr, vmAddrConverter, visitProtocol); + if (!protocols.array.empty()) { + entry.map["protocols"] = protocols; + } + } + + Node methods; + getMethods(ma, objcProto.instanceMethodsVMAddr, instancePrefix, methods); + getMethods(ma, objcProto.classMethodsVMAddr, classPrefix, methods); + if (!methods.array.empty()) { + entry.map["methods"] = methods; + } + + Node optMethods; + getMethods(ma, objcProto.optionalInstanceMethodsVMAddr, instancePrefix, optMethods); + getMethods(ma, objcProto.optionalClassMethodsVMAddr, classPrefix, optMethods); + if (!optMethods.array.empty()) { + entry.map["optionalMethods"] = optMethods; + } + + protocols.array.push_back(entry); + }; + + Diagnostics diag; + ma->forEachObjCProtocol(diag, vmAddrConverter, visitProtocol); + + return protocols.array.empty() ? std::optional() : protocols; + }; + + auto getSelRefs = ^(const dyld3::MachOAnalyzer* ma, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + __block std::vector selNames; + + auto visitSelRef = ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) { + if (auto selValue = getString(ma, selRefTargetVMAddr)) { + selNames.push_back(selValue); + } + }; + + Diagnostics diag; + ma->forEachObjCSelectorReference(diag, vmAddrConverter, visitSelRef); + + std::sort(selNames.begin(), selNames.end(), + [](const char* a, const char* b) { + return strcasecmp(a, b) < 0; + }); + + Node selrefs; + for (auto s: selNames) { + selrefs.array.push_back(Node{s}); + } + + return selrefs.array.empty() ? std::optional() : selrefs; + }; + + auto getClasses = ^(const dyld3::MachOAnalyzer* ma, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + Diagnostics diag; + const uint32_t pointerSize = ma->pointerSize(); + + // Get the vmAddrs for all exported symbols as we want to know if classes + // are exported + std::set exportedSymbolVMAddrs; + { + uint64_t loadAddress = ma->preferredLoadAddress(); + + uint32_t exportTrieRuntimeOffset; + uint32_t exportTrieSize; + if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) { + const uint8_t* start = (uint8_t*)ma + exportTrieRuntimeOffset; + const uint8_t* end = start + exportTrieSize; + std::vector exports; + if ( ExportInfoTrie::parseTrie(start, end, exports) ) { + for (const ExportInfoTrie::Entry& entry: exports) { + exportedSymbolVMAddrs.insert(loadAddress + entry.info.address); + } + } + } + } + + __block Node classesNode; + __block bool skippedPreviousClass = false; + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if (isMetaClass) { + if (skippedPreviousClass) { + // If the class was bad, then skip the meta class too + skippedPreviousClass = false; + return; + } + } else { + skippedPreviousClass = true; + } + + std::string classType = "-"; + if (isMetaClass) + classType = "+"; + dyld3::MachOAnalyzer::PrintableStringResult classNameResult; + const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult); + if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) { + return; + } + + const char* superClassName = nullptr; + if ( ma->inDyldCache() ) { + if ( objcClass.superclassVMAddr != 0 ) { + if (isMetaClass) { + // If we are root class, then our superclass should actually point to our own class + const uint32_t RO_ROOT = (1<<1); + if ( objcClass.flags(pointerSize) & RO_ROOT ) { + auto it = classVMAddrToName.find(objcClass.superclassVMAddr); + assert(it != classVMAddrToName.end()); + superClassName = it->second; + } else { + auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr); + assert(it != metaclassVMAddrToName.end()); + superClassName = it->second; + } + } else { + auto it = classVMAddrToName.find(objcClass.superclassVMAddr); + assert(it != classVMAddrToName.end()); + superClassName = it->second; + } + } + } else { + // On-disk binary. Lets crack the chain to work out what we are pointing at + dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup; + fixup.raw64 = objcClass.superclassVMAddr; + if (fixup.arm64e.bind.bind) { + uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal; + // Bind to another image. Use the bind table to work out which name to bind to + const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal]; + if (isMetaClass) { + if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) { + superClassName = symbolName + strlen("_OBJC_METACLASS_$_"); + } else { + // Swift classes don't start with these prefixes so just skip them + if (objcClass.isSwiftLegacy || objcClass.isSwiftStable) + return; + } + } else { + if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) { + superClassName = symbolName + strlen("_OBJC_CLASS_$_"); + } else { + // Swift classes don't start with these prefixes so just skip them + if (objcClass.isSwiftLegacy || objcClass.isSwiftStable) + return; + } + } + } else { + // Rebase within this image. + if (isMetaClass) { + auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr); + assert(it != onDiskMetaclassVMAddrToName.end()); + superClassName = it->second; + } else { + auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr); + assert(it != onDiskClassVMAddrToName.end()); + superClassName = it->second; + } + } + } + + // Print the methods on this class + __block Node methodsNode; + auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + dyld3::MachOAnalyzer::PrintableStringResult methodNameResult; + const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult); + if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) + return; + methodsNode.array.push_back(Node{classType + methodName}); + }; + ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, + visitMethod); + + std::optional properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize), vmAddrConverter); + + if (isMetaClass) { + assert(!classesNode.array.empty()); + Node& currentClassNode = classesNode.array.back(); + assert(currentClassNode.map["className"].value == className); + if (!methodsNode.array.empty()) { + Node& currentMethodsNode = currentClassNode.map["methods"]; + currentMethodsNode.array.insert(currentMethodsNode.array.end(), + methodsNode.array.begin(), + methodsNode.array.end()); + } + if (properties.has_value()) { + Node& currentPropertiesNode = currentClassNode.map["properties"]; + currentPropertiesNode.array.insert(currentPropertiesNode.array.end(), + properties->array.begin(), + properties->array.end()); + } + return; + } + + Node currentClassNode; + currentClassNode.map["className"] = Node{className}; + if ( superClassName != nullptr ) + currentClassNode.map["superClassName"] = Node{superClassName}; + if (!methodsNode.array.empty()) + currentClassNode.map["methods"] = methodsNode; + if (properties.has_value()) + currentClassNode.map["properties"] = properties.value(); + if (std::optional protocols = getClassProtocols(ma, objcClass.baseProtocolsVMAddr(pointerSize), vmAddrConverter)) + currentClassNode.map["protocols"] = protocols.value(); + + currentClassNode.map["exported"] = Node{exportedSymbolVMAddrs.count(classVMAddr) != 0}; + + // We didn't skip this class so mark it as such + skippedPreviousClass = false; + + classesNode.array.push_back(currentClassNode); + }; + + ma->forEachObjCClass(diag, vmAddrConverter, visitClass); + return classesNode.array.empty() ? std::optional() : classesNode; + }; + + auto getCategories = ^(const dyld3::MachOAnalyzer* ma, + const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) { + Diagnostics diag; + + __block Node categoriesNode; + auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr, + const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult; + const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult); + if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) + return; + + const char* className = nullptr; + if ( ma->inDyldCache() ) { + auto it = classVMAddrToName.find(objcCategory.clsVMAddr); + assert(it != classVMAddrToName.end()); + className = it->second; + } else { + // On-disk binary. Lets crack the chain to work out what we are pointing at + dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup; + fixup.raw64 = objcCategory.clsVMAddr; + if (fixup.arm64e.bind.bind) { + uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal; + // Bind to another image. Use the bind table to work out which name to bind to + const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal]; + if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) { + className = symbolName + strlen("_OBJC_CLASS_$_"); + } else { + // Swift classes don't start with these prefixes so just skip them + // We don't know that this is a Swift class/category though, but skip it anyway + return; + } + } else { + auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr); + if (it == onDiskClassVMAddrToName.end()) { + // This is an odd binary with perhaps a Swift class. Just skip this entry + return; + } + className = it->second; + } + } + + // Print the instance methods on this category + __block Node methodsNode; + auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + if (auto methodName = getString(ma, method.nameVMAddr)) + methodsNode.array.push_back(Node{instancePrefix + methodName}); + }; + ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, + visitInstanceMethod); + + // Print the instance methods on this category + __block Node classMethodsNode; + auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + if (auto methodName = getString(ma, method.nameVMAddr)) + methodsNode.array.push_back(Node{classPrefix + methodName}); + }; + ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, + visitClassMethod); + + Node currentCategoryNode; + currentCategoryNode.map["categoryName"] = Node{categoryName}; + currentCategoryNode.map["className"] = Node{className}; + if (!methodsNode.array.empty()) + currentCategoryNode.map["methods"] = methodsNode; + if (std::optional properties = getProperties(ma, objcCategory.instancePropertiesVMAddr, vmAddrConverter)) + currentCategoryNode.map["properties"] = properties.value(); + if (std::optional protocols = getClassProtocols(ma, objcCategory.protocolsVMAddr, vmAddrConverter)) + currentCategoryNode.map["protocols"] = protocols.value(); + + categoriesNode.array.push_back(currentCategoryNode); + }; + + ma->forEachObjCCategory(diag, vmAddrConverter, visitCategory); + return categoriesNode.array.empty() ? std::optional() : categoriesNode; + }; + + __block bool needsComma = false; + + dyld3::json::streamArrayBegin(needsComma); + + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased); + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + + Node imageRecord; + imageRecord.map["imagePath"] = Node{installName}; + imageRecord.map["imageType"] = Node{"cache-dylib"}; + std::optional classes = getClasses(ma, vmAddrConverter); + std::optional categories = getCategories(ma, vmAddrConverter); + std::optional protocols = getProtocols(ma, vmAddrConverter); + std::optional selrefs = getSelRefs(ma, vmAddrConverter); + + // Skip emitting images with no objc data + if (!classes.has_value() && !categories.has_value() && !protocols.has_value() && !selrefs.has_value()) + return; + if (classes.has_value()) + imageRecord.map["classes"] = classes.value(); + if (categories.has_value()) + imageRecord.map["categories"] = categories.value(); + if (protocols.has_value()) + imageRecord.map["protocols"] = protocols.value(); + if (selrefs.has_value()) + imageRecord.map["selrefs"] = selrefs.value(); + + dyld3::json::streamArrayNode(needsComma, imageRecord); + }); + + FileSystemPhysical fileSystem; + dyld3::Platform platform = dyldCache->platform(); + const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true); + + dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) { + Diagnostics diag; + char realerPath[MAXPATHLEN]; + dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath); + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent; + uint32_t pointerSize = ma->pointerSize(); + + // Populate the bind targets for classes from other images + onDiskChainedFixupBindTargets.clear(); + ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { + onDiskChainedFixupBindTargets.push_back(symbolName); + }); + if ( diag.hasError() ) + return; + + // Populate the rebase targets for class names + onDiskMetaclassVMAddrToName.clear(); + onDiskClassVMAddrToName.clear(); + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) { + if (isMetaClass) + onDiskMetaclassVMAddrToName[classVMAddr] = className; + else + onDiskClassVMAddrToName[classVMAddr] = className; + } + }; + + // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one + dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased); + + ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass); + + Node imageRecord; + imageRecord.map["imagePath"] = Node{executableRuntimePath}; + imageRecord.map["imageType"] = Node{"executable"}; + std::optional classes = getClasses(ma, onDiskVMAddrConverter); + std::optional categories = getCategories(ma, onDiskVMAddrConverter); + // TODO: protocols + std::optional selrefs = getSelRefs(ma, onDiskVMAddrConverter); + + // Skip emitting images with no objc data + if (!classes.has_value() && !categories.has_value() && !selrefs.has_value()) + return; + if (classes.has_value()) + imageRecord.map["classes"] = classes.value(); + if (categories.has_value()) + imageRecord.map["categories"] = categories.value(); + if (selrefs.has_value()) + imageRecord.map["selrefs"] = selrefs.value(); + + dyld3::json::streamArrayNode(needsComma, imageRecord); + }); + + dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) { + Diagnostics diag; + char realerPath[MAXPATHLEN]; + dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath); + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent; + uint32_t pointerSize = ma->pointerSize(); + + // Populate the bind targets for classes from other images + onDiskChainedFixupBindTargets.clear(); + ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { + onDiskChainedFixupBindTargets.push_back(symbolName); + }); + if ( diag.hasError() ) + return; + + // Populate the rebase targets for class names + onDiskMetaclassVMAddrToName.clear(); + onDiskClassVMAddrToName.clear(); + auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) { + if (isMetaClass) + onDiskMetaclassVMAddrToName[classVMAddr] = className; + else + onDiskClassVMAddrToName[classVMAddr] = className; + } + }; + + // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one + dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased); + + ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass); + + Node imageRecord; + imageRecord.map["imagePath"] = Node{runtimePath}; + imageRecord.map["imageType"] = Node{"non-cache-dylib"}; + std::optional classes = getClasses(ma, onDiskVMAddrConverter); + std::optional categories = getCategories(ma, onDiskVMAddrConverter); + // TODO: protocols + std::optional selrefs = getSelRefs(ma, onDiskVMAddrConverter); + + // Skip emitting images with no objc data + if (!classes.has_value() && !categories.has_value() && !selrefs.has_value()) + return; + if (classes.has_value()) + imageRecord.map["classes"] = classes.value(); + if (categories.has_value()) + imageRecord.map["categories"] = categories.value(); + if (selrefs.has_value()) + imageRecord.map["selrefs"] = selrefs.value(); + + dyld3::json::streamArrayNode(needsComma, imageRecord); + }); + + dyld3::json::streamArrayEnd(needsComma); + } + else if ( options.mode == modeObjCSelectors ) { + if ( dyldCache->objcOpt() == nullptr ) { + fprintf(stderr, "Error: could not get optimized objc\n"); + return 1; + } + const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt(); + if ( selectors == nullptr ) { + fprintf(stderr, "Error: could not get optimized objc selectors\n"); + return 1; + } + + std::vector selNames; + for (uint64_t index = 0; index != selectors->capacity; ++index) { + objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index]; + if ( offset == 0 ) + continue; + const char* selName = selectors->getEntryForIndex((uint32_t)index); + selNames.push_back(selName); + } + + std::sort(selNames.begin(), selNames.end(), + [](const char* a, const char* b) { + // Sort by offset, not string value + return a < b; + }); + + dyld3::json::Node root; + for (const char* selName : selNames) { + dyld3::json::Node selNode; + selNode.map["selectorName"] = dyld3::json::Node{selName}; + selNode.map["offset"] = dyld3::json::Node{(int64_t)selName - (int64_t)dyldCache}; + + root.array.push_back(selNode); + } + + dyld3::json::printJSON(root, 0, std::cout); + } + else if ( options.mode == modeExtract ) { + return dyld_shared_cache_extract_dylibs(sharedCachePath, options.extractionDir); + } + else if ( options.mode == modeObjCImpCaches ) { + if (sharedCachePath == nullptr) { + fprintf(stderr, "Cannot emit imp caches with live cache. Run again with the path to the cache file\n"); + return 1; + } + __block std::map methodToClassMap; + __block std::map classVMAddrToNameMap; + const bool contentRebased = false; + const uint32_t pointerSize = 8; + + // Get the base pointers from the magic section in objc + __block uint64_t objcCacheOffsetsSize = 0; + __block const void* objcCacheOffsets = nullptr; + __block Diagnostics diag; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") ) { + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + objcCacheOffsets = ma->findSectionContent("__DATA_CONST", "__objc_scoffs", objcCacheOffsetsSize); + } + }); + + if ( objcCacheOffsets == nullptr ) { + fprintf(stderr, "Unable to print imp-caches as cannot find __DATA_CONST __objc_scoffs inside /usr/lib/libobjc.A.dylib\n"); + return 1; + } + + if ( objcCacheOffsetsSize < (4 * pointerSize) ) { + fprintf(stderr, "Unable to print imp-caches as __DATA_CONST __objc_scoffs is too small (%lld vs required %u)\n", objcCacheOffsetsSize, (4 * pointerSize)); + return 1; + } + + dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(contentRebased); + + uint64_t selectorStringVMAddrStart = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[0]); + uint64_t selectorStringVMAddrEnd = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[1]); + + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + if (diag.hasError()) + return; + + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + intptr_t slide = ma->getSlide(); + + ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, + uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, + uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, + bool isMetaClass) { + const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide; + classVMAddrToNameMap[classVMAddr] = className; + ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, + ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + // const char* methodName = (const char*)(method.nameVMAddr + slide); + methodToClassMap[method.impVMAddr] = className; + }); + }); + + ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + const char* catName = (const char*)objcCategory.nameVMAddr + slide; + // const char* methodName = (const char*)(method.nameVMAddr + slide); + methodToClassMap[method.impVMAddr] = catName; + }); + + ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + const char* catName = (const char*)objcCategory.nameVMAddr + slide; + // const char* methodName = (const char*)(method.nameVMAddr + slide); + methodToClassMap[method.impVMAddr] = catName; + }); + }); + }); + if (diag.hasError()) + return 1; + + dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { + if (diag.hasError()) + return; + + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; + intptr_t slide = ma->getSlide(); + + ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, + uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, + uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, + bool isMetaClass) { + const char* type = "class"; + if (isMetaClass) + type = "meta-class"; + const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide; + + if (objcClass.methodCacheVMAddr == 0) { + printf("%s (%s): empty\n", className, type); + return; + } + + struct Bucket { + uint32_t selOffset; + uint32_t impOffset; + }; + struct ImpCache { + int32_t fallback_class_offset; + uint32_t cache_shift : 5; + uint32_t cache_mask : 11; + uint32_t occupied : 14; + uint32_t has_inlines : 1; + uint32_t bit_one : 1; + struct Bucket buckets[]; + }; + + const ImpCache* impCache = (const ImpCache*)(objcClass.methodCacheVMAddr + slide); + printf("%s (%s): %d buckets\n", className, type, impCache->cache_mask + 1); + + if ((classVMAddr + impCache->fallback_class_offset) != objcClass.superclassVMAddr) { + printf("Flattening fallback: %s\n", classVMAddrToNameMap[classVMAddr + impCache->fallback_class_offset]); + } + // Buckets are a 32-bit offset from the impcache itself + for (uint32_t i = 0; i <= impCache->cache_mask ; ++i) { + const Bucket& b = impCache->buckets[i]; + uint64_t sel = (uint64_t)b.selOffset + selectorStringVMAddrStart; + uint64_t imp = classVMAddr - (uint64_t)b.impOffset; + if (b.selOffset == 0xFFFFFFFF) { + // Empty bucket + printf(" - 0x%016llx: %s\n", 0ULL, ""); + } else { + assert(sel < selectorStringVMAddrEnd); + + auto it = methodToClassMap.find(imp); + if (it == methodToClassMap.end()) { + fprintf(stderr, "Could not find IMP %llx (for %s)\n", imp, (const char*)(sel + slide)); + } + assert(it != methodToClassMap.end()); + printf(" - 0x%016llx: %s (from %s)\n", imp, (const char*)(sel + slide), it->second); + } + } + }); + }); + } else { + switch ( options.mode ) { + case modeList: { + // list all dylibs, including their aliases (symlinks to them) with option vmaddr + __block std::vector> indexToPaths; + __block std::vector indexToAddr; + __block std::vector indexToUUID; + dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) { + std::unordered_set empty; + if ( options.printVMAddrs ) + indexToAddr.push_back(loadAddressUnslid); + if ( options.printUUIDs ) { + uuid_string_t uuidString; + uuid_unparse_upper(dylibUUID, uuidString); + indexToUUID.push_back(uuidString); + } + indexToPaths.push_back(empty); + indexToPaths.back().insert(installName); + }); + dyldCache->forEachDylibPath(^(const char* dylibPath, uint32_t index) { + indexToPaths[index].insert(dylibPath); + }); + int index = 0; + for (const std::unordered_set& paths : indexToPaths) { + for (const std::string& path: paths) { + if ( options.printVMAddrs ) + printf("0x%08llX ", indexToAddr[index]); + if ( options.printUUIDs ) + printf("<%s> ", indexToUUID[index].c_str()); + printf("%s\n", path.c_str()); + } + ++index; + } + break; + } + case modeListDylibsWithSection: { + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOFile* mf = (dyld3::MachOFile*)mh; + mf->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( (strcmp(sectInfo.sectName, options.sectionName) == 0) && (strcmp(sectInfo.segInfo.segName, options.segmentName) == 0) ) { + printf("%s\n", installName); + stop = true; + } + }); + }); + break; + } + case modeMap: { + __block std::map dataSegNames; + __block std::map dataSegEnds; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOFile* mf = (dyld3::MachOFile*)mh; + mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName); + if ( strncmp(info.segName, "__DATA", 6) == 0 ) { + dataSegNames[info.vmAddr] = installName; + dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize; + } + }); + }); + // Enhance dyld_shared_cache_util to show where section alignment added padding + uint64_t lastEnd = 0; + for (const auto& entry : dataSegEnds) { + uint64_t padding = entry.first - lastEnd; + if ( (padding > 32) && (lastEnd != 0) ) { + printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024); + } + lastEnd = entry.second; + } + break; + } + case modeDependencies: { + __block bool dependentTargetFound = false; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + if ( strcmp(options.dependentsOfPath, installName) != 0 ) + return; + dependentTargetFound = true; + + auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) { + if ( options.printDylibVersions ) { + uint32_t compat_vers = compatVersion; + uint32_t current_vers = curVersion; + printf("\t%s", loadPath); + if ( compat_vers != 0xFFFFFFFF ) { + printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", + (compat_vers >> 16), + (compat_vers >> 8) & 0xff, + (compat_vers) & 0xff, + (current_vers >> 16), + (current_vers >> 8) & 0xff, + (current_vers) & 0xff); + } + else { + printf("\n"); + } + } + else { + printf("\t%s\n", loadPath); + } + }; + + dyld3::MachOFile* mf = (dyld3::MachOFile*)mh; + + // First print out our dylib and version. + const char* dylibInstallName; + uint32_t currentVersion; + uint32_t compatVersion; + if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, ¤tVersion) ) { + printDep(dylibInstallName, compatVersion, currentVersion); + } + + // Then the dependent dylibs. + mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + printDep(loadPath, compatVersion, curVersion); + }); + }); + if (options.dependentsOfPath && !dependentTargetFound) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); + exit(1); + } + break; + } + case modeLinkEdit: { + std::map pageToContent; + auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) { + for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { + std::map::iterator pos = pageToContent.find(p); + if ( pos == pageToContent.end() ) { + pageToContent[p] = strdup(message); + } + else { + const char* oldMessage = pos->second; + char* newMesssage; + asprintf(&newMesssage, "%s, %s", oldMessage, message); + pageToContent[p] = newMesssage; + ::free((void*)oldMessage); + } + } + }; + + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh; + Diagnostics diag; + dyld3::MachOAnalyzer::LinkEditInfo leInfo; + ma->getLinkEditPointers(diag, leInfo); + + if (diag.hasError()) + return; + + char message[1000]; + const char* shortName = strrchr(installName, '/') + 1; + // add export trie info + if ( leInfo.dyldInfo->export_size != 0 ) { + //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off()); + uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096); + uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096); + sprintf(message, "exports from %s", shortName); + add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message); + } + // add binding info + if ( leInfo.dyldInfo->bind_size != 0 ) { + uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096); + uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096); + sprintf(message, "bindings from %s", shortName); + add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message); + } + // add lazy binding info + if ( leInfo.dyldInfo->lazy_bind_size != 0 ) { + uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096); + uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096); + sprintf(message, "lazy bindings from %s", shortName); + add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message); + } + // add weak binding info + if ( leInfo.dyldInfo->weak_bind_size != 0 ) { + uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096); + uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096); + sprintf(message, "weak bindings from %s", shortName); + add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message); + } + }); + + for (std::map::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) { + printf("0x%08X %s\n", it->first, it->second); + } + break; + } + case modeSize: { + struct TextInfo { + uint64_t textSize; + const char* path; + }; + __block std::vector textSegments; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + + dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh; + ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { + if ( strcmp(info.segName, "__TEXT") != 0 ) + return; + textSegments.push_back({ info.fileSize, installName }); + }); + }); + std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) { + return (left.textSize > right.textSize); + }); + for (std::vector::iterator it = textSegments.begin(); it != textSegments.end(); ++it) { + printf(" 0x%08llX %s\n", it->textSize, it->path); + } + break; + } + case modePatchTable: { + std::vector segInfos; + buildSegmentInfo(dyldCache, segInfos); + __block uint32_t imageIndex = 0; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + printf("%s:\n", installName); + dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) { + printf(" export: 0x%08X %s\n", cacheOffsetOfImpl, exportName); + dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) { + SegmentInfo usageAt; + const uint64_t patchLocVmAddr = dyldCache->unslidLoadAddress() + patchLocation.cacheOffset; + findImageAndSegment(dyldCache, segInfos, patchLocation.cacheOffset, &usageAt); + if ( patchLocation.addend == 0 ) + printf(" used by: %s+0x%04llX in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, usageAt.installName); + else + printf(" used by: %s+0x%04llX (addend=%d) in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, patchLocation.addend, usageAt.installName); + }); + }); + ++imageIndex; + }); + break; + } + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeVerboseSlideInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeJSONMap: + case modeJSONDependents: + case modeSectionSizes: + case modeStrings: + case modeObjCProtocols: + case modeObjCImpCaches: + case modeObjCClasses: + case modeObjCSelectors: + case modeExtract: + break; + } + } + return 0; +} diff --git a/dyld3/shared-cache/dyld_shared_cache_util.cpp-strings b/dyld3/shared-cache/dyld_shared_cache_util.cpp-strings new file mode 100644 index 0000000..e69de29 diff --git a/dyld3/shared-cache/dyldinfo.cpp b/dyld3/shared-cache/dyldinfo.cpp index 0e1817e..7e4361d 100644 --- a/dyld3/shared-cache/dyldinfo.cpp +++ b/dyld3/shared-cache/dyldinfo.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008-2010 Apple Inc. All rights reserved. + * Copyright (c) 2018-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -41,12 +42,11 @@ #include "MachOFile.h" #include "MachOLoaded.h" #include "MachOAnalyzer.h" +#include "MachOAnalyzerSet.h" #include "ClosureFileSystemPhysical.h" +#include "DyldSharedCache.h" -static bool printSharedRegion = false; -static bool printFunctionStarts = false; -static bool printDataCode = false; - +typedef dyld3::MachOLoaded::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk; static void versionToString(uint32_t value, char buffer[32]) { @@ -71,26 +71,47 @@ static void printPlatforms(const dyld3::MachOAnalyzer* ma) }); } +static void permString(uint32_t permFlags, char str[4]) +{ + str[0] = (permFlags & VM_PROT_READ) ? 'r' : '.'; + str[1] = (permFlags & VM_PROT_WRITE) ? 'w' : '.'; + str[2] = (permFlags & VM_PROT_EXECUTE) ? 'x' : '.'; + str[3] = '\0'; +} + static void printSegments(const dyld3::MachOAnalyzer* ma) { - printf(" -segments:\n"); - printf(" load-offset segment section sect-size seg-size perm\n"); - __block const char* lastSegName = ""; - __block uint64_t firstSegVmAddr = 0; - ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { - if ( lastSegName[0] == '\0' ) - firstSegVmAddr = sectInfo.segInfo.vmAddr; - if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) { - char r = (sectInfo.segInfo.protections & VM_PROT_READ) ? 'r' : '.'; - char w = (sectInfo.segInfo.protections & VM_PROT_WRITE) ? 'w' : '.'; - char x = (sectInfo.segInfo.protections & VM_PROT_EXECUTE) ? 'x' : '.'; - printf(" 0x%08llX %-12s %6lluKB %c%c%c\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, r, w, x); - lastSegName = sectInfo.segInfo.segName; - } - printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize); - - }); - + if ( ma->inDyldCache() ) { + printf(" -segments:\n"); + printf(" load-address segment section sect-size seg-size perm\n"); + __block const char* lastSegName = ""; + ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) { + char permChars[8]; + permString(sectInfo.segInfo.protections, permChars); + printf(" 0x%08llX %-16s %16lluKB %s\n", sectInfo.segInfo.vmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars); + lastSegName = sectInfo.segInfo.segName; + } + printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr, sectInfo.sectName, sectInfo.sectSize); + }); + } + else { + printf(" -segments:\n"); + printf(" load-offset segment section sect-size seg-size perm\n"); + __block const char* lastSegName = ""; + __block uint64_t firstSegVmAddr = 0; + ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( lastSegName[0] == '\0' ) + firstSegVmAddr = sectInfo.segInfo.vmAddr; + if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) { + char permChars[8]; + permString(sectInfo.segInfo.protections, permChars); + printf(" 0x%08llX %-16s %6lluKB %s\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars); + lastSegName = sectInfo.segInfo.segName; + } + printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize); + }); + } } @@ -110,43 +131,126 @@ static void printDependents(const dyld3::MachOAnalyzer* ma) }); } -static const char* rebaseTypeName(uint8_t type) +static bool liveMachO(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen) { - switch (type ){ - case REBASE_TYPE_POINTER: - return "rebase pointer"; - case REBASE_TYPE_TEXT_ABSOLUTE32: - return "rebase text abs32"; - case REBASE_TYPE_TEXT_PCREL32: - return "rebase text rel32"; - } - return "!!unknown!!"; + if ( dyldCache == nullptr ) + return false; + const uint8_t* cacheStart = (uint8_t*)dyldCache; + const uint8_t* cacheEnd = &cacheStart[cacheLen]; + if ( (uint8_t*)ma < cacheStart) + return false; + if ( (uint8_t*)ma > cacheEnd) + return false; + + // only return true for live images + return ( dyld_image_header_containing_address(ma) != nullptr ); } -static const char* bindTypeName(uint8_t type) +static void printInitializers(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen) { - switch (type ){ - case BIND_TYPE_POINTER: - return "bind pointer"; - case BIND_TYPE_TEXT_ABSOLUTE32: - return "bind text abs32"; - case BIND_TYPE_TEXT_PCREL32: - return "bind text rel32"; + printf(" -inits:\n"); + Diagnostics diag; + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false)); + ma->forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) { + uint64_t targetLoadAddr = (uint64_t)ma+offset; + const char* symbolName; + uint64_t symbolLoadAddr; + if ( ma->findClosestSymbol(targetLoadAddr, &symbolName, &symbolLoadAddr) ) { + uint64_t delta = targetLoadAddr - symbolLoadAddr; + if ( delta == 0 ) + printf(" 0x%08X %s\n", offset, symbolName); + else + printf(" 0x%08X %s + 0x%llX\n", offset, symbolName, delta); + } + else + printf(" 0x%08X\n", offset); + }); + if ( ma->hasPlusLoadMethod(diag) ) { + // can't inspect ObjC of a live dylib + if ( liveMachO(ma, dyldCache, cacheLen) ) { + printf(" <<>>\n"); + return; + } + const uint32_t pointerSize = ma->pointerSize(); + uint64_t prefLoadAddress = ma->preferredLoadAddress(); + // print all +load methods on classes in this image + ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if (!isMetaClass) + return; + dyld3::MachOAnalyzer::PrintableStringResult classNameResult; + const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult); + if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) { + ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + dyld3::MachOAnalyzer::PrintableStringResult methodNameResult; + const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult); + if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) { + if ( strcmp(methodName, "load") == 0 ) + printf(" 0x%08llX +[%s %s]\n", methodVMAddr-prefLoadAddress, className, methodName); + } + }); + } + }); + // print all +load methods on categories in this image + ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, + const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { + dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult; + const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult); + if ( categoryNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) { + ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { + dyld3::MachOAnalyzer::PrintableStringResult methodNameResult; + const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult); + if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) { + if ( strcmp(methodName, "load") == 0 ) { + // FIXME: if category is on class in another image, forEachObjCCategory returns null for objcCategory.clsVMAddr, need way to get name + __block const char* catOnClassName = ""; + ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, + uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, + const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) { + if ( objcCategory.clsVMAddr == classVMAddr ) { + dyld3::MachOAnalyzer::PrintableStringResult classNameResult; + const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult); + if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) { + catOnClassName = className; + } + } + }); + printf(" 0x%08llX +[%s(%s) %s]\n", methodVMAddr-prefLoadAddress, catOnClassName, categoryName, methodName); + } + } + }); + } + }); } - return "!!unknown!!"; } + static const char* pointerFormat(uint16_t format) { switch (format) { case DYLD_CHAINED_PTR_ARM64E: - return "authenticated arm64e"; + return "authenticated arm64e, 8-byte stride, target vmadddr"; + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + return "authenticated arm64e, 8-byte stride, target vmoffset"; + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + return "authenticated arm64e, 4-byte stride, target vmadddr"; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + return "authenticated arm64e, 4-byte stride, target vmoffset"; case DYLD_CHAINED_PTR_64: - return "generic 64-bit"; + return "generic 64-bit, 4-byte stride, target vmadddr"; + case DYLD_CHAINED_PTR_64_OFFSET: + return "generic 64-bit, 4-byte stride, target vmoffset "; case DYLD_CHAINED_PTR_32: return "generic 32-bit"; case DYLD_CHAINED_PTR_32_CACHE: return "32-bit for dyld cache"; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + return "64-bit for kernel cache"; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + return "64-bit for x86_64 kernel cache"; + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + return "authenticated arm64e, 8-byte stride, target vmoffset, 24-bit bind ordinals"; } return "unknown"; } @@ -167,18 +271,112 @@ static void printChains(const dyld3::MachOAnalyzer* ma) printf(" segment_offset: 0x%08llX\n", seg->segment_offset); printf(" max_pointer: 0x%08X\n", seg->max_valid_pointer); printf(" pages: %d\n", seg->page_count); - for (int p=0; p < seg->page_count; ++p) { - printf(" start[% 2d]: 0x%04X\n", p, seg->page_start[p]); + for (int pageIndex=0; pageIndex < seg->page_count; ++pageIndex) { + uint16_t offsetInPage = seg->page_start[pageIndex]; + if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE ) + continue; + if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) { + // 32-bit chains which may need multiple starts per page + uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI; + bool chainEnd = false; + while (!chainEnd) { + chainEnd = (seg->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST); + offsetInPage = (seg->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST); + printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage); + ++overflowIndex; + } + } + else { + // one chain per page + printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage); + } } } }); } + +static void printChainDetails(const dyld3::MachOAnalyzer* ma) +{ + __block Diagnostics diag; + ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { + ma->forEachFixupInAllChains(diag, starts, true, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { + uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; + switch (segInfo->pointer_format) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + if ( fixupLoc->arm64e.authRebase.auth ) { + uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.authBind24.ordinal : fixupLoc->arm64e.authBind.ordinal; + if ( fixupLoc->arm64e.authBind.bind ) { + printf(" 0x%08llX: raw: 0x%016llX auth-bind: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, ordinal: %04X)\n", vmOffset, fixupLoc->raw64, + fixupLoc->arm64e.authBind.next, fixupLoc->arm64e.keyName(), + fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, bindOrdinal); + } + else { + printf(" 0x%08llX: raw: 0x%016llX auth-rebase: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, target: 0x%08X)\n", vmOffset, fixupLoc->raw64, + fixupLoc->arm64e.authRebase.next, fixupLoc->arm64e.keyName(), + fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authRebase.target); + } + } + else { + uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.bind24.ordinal : fixupLoc->arm64e.bind.ordinal; + if ( fixupLoc->arm64e.rebase.bind ) { + printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %04X, addend: %d)\n", vmOffset, fixupLoc->raw64, + fixupLoc->arm64e.bind.next, bindOrdinal, fixupLoc->arm64e.bind.addend); + } + else { + printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64, + fixupLoc->arm64e.rebase.next, fixupLoc->arm64e.rebase.target, fixupLoc->arm64e.rebase.high8); + } + } + break; + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + if ( fixupLoc->generic64.rebase.bind ) { + printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %06X, addend: %d)\n", vmOffset, fixupLoc->raw64, + fixupLoc->generic64.bind.next, fixupLoc->generic64.bind.ordinal, fixupLoc->generic64.bind.addend); + } + else { + printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64, + fixupLoc->generic64.rebase.next, fixupLoc->generic64.rebase.target, fixupLoc->generic64.rebase.high8); + } + break; + case DYLD_CHAINED_PTR_32: + if ( fixupLoc->generic32.bind.bind ) { + printf(" 0x%08llX: raw: 0x%08X bind: (next:%02d ordinal:%05X addend:%d)\n", vmOffset, fixupLoc->raw32, + fixupLoc->generic32.bind.next, fixupLoc->generic32.bind.ordinal, fixupLoc->generic32.bind.addend); + } + else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) { + uint32_t bias = (0x04000000 + segInfo->max_valid_pointer)/2; + uint32_t value = fixupLoc->generic32.rebase.target - bias; + printf(" 0x%08llX: raw: 0x%08X nonptr: (next:%02d value: 0x%08X)\n", vmOffset, fixupLoc->raw32, + fixupLoc->generic32.rebase.next, value); + } + else { + printf(" 0x%08llX: raw: 0x%08X rebase: (next:%02d target: 0x%07X)\n", vmOffset, fixupLoc->raw32, + fixupLoc->generic32.rebase.next, fixupLoc->generic32.rebase.target); + } + break; + default: + fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format); + break; + } + }); + }); + if ( diag.hasError() ) + fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage()); +} + + struct FixupInfo { - const char* segName; + std::string segName; std::string sectName; uint64_t address; + dyld3::MachOAnalyzerSet::PointerMetaData pmd; const char* type; uint64_t targetValue; const char* targetDylib; @@ -188,6 +386,14 @@ struct FixupInfo }; +struct SymbolicFixupInfo +{ + uint64_t address; + const char* kind; + std::string target; +}; + + static const char* ordinalName(const dyld3::MachOAnalyzer* ma, int libraryOrdinal) { @@ -229,6 +435,8 @@ public: const char* segmentName(uint64_t vmOffset) const; const char* sectionName(uint64_t vmOffset) const; uint64_t baseAddress() const { return _baseAddress; } + uint64_t currentSectionAddress() const { return _lastSection.sectAddr; } + bool isNewSection(uint64_t vmOffset) const; private: void updateLastSection(uint64_t vmOffset) const; @@ -236,6 +444,7 @@ private: const dyld3::MachOAnalyzer* _ma; uint64_t _baseAddress; mutable dyld3::MachOFile::SectionInfo _lastSection; + mutable char _lastSegName[20]; mutable char _lastSectName[20]; }; @@ -247,13 +456,22 @@ SectionFinder::SectionFinder(const dyld3::MachOAnalyzer* ma) _lastSection.sectSize = 0; } -void SectionFinder::updateLastSection(uint64_t vmOffset) const +bool SectionFinder::isNewSection(uint64_t vmOffset) const { uint64_t vmAddr = _baseAddress + vmOffset; - if ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) ) { + return ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) ); +} + +void SectionFinder::updateLastSection(uint64_t vmOffset) const +{ + if ( isNewSection(vmOffset) ) { + _lastSegName[0] = '\0'; + _lastSectName[0] = '\0'; + uint64_t vmAddr = _baseAddress + vmOffset; _ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectStop) { if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) { _lastSection = sectInfo; + strcpy(_lastSegName, _lastSection.segInfo.segName); strcpy(_lastSectName, _lastSection.sectName); sectStop = true; } @@ -264,7 +482,7 @@ void SectionFinder::updateLastSection(uint64_t vmOffset) const const char* SectionFinder::segmentName(uint64_t vmOffset) const { updateLastSection(vmOffset); - return _lastSection.segInfo.segName; + return _lastSegName; } const char* SectionFinder::sectionName(uint64_t vmOffset) const @@ -274,209 +492,188 @@ const char* SectionFinder::sectionName(uint64_t vmOffset) const } +static inline std::string decimal(int64_t value) { + char buff[64]; + sprintf(buff, "%lld", value); + return buff; +} -static void printPreloadChainedFixups(const dyld3::MachOAnalyzer* ma) +static inline std::string hex(int64_t value) { + char buff[64]; + sprintf(buff, "0x%llX", value); + return buff; +} + +static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr) { - printf(" segment section address type (dvrsty addr key) target\n"); - SectionFinder namer(ma); - uint64_t sectionSize; - const dyld_chained_starts_offsets* startsSection = (dyld_chained_starts_offsets*)ma->findSectionContent("__TEXT", "__chain_starts", sectionSize); - if ( startsSection != nullptr ) { - switch (startsSection->pointer_format) { - case DYLD_CHAINED_PTR_32_FIRMWARE: - for (uint32_t startIndex=0; startIndex < startsSection->starts_count; ++startIndex) { - const dyld_chained_ptr_32_firmware_rebase* p = (dyld_chained_ptr_32_firmware_rebase*)(((uint8_t*)ma)+ startsSection->chain_starts[startIndex]); - bool done = false; - while (!done) { - uint64_t vmOffset = ((uint8_t*)p - (uint8_t*)ma); - printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), namer.baseAddress()+vmOffset, - "rebase pointer", p->target); - done = (p->next == 0); - p += p->next; - } - } + uint64_t targetLoadAddr = (uint64_t)ma+vmAddr; + const char* targetSymbolName; + uint64_t targetSymbolLoadAddr; + if ( ma->findClosestSymbol(targetLoadAddr, &targetSymbolName, &targetSymbolLoadAddr) ) { + uint64_t delta = targetLoadAddr - targetSymbolLoadAddr; + if ( delta == 0 ) { + return targetSymbolName; } - + else { + if ( (delta == 1) && (ma->cputype == CPU_TYPE_ARM) ) + return std::string(targetSymbolName) + std::string(" [thumb]"); + else + return std::string(targetSymbolName) + std::string("+") + decimal(delta); + } + } + else { + __block std::string result; + ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) { + if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) { + if ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) { + const char* cstring = (char*)ma + (vmAddr-ma->preferredLoadAddress()); + result = std::string("\"") + cstring + std::string("\""); + } + else { + result = std::string(sectInfo.segInfo.segName) + "/" + sectInfo.sectName + "+" + decimal(vmAddr - sectInfo.sectAddr); + } + } + }); + return result; } } -struct FixupTarget +typedef dyld3::MachOAnalyzerSet MachOAnalyzerSet; +typedef dyld3::MachOAnalyzerSet::WrappedMachO WrappedMachO; + +struct InfoAnalyzerSet : public MachOAnalyzerSet { - uint64_t value; - const char* dylib; - const char* symbolName; - uint64_t addend; - bool weakImport; + void mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override; + void mas_mainExecutable(WrappedMachO& anImage) const override; + void* mas_dyldCache() const override; + bool wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override; + const char* wmo_path(const WrappedMachO* image) const override; + ExportsTrie wmo_getExportsTrie(const WrappedMachO* image) const override; + bool wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, + bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const override; + bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override; }; - -static void printChainedFixups(const dyld3::MachOAnalyzer* ma) +bool InfoAnalyzerSet::wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const { - // build array of targets - __block Diagnostics diag; - __block std::vector targets; - ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { - FixupTarget target; - target.value = 0; - target.dylib = ordinalName(ma, libOrdinal); - target.symbolName = symbolName; - target.addend = addend; - target.weakImport = weakImport; - targets.push_back(target); - }); - if ( diag.hasError() ) - return; - - uint64_t baseAddress = ma->preferredLoadAddress(); - - printf(" segment section address type (dvrsty addr key) target\n"); - SectionFinder namer(ma); - ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { - ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) { - uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma; - switch (segInfo->pointer_format) { - case DYLD_CHAINED_PTR_ARM64E: - if ( fixupLoc->arm64e.authRebase.auth ) { - if ( fixupLoc->arm64e.authBind.bind ) { - const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal]; - if ( bindTarget.addend ) - printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s + 0x%llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr", - fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, - fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend); - else - printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr", - fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, - fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName); - } - else { - uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress; - printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) 0x%08llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "rebase authptr", - fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, - fixupLoc->arm64e.keyName(), targetAddr); - } - } - else { - if ( fixupLoc->arm64e.rebase.bind ) { - const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal]; - uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend(); - if ( fullAddend ) - printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend); - else - printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName); - } - else { - uint64_t targetAddr = fixupLoc->arm64e.unpackTarget(); - printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "rebase pointer", targetAddr); - } - } - break; - case DYLD_CHAINED_PTR_64: - if ( fixupLoc->generic64.rebase.bind ) { - const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal]; - uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend(); - if ( fullAddend ) - printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend); - else - printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName); - } - else { - uint64_t targetAddr = fixupLoc->generic64.unpackedTarget(); - printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "rebase pointer", targetAddr); - } - break; - case DYLD_CHAINED_PTR_32: - if ( fixupLoc->generic32.rebase.bind ) { - const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal]; - uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend; - if ( fullAddend ) - printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%X\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend); - else - printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "bind pointer", bindTarget.dylib, bindTarget.symbolName); - } - else { - uint32_t targetAddr = fixupLoc->generic32.rebase.target; - printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n", - namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), - "rebase pointer", targetAddr); - } - break; - default: - fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format); - break; - } - }); - }); + const char* depPath = image->_mh->dependentDylibLoadPath(depIndex); + childObj = WrappedMachO(nullptr, image->_set, (void*)depPath); + return true; } -static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma) +const char* InfoAnalyzerSet::wmo_path(const WrappedMachO* image) const { + return (const char*)image->_other; +} + +MachOAnalyzerSet::ExportsTrie InfoAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* obj) const +{ + return { nullptr, nullptr }; +} + +bool InfoAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* obj, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, + bool lazyBind, uint64_t addend, CachePatchHandler ph, MachOAnalyzerSet::FixupTarget& target) const +{ + target.requestedSymbolName = symbolName; + target.foundSymbolName = symbolName; + target.addend = addend; + target.kind = weakImport ? MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol : MachOAnalyzerSet::FixupTarget::Kind::bindToImage; + if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"self"); + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"main-executable"); + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) { + target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"flat-namespace"); + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) { + target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"weak-coalesce"); + } + else { + int depIndex = libOrdinal - 1; + const char* depPath = obj->_mh->dependentDylibLoadPath(depIndex); + const char* lastSlash = strrchr(depPath, '/'); + if (lastSlash) + ++lastSlash; + else + lastSlash = depPath; + target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)lastSlash); + } + return true; +} + +void InfoAnalyzerSet::mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const +{ + WrappedMachO anImage(nullptr, this, (void*)"flat-namespace"); + bool stop = false; + handler(anImage, false, stop); +} + + +bool InfoAnalyzerSet::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const +{ + return false; +} + +void InfoAnalyzerSet::mas_mainExecutable(WrappedMachO& anImage) const +{ + anImage = WrappedMachO(nullptr, this, (void*)"main-executable"); +} + +void* InfoAnalyzerSet::mas_dyldCache() const +{ + return nullptr; +} + +static void printFixups(const dyld3::MachOAnalyzer* ma, const char* path) +{ + printf(" -fixups:\n"); Diagnostics diag; __block std::vector fixups; + uint64_t prefLoadAddr = ma->preferredLoadAddress(); SectionFinder namer(ma); - ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[], - bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) { - const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex]; - uint64_t locVmAddr = segment.vmAddr + segOffset; - uint64_t runtimeOffset = locVmAddr - namer.baseAddress(); - const uint8_t* loc = ((uint8_t*)ma + runtimeOffset); - uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc)); + InfoAnalyzerSet ias; + WrappedMachO wm(ma, &ias, (void*)path); + wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) { FixupInfo fixup; - fixup.segName = namer.segmentName(runtimeOffset); - fixup.sectName = namer.sectionName(runtimeOffset); - fixup.address = locVmAddr; - fixup.type = rebaseTypeName(type); - fixup.targetValue = value; - fixup.targetDylib = nullptr; - fixup.targetSymbolName = nullptr; - fixup.targetAddend = 0; + fixup.segName = namer.segmentName(fixupLocRuntimeOffset); + fixup.sectName = namer.sectionName(fixupLocRuntimeOffset); + fixup.address = prefLoadAddr + fixupLocRuntimeOffset; + fixup.pmd = pmd; fixup.targetWeakImport = false; + switch ( target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + fixup.type = "absolute"; + break; + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + fixup.type = "rebase"; + fixup.targetSymbolName = nullptr; + fixup.targetDylib = nullptr; + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + fixup.type = "bind"; + fixup.targetSymbolName = target.requestedSymbolName; + fixup.targetDylib = target.foundInImage.path(); + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + fixup.type = "bind"; + fixup.targetSymbolName = target.requestedSymbolName; + fixup.targetDylib = target.foundInImage.path(); + fixup.targetWeakImport = true; + break; + } + fixup.targetValue = target.offsetInImage; + fixup.targetAddend = target.addend; + if ( pmd.high8 ) + fixup.targetAddend += ((uint64_t)pmd.high8 << 56); fixups.push_back(fixup); + }, + ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) { }); - ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[], - bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, - uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, - uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) { - const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex]; - uint64_t locVmAddr = segment.vmAddr + segOffset; - uint64_t runtimeOffset = locVmAddr - namer.baseAddress(); - FixupInfo fixup; - fixup.segName = namer.segmentName(runtimeOffset); - fixup.sectName = namer.sectionName(runtimeOffset); - fixup.address = locVmAddr; - fixup.type = bindTypeName(type); - fixup.targetValue = 0; - fixup.targetDylib = ordinalName(ma, libOrdinal); - fixup.targetSymbolName = symbolName; - fixup.targetAddend = addend; - fixup.targetWeakImport = weakImport; - fixups.push_back(fixup); - },^(const char* symbolName) { - },^() { }); - - std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) { if ( &l == &r ) return false; @@ -487,33 +684,91 @@ static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma) printf(" segment section address type target\n"); for (const FixupInfo& fixup : fixups) { + char authInfo[128]; + authInfo[0] = '\0'; + if ( fixup.pmd.authenticated ) { + sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", fixup.pmd.diversity, fixup.pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(fixup.pmd.key)); + } if ( fixup.targetSymbolName == nullptr ) - printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue); + printf(" %-12s %-16s 0x%08llX %16s 0x%08llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue, authInfo); else if ( fixup.targetAddend != 0 ) - printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, fixup.targetAddend); + printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, fixup.targetAddend, authInfo); else if ( fixup.targetWeakImport ) - printf(" %-12s %-16s 0x%08llX %16s %s/%s [weak-import]\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName); + printf(" %-12s %-16s 0x%08llX %16s %s/%s [weak-import]%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo); else - printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName); + printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo); } - - } - -static void printFixups(const dyld3::MachOAnalyzer* ma) +static void printSymbolicFixups(const dyld3::MachOAnalyzer* ma, const char* path) { - printf(" -fixups:\n"); - if ( ma->isPreload() || ma->isStaticExecutable() ) { - printPreloadChainedFixups(ma); + printf(" -symbolic_fixups:\n"); + Diagnostics diag; + __block std::vector fixups; + uint64_t prefLoadAddr = ma->preferredLoadAddress(); + InfoAnalyzerSet ias; + WrappedMachO wm(ma, &ias, (void*)path); + wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) { + SymbolicFixupInfo fixup; + fixup.address = prefLoadAddr + fixupLocRuntimeOffset; + switch ( target.kind ) { + case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute: + fixup.kind = "absolute"; + fixup.target = ""; + break; + case MachOAnalyzerSet::FixupTarget::Kind::rebase: + fixup.kind = "rebase pointer"; + fixup.target = rebaseTargetString(ma, target.offsetInImage); + break; + case MachOAnalyzerSet::FixupTarget::Kind::bindToImage: + case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol: + fixup.kind = "bind pointer"; + fixup.target = std::string(target.foundInImage.path()) + "/" + target.requestedSymbolName; + if ( target.addend != 0 ) + fixup.target += std::string("+") + decimal(target.addend); + if ( pmd.high8 ) + fixup.target += std::string("+") + hex(((uint64_t)pmd.high8 << 56)); + //if ( target.weakImport ) + // fixup.target += " [weak-import]"; + break; + } + if ( pmd.authenticated ) { + char authInfo[256]; + sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", pmd.diversity, pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(pmd.key)); + fixup.target += authInfo; + } + fixups.push_back(fixup); + }, + ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) { + }); + + + std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) { + if ( &l == &r ) + return false; + return ( l.address < r.address ); + }); + + SectionFinder sectionTracker(ma); + uint64_t lastSymbolVmOffset = 0; + for (const SymbolicFixupInfo& fixup : fixups) { + uint64_t vmAddr = fixup.address; + uint64_t vmOffset = vmAddr - prefLoadAddr; + if ( sectionTracker.isNewSection(vmOffset) ) { + printf(" 0x%08llX %-12s %-16s \n", vmAddr, sectionTracker.segmentName(vmOffset), sectionTracker.sectionName(vmOffset)); + } + const char* symbolName; + uint64_t symbolLoadAddr = 0; + if ( ma->findClosestSymbol((uint64_t)ma+vmOffset, &symbolName, &symbolLoadAddr) ) { + uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma; + if ( symbolVmOffset != lastSymbolVmOffset ) { + printf(" %s:\n", symbolName); + lastSymbolVmOffset = symbolVmOffset; + } + } + printf(" +0x%04llX %16s %s\n", vmOffset - lastSymbolVmOffset, fixup.kind, fixup.target.c_str()); } - else if ( ma->hasChainedFixups() ) { - printChainedFixups(ma); - } - else { - printOpcodeFixups(ma); - } - } +} static void printExports(const dyld3::MachOAnalyzer* ma) @@ -571,11 +826,11 @@ static void printExports(const dyld3::MachOAnalyzer* ma) } -static void printObjC(const dyld3::MachOAnalyzer* ma) +static void printObjC(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen) { Diagnostics diag; - const bool contentRebased = false; const uint32_t pointerSize = ma->pointerSize(); + const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false)); auto printMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) { const char* type = "method"; @@ -600,6 +855,11 @@ static void printObjC(const dyld3::MachOAnalyzer* ma) }; printf(" -objc:\n"); + // can't inspect ObjC of a live dylib + if ( liveMachO(ma, dyldCache, cacheLen) ) { + printf(" <<>>\n"); + return; + } printf(" type vmaddr data-vmaddr name\n"); auto printClass = ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, @@ -627,8 +887,7 @@ static void printObjC(const dyld3::MachOAnalyzer* ma) type, classVMAddr, objcClass.dataVMAddr, className); // Now print the methods on this class - ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), contentRebased, - printMethod); + ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, printMethod); }; auto printCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) { @@ -653,9 +912,9 @@ static void printObjC(const dyld3::MachOAnalyzer* ma) type, categoryVMAddr, categoryName); // Now print the methods on this category - ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, printMethod); - ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, printMethod); }; auto printProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr, @@ -681,18 +940,18 @@ static void printObjC(const dyld3::MachOAnalyzer* ma) type, protocolVMAddr, protocolName); // Now print the methods on this protocol - ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, vmAddrConverter, printMethod); - ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, vmAddrConverter, printMethod); - ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, vmAddrConverter, printMethod); - ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, contentRebased, + ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, vmAddrConverter, printMethod); }; - ma->forEachObjCClass(diag, contentRebased, printClass); - ma->forEachObjCCategory(diag, contentRebased, printCategory); - ma->forEachObjCProtocol(diag, contentRebased, printProtocol); + ma->forEachObjCClass(diag, vmAddrConverter, printClass); + ma->forEachObjCCategory(diag, vmAddrConverter, printCategory); + ma->forEachObjCProtocol(diag, vmAddrConverter, printProtocol); } static void usage() @@ -747,10 +1006,14 @@ int main(int argc, const char* argv[]) return 0; } + size_t cacheLen; + const DyldSharedCache* cache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLen); for (const char* path : files) { Diagnostics diag; + bool fromSharedCache = false; dyld3::closure::FileSystemPhysical fileSystem; dyld3::closure::LoadedFileInfo info; + __block std::vector archesForFile; char realerPath[MAXPATHLEN]; __block bool printedError = false; if (!fileSystem.loadFile(path, info, realerPath, ^(const char* format, ...) { @@ -761,11 +1024,27 @@ int main(int argc, const char* argv[]) va_end(list); printedError = true; })) { - if (!printedError ) + if ( printedError ) + return 1; + // see if path is in current dyld shared cache + info.fileContent = nullptr; + if ( cache != nullptr ) { + uint32_t imageIndex; + if ( cache->hasImagePath(path, imageIndex) ) { + uint64_t mTime; + uint64_t inode; + const mach_header* mh = cache->getIndexedImageEntry(imageIndex, mTime, inode); + info.fileContent = mh; + info.path = path; + fromSharedCache = true; + archesForFile.push_back(cache->archName()); + } + } + if ( !fromSharedCache ) { fprintf(stderr, "dyldinfo: %s: file not found\n", path); - return 1; + return 1; + } } - __block std::vector archesForFile; __block dyld3::Platform platform = dyld3::Platform::unknown; if ( dyld3::FatFile::isFatFile(info.fileContent) ) { const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent; @@ -781,7 +1060,7 @@ int main(int argc, const char* argv[]) } }); } - else { + else if ( !fromSharedCache ) { const dyld3::MachOFile* mo = (dyld3::MachOFile*)info.fileContent; if ( mo->isMachO(diag, info.sliceLen) ) { archesForFile.push_back(mo->archName()); @@ -801,7 +1080,8 @@ int main(int argc, const char* argv[]) } char loadedPath[MAXPATHLEN]; for (const char* sliceArch : archesForFile) { - info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath); + if ( !fromSharedCache ) + info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath); if ( diag.hasError() ) { fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage()); return 1; @@ -828,10 +1108,14 @@ int main(int argc, const char* argv[]) } else if ( strcmp(arg, "-dependents") == 0 ) { printDependents(ma); - somethingPrinted = true; + somethingPrinted = true; + } + else if ( strcmp(arg, "-inits") == 0 ) { + printInitializers(ma, cache, cacheLen); + somethingPrinted = true; } else if ( strcmp(arg, "-fixups") == 0 ) { - printFixups(ma); + printFixups(ma, path); somethingPrinted = true; } else if ( strcmp(arg, "-exports") == 0 ) { @@ -842,6 +1126,14 @@ int main(int argc, const char* argv[]) printChains(ma); somethingPrinted = true; } + else if ( strcmp(arg, "-fixup_chain_details") == 0 ) { + printChainDetails(ma); + somethingPrinted = true; + } + else if ( strcmp(arg, "-symbolic_fixups") == 0 ) { + printSymbolicFixups(ma, path); + somethingPrinted = true; + } else if ( strcmp(arg, "-opcodes") == 0 ) { } else if ( strcmp(arg, "-shared_region") == 0 ) { @@ -851,7 +1143,7 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-data_in_code") == 0 ) { } else if ( strcmp(arg, "-objc") == 0 ) { - printObjC(ma); + printObjC(ma, cache, cacheLen); somethingPrinted = true; } else { @@ -863,9 +1155,10 @@ int main(int argc, const char* argv[]) printPlatforms(ma); printSegments(ma); printDependents(ma); - printFixups(ma); + printInitializers(ma, cache, cacheLen); + printFixups(ma, path); printExports(ma); - printObjC(ma); + printObjC(ma, cache, cacheLen); } } diff --git a/dyld3/shared-cache/kernel_collection_builder.cpp b/dyld3/shared-cache/kernel_collection_builder.cpp new file mode 100644 index 0000000..ab76b2b --- /dev/null +++ b/dyld3/shared-cache/kernel_collection_builder.cpp @@ -0,0 +1,643 @@ +/* -*- 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 "kernel_collection_builder.h" + +#include "AppCacheBuilder.h" +#include "Diagnostics.h" +#include "ClosureFileSystemNull.h" +#include "MachOAppCache.h" + +#include +#include +#include + +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 + +static const uint32_t MajorVersion = 1; +static const uint32_t MinorVersion = 0; + +struct KernelCollectionBuilder { + + KernelCollectionBuilder(const BuildOptions_v1* options); + + struct CollectionFile { + const uint8_t* data = nullptr; + uint64_t size = 0; + const char* path = nullptr; + }; + + struct CacheBuffer { + uint8_t* buffer = nullptr; + uint64_t bufferSize = 0; + }; + + const char* arch = ""; + BuildOptions_v1 options; + std::list inputFileDiags; + std::vector inputFiles; + dyld3::closure::LoadedFileInfo kernelCollectionFileInfo; + dyld3::closure::LoadedFileInfo pageableCollectionFileInfo; + std::vector customSections; + CFDictionaryRef prelinkInfoExtraData = nullptr; + + std::list fileResultsStorage; + std::vector fileResults; + CFDictionaryRef errorsDict = nullptr; + + std::vector errors; + std::vector errorStorage; + + std::list duplicatedStrings; + std::vector typeRefs; + + __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); + + errorStorage.push_back(diag.errorMessage()); + errors.push_back(errorStorage.back().data()); + } + + void retain(CFTypeRef v) { + CFRetain(v); + typeRefs.push_back(v); + } + + const char* strdup(CFStringRef str) { + size_t length = CFStringGetLength(str); + char buffer[length + 1]; + memset(&buffer[0], 0, length + 1); + if ( !CFStringGetCString(str, buffer, length + 1, kCFStringEncodingASCII) ) { + error("Could not convert to ASCII"); + return nullptr; + } + duplicatedStrings.push_back(buffer); + return duplicatedStrings.back().c_str(); + } + +}; + +// What is the version of this builder dylib. Can be used to determine what API is available. +void getVersion(uint32_t *major, uint32_t *minor) { + *major = MajorVersion; + *minor = MinorVersion; +} + +KernelCollectionBuilder::KernelCollectionBuilder(const BuildOptions_v1* options) + : options(*options) { + retain(this->options.arch); +} + +// Returns a valid object on success, or NULL on failure. +__API_AVAILABLE(macos(10.14)) +struct KernelCollectionBuilder* createKernelCollectionBuilder(const struct BuildOptions_v1* options) { + KernelCollectionBuilder* builder = new KernelCollectionBuilder(options); + + if (options->version < kMinBuildVersion) { + builder->error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion); + return builder; + } + if (options->version > kMaxBuildVersion) { + builder->error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion); + return builder; + } + if ( options->arch == nullptr ) { + builder->error("arch must not be null"); + return builder; + } + const char* archName = builder->strdup(options->arch); + if ( archName == nullptr ) { + // Already generated an error in strdup. + return builder; + } + builder->arch = archName; + + return builder; +} + +static bool loadFileFromData(struct KernelCollectionBuilder* builder, + const CFStringRef path, const CFDataRef data, + dyld3::closure::LoadedFileInfo& fileInfo) { + fileInfo.fileContent = CFDataGetBytePtr(data); + fileInfo.fileContentLen = CFDataGetLength(data); + fileInfo.sliceOffset = 0; + fileInfo.sliceLen = CFDataGetLength(data); + fileInfo.isOSBinary = false; + fileInfo.inode = 0; + fileInfo.mtime = 0; + fileInfo.unload = nullptr; + fileInfo.path = builder->strdup(path); + + Diagnostics diag; + dyld3::closure::FileSystemNull fileSystem; + const dyld3::GradedArchs& arch = dyld3::GradedArchs::forName(builder->arch); + auto loaded = dyld3::MachOAnalyzer::loadFromBuffer(diag, fileSystem, fileInfo.path, arch, + dyld3::Platform::unknown, fileInfo); + if ( !loaded ) { + builder->error("%s", diag.errorMessage().c_str()); + return false; + } + + return true; +} + +// Add a kernel static executable file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addKernelFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data) { + builder->retain(path); + builder->retain(data); + + dyld3::closure::LoadedFileInfo info; + bool loaded = loadFileFromData(builder, path, data, info); + if ( !loaded ) + return false; + + DyldSharedCache::MappedMachO mappedFile(info.path, (const dyld3::MachOAnalyzer *)info.fileContent, + info.fileContentLen, false, false, + info.sliceOffset, info.mtime, info.inode); + + AppCacheBuilder::InputDylib input; + input.dylib.mappedFile = mappedFile; + input.dylib.loadedFileInfo = info; + input.dylib.inputFile = nullptr; + input.dylibID = "com.apple.kernel"; + input.errors = &builder->inputFileDiags.emplace_back(); + + builder->inputFiles.push_back(input); + return true; +} + +// Add kext mach-o and plist files. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addKextDataFile(struct KernelCollectionBuilder* builder, const KextFileData_v1* fileData) { + if (fileData->version != 1) { + builder->error("addKextDataFile version %llu is less than minimum supported version of %d", fileData->version, 1); + return false; + } + + builder->retain(fileData->dependencies); + builder->retain(fileData->bundleID); + builder->retain(fileData->bundlePath); + builder->retain(fileData->plist); + + dyld3::closure::LoadedFileInfo info; + + // Code-less kext's don't have a mach-o to load + const char* kextpath = nullptr; + if ( fileData->kextdata != nullptr ) { + builder->retain(fileData->kextpath); + builder->retain(fileData->kextdata); + bool loaded = loadFileFromData(builder, fileData->kextpath, fileData->kextdata, info); + if ( !loaded ) + return false; + kextpath = info.path; + } else { + kextpath = "codeless"; + } + + DyldSharedCache::MappedMachO mappedFile(kextpath, (const dyld3::MachOAnalyzer *)info.fileContent, + info.fileContentLen, false, false, + info.sliceOffset, info.mtime, info.inode); + + uint64_t numDependencies = CFArrayGetCount(fileData->dependencies); + + AppCacheBuilder::InputDylib input; + input.dylib.mappedFile = mappedFile; + input.dylib.loadedFileInfo = info; + input.dylib.inputFile = nullptr; + input.dylibID = builder->strdup(fileData->bundleID); + for (uint64_t i = 0; i != numDependencies; ++i) { + CFTypeRef elementRef = CFArrayGetValueAtIndex(fileData->dependencies, i); + if ( CFGetTypeID(elementRef) != CFStringGetTypeID() ) { + builder->error("Dependency %llu of %s is not a string", i, info.path); + return false; + } + CFStringRef stringRef = (CFStringRef)elementRef; + input.dylibDeps.push_back(builder->strdup(stringRef)); + } + input.infoPlist = fileData->plist; + input.errors = &builder->inputFileDiags.emplace_back(); + input.bundlePath = builder->strdup(fileData->bundlePath); + switch ( fileData->stripMode ) { + case binaryUnknownStripMode: + input.stripMode = CacheBuilder::DylibStripMode::stripNone; + break; + case binaryStripNone: + input.stripMode = CacheBuilder::DylibStripMode::stripNone; + break; + case binaryStripExports: + input.stripMode = CacheBuilder::DylibStripMode::stripExports; + break; + case binaryStripLocals: + input.stripMode = CacheBuilder::DylibStripMode::stripLocals; + break; + case binaryStripAll: + input.stripMode = CacheBuilder::DylibStripMode::stripAll; + break; + } + + builder->inputFiles.push_back(input); + return true; +} + +// Add interface plist file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addInterfaceFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data) { + builder->retain(path); + builder->retain(data); + + assert(0); +} + +// Add collection file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addCollectionFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data, + CollectionKind kind) { + dyld3::closure::LoadedFileInfo* fileInfo = nullptr; + if ( kind == baseKC ) { + if ( (builder->options.collectionKind != auxKC) && (builder->options.collectionKind != pageableKC) ) { + builder->error("Invalid collection file for build"); + return false; + } + fileInfo = &builder->kernelCollectionFileInfo; + } else if ( kind == pageableKC ) { + if ( builder->options.collectionKind != auxKC ) { + builder->error("Invalid collection file for build"); + return false; + } + if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) { + builder->error("Already have collection file"); + return false; + } + fileInfo = &builder->pageableCollectionFileInfo; + } else { + builder->error("Unsupported collection kind"); + return false; + } + if ( fileInfo->fileContent != nullptr ) { + builder->error("Already have collection file"); + return false; + } + + builder->retain(path); + builder->retain(data); + + bool loaded = loadFileFromData(builder, path, data, *fileInfo); + if ( !loaded ) + return false; + + const dyld3::MachOFile* mf = (const dyld3::MachOFile*)fileInfo->fileContent; + if ( !mf->isFileSet() ) { + builder->error("kernel collection is not a cache file: %s\n", builder->strdup(path)); + return false; + } + + return true; +} + +// Add data to the given segment of the final file. Note the section can be null (or "") if desired +__API_AVAILABLE(macos(10.14)) +bool addSegmentData(struct KernelCollectionBuilder* builder, const CFStringRef segmentName, + const CFStringRef sectionName, const CFDataRef data) { + // Check the segment name + if ( segmentName == nullptr ) { + builder->error("Segment data name must be non-null"); + return false; + } + CFIndex segmentNameLength = CFStringGetLength(segmentName); + if ( (segmentNameLength == 0) || (segmentNameLength > 16) ) { + builder->error("Segment data name must not be empty or > 16 characters"); + return false; + } + + AppCacheBuilder::CustomSegment::CustomSection section; + + // Check the section name + if ( sectionName != nullptr ) { + CFIndex sectionNameLength = CFStringGetLength(sectionName); + if ( sectionNameLength != 0 ) { + if ( sectionNameLength > 16 ) { + builder->error("Section data name must not be empty or > 16 characters"); + return false; + } + section.sectionName = builder->strdup(sectionName); + } + } + + // Check the data + if ( data == nullptr ) { + builder->error("Segment data payload must be non-null"); + return false; + } + const uint8_t* dataStart = CFDataGetBytePtr(data); + const uint64_t dataLength = CFDataGetLength(data); + if ( dataLength == 0 ) { + builder->error("Segment data payload must not be empty"); + return false; + } + + builder->retain(data); + section.data.insert(section.data.end(), dataStart, dataStart + dataLength); + + AppCacheBuilder::CustomSegment segment; + segment.segmentName = builder->strdup(segmentName); + segment.sections.push_back(section); + builder->customSections.push_back(segment); + return true; +} + +// Add data to the given segment of the final file. Note the section can be null (or "") if desired +__API_AVAILABLE(macos(10.14)) +bool addPrelinkInfo(struct KernelCollectionBuilder* builder, const CFDictionaryRef extraData) { + if ( builder->prelinkInfoExtraData != nullptr ) { + builder->error("Prelink info data has already been set by an earlier call"); + return false; + } + // Check the data + if ( extraData == nullptr ) { + builder->error("Prelink info data payload must be non-null"); + return false; + } + if ( CFDictionaryGetCount(extraData) == 0 ) { + builder->error("Prelink info data payload must not be empty"); + return false; + } + builder->retain(extraData); + builder->prelinkInfoExtraData = extraData; + return true; +} + +// Set a handler to be called at various points during the build to notify the user of progress. +__API_AVAILABLE(macos(10.14)) +void setProgressCallback(const ProgressCallback callback) { + assert(0); +} + +static AppCacheBuilder::Options::AppCacheKind cacheKind(CollectionKind kind) { + switch (kind) { + case unknownKC: + return AppCacheBuilder::Options::AppCacheKind::none; + case baseKC: + return AppCacheBuilder::Options::AppCacheKind::kernel; + case auxKC: + return AppCacheBuilder::Options::AppCacheKind::auxKC; + case pageableKC: + return AppCacheBuilder::Options::AppCacheKind::pageableKC; + } +} + +static AppCacheBuilder::Options::StripMode stripMode(StripMode mode) { + switch (mode) { + case unknownStripMode: + return AppCacheBuilder::Options::StripMode::none; + case stripNone: + return AppCacheBuilder::Options::StripMode::none; + case stripAll: + return AppCacheBuilder::Options::StripMode::all; + case stripAllKexts: + return AppCacheBuilder::Options::StripMode::allExceptKernel; + } +} + +static void generatePerKextErrors(struct KernelCollectionBuilder* builder) { + CFMutableDictionaryRef errorsDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr); + builder->typeRefs.push_back(errorsDict); + + for (const AppCacheBuilder::InputDylib& file : builder->inputFiles) { + if ( !file.errors->hasError() ) + continue; + + CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, file.dylibID.c_str(), + kCFStringEncodingASCII); + builder->typeRefs.push_back(bundleID); + + CFMutableArrayRef errorsArray = CFArrayCreateMutable(kCFAllocatorDefault, 1, nullptr); + builder->typeRefs.push_back(errorsArray); + + CFStringRef errorString = CFStringCreateWithCString(kCFAllocatorDefault, file.errors->errorMessage().c_str(), + kCFStringEncodingASCII); + builder->typeRefs.push_back(errorString); + + CFArrayAppendValue(errorsArray, errorString); + + // Add this bundle to the dictionary + CFDictionarySetValue(errorsDict, bundleID, errorsArray); + + // FIXME: Remove this once the kernel linker has adopted the new API to get the per-kext errors + // For now also put the per-kext errors on the main error list + builder->error("Could not use '%s' because: %s", file.dylibID.c_str(), file.errors->errorMessage().c_str()); + } + + if ( CFDictionaryGetCount(errorsDict) != 0 ) { + builder->errorsDict = errorsDict; + } +} + +// Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool runKernelCollectionBuilder(struct KernelCollectionBuilder* builder) { + + // Make sure specificed bundle-id's are not already in the caches we + // are linking to + __block std::set existingBundles; + if ( (builder->options.collectionKind == auxKC) || (builder->options.collectionKind == pageableKC) ) { + if ( builder->kernelCollectionFileInfo.fileContent == nullptr ) { + builder->error("Cannot build pageableKC/auxKC without baseKC"); + return false; + } + Diagnostics diag; + + // Check the base KC + const dyld3::MachOAppCache* kernelCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent; + kernelCacheMA->forEachDylib(diag, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + existingBundles.insert(name); + }); + + // Check the pageableKC if we have one + const dyld3::MachOAppCache* pageableCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent; + if ( pageableCacheMA != nullptr ) { + pageableCacheMA->forEachDylib(diag, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) { + existingBundles.insert(name); + }); + } + + bool foundBadBundle = false; + for (const AppCacheBuilder::InputDylib& input : builder->inputFiles) { + if ( existingBundles.find(input.dylibID) != existingBundles.end() ) { + builder->error("kernel collection already contains bundle-id: %s\n", input.dylibID.c_str()); + foundBadBundle = true; + } + } + if ( foundBadBundle ) + return false; + } + + dispatch_apply(builder->inputFiles.size(), DISPATCH_APPLY_AUTO, ^(size_t index) { + const AppCacheBuilder::InputDylib& input = builder->inputFiles[index]; + auto errorHandler = ^(const char* msg) { + input.errors->error("cannot be placed in kernel collection because: %s", msg); + }; + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)input.dylib.loadedFileInfo.fileContent; + // Skip codeless kexts + if ( ma == nullptr ) + return; + if (!ma->canBePlacedInKernelCollection(input.dylib.loadedFileInfo.path, errorHandler)) { + assert(input.errors->hasError()); + } + }); + for (const AppCacheBuilder::InputDylib& input : builder->inputFiles) { + if ( input.errors->hasError() ) { + builder->error("One or more binaries has an error which prevented linking. See other errors."); + generatePerKextErrors(builder); + return false; + } + } + + DyldSharedCache::CreateOptions builderOptions = {}; + std::string runtimePath = ""; + builderOptions.outputFilePath = runtimePath; + builderOptions.outputMapFilePath = builderOptions.outputFilePath + ".json"; + builderOptions.archs = &dyld3::GradedArchs::forName(builder->arch); + builderOptions.platform = dyld3::Platform::unknown; + builderOptions.localSymbolMode = DyldSharedCache::LocalSymbolsMode::keep; + builderOptions.optimizeStubs = true; + builderOptions.optimizeDyldDlopens = false; + builderOptions.optimizeDyldLaunches = false; + builderOptions.codeSigningDigestMode = DyldSharedCache::CodeSigningDigestMode::SHA256only; + builderOptions.dylibsRemovedDuringMastering = true; + builderOptions.inodesAreSameAsRuntime = false; + builderOptions.cacheSupportsASLR = true; + builderOptions.forSimulator = false; + builderOptions.isLocallyBuiltCache = true; + builderOptions.verbose = builder->options.verboseDiagnostics; + builderOptions.evictLeafDylibsOnOverflow = true; + builderOptions.loggingPrefix = ""; + builderOptions.dylibOrdering = {}; + builderOptions.dirtyDataSegmentOrdering = {}; + builderOptions.objcOptimizations = {}; + + AppCacheBuilder::Options appCacheOptions; + appCacheOptions.cacheKind = cacheKind(builder->options.collectionKind); + appCacheOptions.stripMode = stripMode(builder->options.stripMode); + + const dyld3::closure::FileSystemNull builderFileSystem; + + AppCacheBuilder cacheBuilder(builderOptions, appCacheOptions, builderFileSystem); + if ( builder->kernelCollectionFileInfo.fileContent != nullptr ) { + const dyld3::MachOAppCache* appCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent; + cacheBuilder.setExistingKernelCollection(appCacheMA); + } + if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) { + const dyld3::MachOAppCache* appCacheMA = (const dyld3::MachOAppCache*)builder->pageableCollectionFileInfo.fileContent; + cacheBuilder.setExistingPageableKernelCollection(appCacheMA); + } + + // Add custom sections + for (const AppCacheBuilder::CustomSegment& segment : builder->customSections) { + if ( !cacheBuilder.addCustomSection(segment.segmentName, segment.sections.front()) ) { + builder->error("%s", cacheBuilder.errorMessage().c_str()); + return false; + } + } + + // Add prelink info data + if ( builder->prelinkInfoExtraData != nullptr ) { + cacheBuilder.setExtraPrelinkInfo(builder->prelinkInfoExtraData); + } + + cacheBuilder.buildAppCache(builder->inputFiles); + if ( !cacheBuilder.errorMessage().empty() ) { + builder->error("%s", cacheBuilder.errorMessage().c_str()); + generatePerKextErrors(builder); + return false; + } + + uint8_t* cacheBuffer = nullptr; + uint64_t cacheSize = 0; + cacheBuilder.writeBuffer(cacheBuffer, cacheSize); + + CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, cacheBuffer, cacheSize, kCFAllocatorDefault); + builder->retain(dataRef); + CFRelease(dataRef); + + CFArrayRef warningsArrayRef = CFArrayCreate(kCFAllocatorDefault, nullptr, 0, nullptr); + builder->retain(warningsArrayRef); + CFRelease(warningsArrayRef); + + CollectionFileResult_v1 fileResult = { 1, kernelCollection, dataRef, warningsArrayRef }; + + builder->fileResultsStorage.push_back(fileResult); + builder->fileResults.push_back(&builder->fileResultsStorage.back()); + + return true; +} + +// Gets the list of errors we have produced. These may be from incorrect input values, or failures in the build itself +__API_AVAILABLE(macos(10.14)) +const char* const* getErrors(const struct KernelCollectionBuilder* builder, uint64_t* errorCount) { + if (builder->errors.empty()) + return nullptr; + *errorCount = builder->errors.size(); + return builder->errors.data(); +} + +__API_AVAILABLE(macos(10.14)) +CFDictionaryRef getKextErrors(const struct KernelCollectionBuilder* builder) { + return builder->errorsDict; +} + +// Returns an array of the resulting files. These may be new collections, or other files required later +__API_AVAILABLE(macos(10.14)) +const struct CollectionFileResult_v1* const* getCollectionFileResults(struct KernelCollectionBuilder* builder, uint64_t* resultCount) { + if ( builder->fileResults.empty() ) { + *resultCount = 0; + return nullptr; + } + + *resultCount = builder->fileResults.size(); + return builder->fileResults.data(); +} + +__API_AVAILABLE(macos(10.14)) +void destroyKernelCollectionBuilder(struct KernelCollectionBuilder* builder) { + for (CFTypeRef ref : builder->typeRefs) + CFRelease(ref); + dyld3::closure::FileSystemNull fileSystem; + for (const AppCacheBuilder::InputDylib& inputFile : builder->inputFiles) { + fileSystem.unloadFile(inputFile.dylib.loadedFileInfo); + } + if ( builder->kernelCollectionFileInfo.fileContent != nullptr) { + fileSystem.unloadFile(builder->kernelCollectionFileInfo); + } + if ( builder->pageableCollectionFileInfo.fileContent != nullptr) { + fileSystem.unloadFile(builder->pageableCollectionFileInfo); + } + delete builder; +} diff --git a/dyld3/shared-cache/kernel_collection_builder.h b/dyld3/shared-cache/kernel_collection_builder.h new file mode 100644 index 0000000..1139e60 --- /dev/null +++ b/dyld3/shared-cache/kernel_collection_builder.h @@ -0,0 +1,179 @@ +/* -*- 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 kernel_collection_builder_h +#define kernel_collection_builder_h + +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef CF_ENUM(uint32_t, CollectionKind) { + unknownKC = 0, + baseKC = 1, + auxKC = 2, + pageableKC = 3, +}; + +typedef CF_ENUM(uint32_t, FileResultKind) { + unknownFileResult = 0, + kernelCollection = 1, +}; + +typedef CF_ENUM(uint32_t, StripMode) { + unknownStripMode = 0, + stripNone = 1, // Don't strip any symbols + stripAll = 2, // Strip all symbols from all binaries + stripAllKexts = 3, // Don't strip xnu, but strip everything else +}; + +typedef CF_ENUM(uint32_t, BinaryStripMode) { + binaryUnknownStripMode = 0, + binaryStripNone = 1, // Don't strip any symbols + binaryStripExports = 2, // Strip all the exports, but leave the local symbols + binaryStripLocals = 3, // Strip all the locals, but leave the exported symbols + binaryStripAll = 4, // Strip all symbols +}; + +typedef CF_ENUM(uint32_t, ProgressKind) { + unknownProgress = 0, + layout = 1, + generateHeader = 2, + copyInputs = 3, + applySplitSeg = 4, + generatePrelinkInfo = 5, + processFixups = 6, + optimizeStubs = 7, + optimizeLinkedit = 8, + writeFixups = 9, + emitFile = 10 +}; + +struct BuildOptions_v1 +{ + uint64_t version; // Future proofing, set to 1 + CollectionKind collectionKind; + StripMode stripMode; +// Valid archs are one of: "arm64", "arm64e", "x86_64", "x86_64h" + const CFStringRef arch; + bool verboseDiagnostics; +}; + + +struct CollectionFileResult_v1 +{ + uint64_t version; // Future proofing, set to 1 + FileResultKind fileKind; + const CFDataRef data; // Owned by the cache builder. Destroyed by destroyKernelCollectionBuilder + const CFArrayRef warnings; // should this be per-result? +}; + +struct KextFileData_v1 +{ + uint64_t version; // Future proofing, set to 1 + const CFStringRef kextpath; + const CFDataRef kextdata; + const CFArrayRef dependencies; + const CFStringRef bundleID; + const CFStringRef bundlePath; + CFDictionaryRef plist; + BinaryStripMode stripMode; +}; + +typedef void (*ProgressCallback)(const char* message, ProgressKind kind); + +struct KernelCollectionBuilder; + +// What is the version of this builder dylib. Can be used to determine what API is available. +__API_AVAILABLE(macos(10.14)) +void getVersion(uint32_t *major, uint32_t *minor); + +// Returns a valid object on success, or NULL on failure. +__API_AVAILABLE(macos(10.14)) +struct KernelCollectionBuilder* createKernelCollectionBuilder(const struct BuildOptions_v1* options); + +// Add a kernel static executable file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addKernelFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data); + +// Add kext mach-o and plist files. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addKextDataFile(struct KernelCollectionBuilder* builder, const struct KextFileData_v1* fileData); + +// Add interface plist file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addInterfaceFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data); + +// Add collection file. Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool addCollectionFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data, CollectionKind kind); + +// Add data to the given segment of the final file. Note the section can be null (or "") if desired +__API_AVAILABLE(macos(10.14)) +bool addSegmentData(struct KernelCollectionBuilder* builder, const CFStringRef segmentName, const CFStringRef sectionName, const CFDataRef data); + +// Add entries to the prelink info dictionary. Note this can only be used once at this time. +__API_AVAILABLE(macos(10.14)) +bool addPrelinkInfo(struct KernelCollectionBuilder* builder, const CFDictionaryRef extraData); + +// Set a handler to be called at various points during the build to notify the user of progress. +__API_AVAILABLE(macos(10.14)) +void setProgressCallback(const ProgressCallback callback); + +// Returns true on success. +__API_AVAILABLE(macos(10.14)) +bool runKernelCollectionBuilder(struct KernelCollectionBuilder* builder); + +// Gets the list of errors we have produced. These may be from incorrect input values, or failures in the build itself +__API_AVAILABLE(macos(10.14)) +const char* const* getErrors(const struct KernelCollectionBuilder* builder, uint64_t* errorCount); + +// Gets the errors on each kext. This returns null if there are no such errors. +// A non-null result is a dictionary where the keys are bundle-ids and the values are +// arrays of strings represeting error messages. +__API_AVAILABLE(macos(10.14)) +CFDictionaryRef getKextErrors(const struct KernelCollectionBuilder* builder); + +// Returns an array of the resulting files. These may be new collections, or other files required later +__API_AVAILABLE(macos(10.14)) +const struct CollectionFileResult_v1* const* getCollectionFileResults(struct KernelCollectionBuilder* builder, uint64_t* resultCount); + +__API_AVAILABLE(macos(10.14)) +void destroyKernelCollectionBuilder(struct KernelCollectionBuilder* builder); + +#ifdef __cplusplus +} +#endif + +#endif /* kernel_collection_builder_h */ diff --git a/dyld3/shared-cache/make_ios_dyld_cache.cpp b/dyld3/shared-cache/make_ios_dyld_cache.cpp deleted file mode 100644 index 20f58c3..0000000 --- a/dyld3/shared-cache/make_ios_dyld_cache.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- - * - * Copyright (c) 2016 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "MachOFile.h" -#include "FileUtils.h" -#include "StringUtils.h" -#include "DyldSharedCache.h" - - - -struct MappedMachOsByCategory -{ - std::string archName; - std::vector dylibsForCache; - std::vector otherDylibsAndBundles; - std::vector mainExecutables; -}; - -static bool verbose = false; - - -static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector& files) -{ - // read start of file to determine if it is mach-o or a fat file - std::string fullPath = buildRootPath + runtimePath; - int fd = ::open(fullPath.c_str(), O_RDONLY); - if ( fd < 0 ) - return false; - bool result = false; - const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if ( wholeFile != MAP_FAILED ) { - Diagnostics diag; - bool usedWholeFile = false; - for (MappedMachOsByCategory& file : files) { - uint64_t sliceOffset = 0; - uint64_t sliceLength = statBuf.st_size; - bool fatButMissingSlice; - const void* slice = MAP_FAILED; - 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()); - mh = (dyld3::MachOFile*)slice; - if ( !mh->isMachO(diag, sliceLength) ) { - ::munmap((void*)slice, sliceLength); - slice = MAP_FAILED; - } - } - } - else if ( !fatButMissingSlice && mh->isMachO(diag, sliceLength) ) { - 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 ) { - 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 ( 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); - } - else { - if ( parser.canBePlacedInDyldCache(runtimePath) ) { - file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); - } - } - result = true; - } - } - } - if ( !usedWholeFile ) - ::munmap((void*)wholeFile, statBuf.st_size); - } - ::close(fd); - return result; -} - - -static bool parsePathsFile(const std::string& filePath, std::vector& paths) { - std::ifstream myfile( filePath ); - if ( myfile.is_open() ) { - std::string line; - while ( std::getline(myfile, line) ) { - size_t pos = line.find('#'); - if ( pos != std::string::npos ) - line.resize(pos); - while ( line.size() != 0 && isspace(line.back()) ) { - line.pop_back(); - } - if ( !line.empty() ) - paths.push_back(line); - } - myfile.close(); - return true; - } - return false; -} - - -static void mapAllFiles(const std::string& dylibsRootDir, const std::vector& paths, dyld3::Platform platform, std::vector& files) -{ - for (const std::string& runtimePath : paths) { - std::string fullPath = dylibsRootDir + runtimePath; - struct stat statBuf; - if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) ) - fprintf(stderr, "could not load: %s\n", fullPath.c_str()); - } -} - - - -inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) -{ - return (uint32_t)(abstime/1000/1000); -} - - -#define TERMINATE_IF_LAST_ARG( s ) \ - do { \ - if ( i == argc - 1 ) { \ - fprintf(stderr, s ); \ - return 1; \ - } \ - } while ( 0 ) - -int main(int argc, const char* argv[]) -{ - std::string rootPath; - std::string dylibListFile; - bool force = false; - std::string cacheDir; - std::string dylibsList; - std::unordered_set archStrs; - - dyld3::Platform platform = dyld3::Platform::iOS; - - // parse command line options - for (int i = 1; i < argc; ++i) { - const char* arg = argv[i]; - if (strcmp(arg, "-debug") == 0) { - verbose = true; - } - else if (strcmp(arg, "-verbose") == 0) { - verbose = true; - } - else if (strcmp(arg, "-tvOS") == 0) { - platform = dyld3::Platform::tvOS; - } - else if (strcmp(arg, "-iOS") == 0) { - platform = dyld3::Platform::iOS; - } - else if (strcmp(arg, "-watchOS") == 0) { - platform = dyld3::Platform::watchOS; - } - else if ( strcmp(arg, "-root") == 0 ) { - TERMINATE_IF_LAST_ARG("-root missing path argument\n"); - rootPath = argv[++i]; - } - else if ( strcmp(arg, "-dylibs_list") == 0 ) { - TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n"); - dylibsList = argv[++i]; - } - else if (strcmp(arg, "-cache_dir") == 0) { - TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); - cacheDir = argv[++i]; - } - else if (strcmp(arg, "-arch") == 0) { - TERMINATE_IF_LAST_ARG("-arch missing argument\n"); - archStrs.insert(argv[++i]); - } - else if (strcmp(arg, "-force") == 0) { - force = true; - } - else { - //usage(); - fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg); - return 1; - } - } - - if ( cacheDir.empty() ) { - fprintf(stderr, "missing -cache_dir option to specify directory in which to write cache file(s)\n"); - return 1; - } - - if ( rootPath.empty() ) { - fprintf(stderr, "missing -runtime_dir option to specify directory which is root of simulator runtime)\n"); - return 1; - } - else { - // canonicalize rootPath - char resolvedPath[PATH_MAX]; - if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { - rootPath = resolvedPath; - } - if ( rootPath.back() != '/' ) - rootPath = rootPath + "/"; - } - - int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); - if ( (err != 0) && (err != EEXIST) ) { - fprintf(stderr, "mkpath_np fail: %d", err); - return 1; - } - - if ( archStrs.empty() ) { - switch ( platform ) { - case dyld3::Platform::iOS: - case dyld3::Platform::tvOS: - archStrs.insert("arm64"); - break; - case dyld3::Platform::watchOS: - archStrs.insert("armv7k"); - archStrs.insert("arm64_32"); - break; - case dyld3::Platform::unknown: - case dyld3::Platform::macOS: - assert(0 && "macOS not support with this tool"); - break; - } - } - - uint64_t t1 = mach_absolute_time(); - - // find all mach-o files for requested architectures - std::vector 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 paths; - parsePathsFile(dylibsList, paths); - mapAllFiles(rootPath, paths, platform, allFileSets); - - uint64_t t2 = mach_absolute_time(); - - fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); - - // build all caches in parallel - __block bool cacheBuildFailure = false; - dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { - const MappedMachOsByCategory& fileSet = allFileSets[index]; - const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName; - - fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size()); - - // build cache new cache file - DyldSharedCache::CreateOptions options; - options.archName = fileSet.archName; - options.platform = platform; - options.excludeLocalSymbols = true; - options.optimizeStubs = false; - options.optimizeObjC = true; - options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ? - DyldSharedCache::Agile : DyldSharedCache::SHA256only; - options.dylibsRemovedDuringMastering = true; - options.inodesAreSameAsRuntime = false; - options.cacheSupportsASLR = true; - options.forSimulator = false; - options.isLocallyBuiltCache = true; - options.verbose = verbose; - options.evictLeafDylibsOnOverflow = false; - DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); - - // print any warnings - for (const std::string& warn : results.warnings) { - fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str()); - } - if ( !results.errorMessage.empty() ) { - // print error (if one) - fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str()); - cacheBuildFailure = true; - } - else { - // save new cache file to disk and write new .map file - assert(results.cacheContent != nullptr); - if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) - cacheBuildFailure = true; - if ( !cacheBuildFailure ) { - std::string mapStr = results.cacheContent->mapFile(); - std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map"; - safeSave(mapStr.c_str(), mapStr.size(), outFileMap); - } - // free created cache buffer - vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); - } - }); - - // we could unmap all input files, but tool is about to quit - - return (cacheBuildFailure ? 1 : 0); -} - diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.cpp b/dyld3/shared-cache/mrm_shared_cache_builder.cpp index 4453375..98a426f 100644 --- a/dyld3/shared-cache/mrm_shared_cache_builder.cpp +++ b/dyld3/shared-cache/mrm_shared_cache_builder.cpp @@ -23,20 +23,22 @@ */ #include "mrm_shared_cache_builder.h" -#include "CacheBuilder.h" +#include "SharedCacheBuilder.h" #include "ClosureFileSystem.h" #include "FileUtils.h" +#include "JSONReader.h" #include #include #include #include #include + 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 +static const uint64_t kMaxBuildVersion = 2; //The maximum version BuildOptions struct we can support static const uint32_t MajorVersion = 1; -static const uint32_t MinorVersion = 0; +static const uint32_t MinorVersion = 2; namespace dyld3 { namespace closure { @@ -97,7 +99,7 @@ public: info.fileContentLen = fileInfo.length; info.sliceOffset = 0; info.sliceLen = fileInfo.length; - info.isSipProtected = false; + info.isOSBinary = true; info.inode = fileInfo.inode; info.mtime = fileInfo.mtime; info.unload = nullptr; @@ -194,7 +196,7 @@ private: struct BuildInstance { std::unique_ptr options; - std::unique_ptr builder; + std::unique_ptr builder; std::vector inputFiles; std::vector errors; std::vector warnings; @@ -203,6 +205,8 @@ struct BuildInstance { uint8_t* cacheData = nullptr; uint64_t cacheSize = 0; std::string jsonMap; + std::string macOSMap; // For compatibility with update_dyld_shared_cache's .map file + std::string macOSMapPath; // Owns the string for the path std::string cdHash; // Owns the data for the cdHash std::string cdHashType; // Owns the data for the cdHashType std::string uuid; // Owns the data for the uuid @@ -214,31 +218,43 @@ struct BuildFileResult { uint64_t size; }; -struct SharedCacheBuilder { - SharedCacheBuilder(const BuildOptions_v1* options); +struct TranslationResult { + const uint8_t* data; + size_t size; + std::string cdHash; + std::string path; + bool bufferWasMalloced; +}; + +struct MRMSharedCacheBuilder { + MRMSharedCacheBuilder(const BuildOptions_v1* options); const BuildOptions_v1* options; dyld3::closure::FileSystemMRM fileSystem; std::string dylibOrderFileData; std::string dirtyDataOrderFileData; + void* objcOptimizationsFileData; + size_t objcOptimizationsFileLength; // An array of builders and their options as we may have more than one builder for a given device variant. std::vector builders; // The paths in all of the caches // We keep this here to own the std::string path data - std::map dylibsInCaches; + std::map> dylibsInCaches; // The results from all of the builders // We keep this in a vector to own the data. - std::vector fileResults; - std::vector fileResultStorage; + std::vector fileResults; + std::vector fileResultStorage; + std::vector> fileResultBuffers; // The results from all of the builders // We keep this in a vector to own the data. std::vector cacheResults; std::vector cacheResultStorage; + // The files to remove. These are in every copy of the caches we built std::vector filesToRemove; @@ -273,11 +289,16 @@ struct SharedCacheBuilder { } }; -SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) { +MRMSharedCacheBuilder::MRMSharedCacheBuilder(const BuildOptions_v1* options) +: options(options) +, lock(PTHREAD_MUTEX_INITIALIZER) +, objcOptimizationsFileData(nullptr) +, objcOptimizationsFileLength(0) +{ } -void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& builder) { +void validiateBuildOptions(const BuildOptions_v1* options, MRMSharedCacheBuilder& builder) { if (options->version < kMinBuildVersion) { builder.error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion); } @@ -294,6 +315,7 @@ void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& b case Disposition::Unknown: case Disposition::InternalDevelopment: case Disposition::Customer: + case Disposition::InternalMinDevelopment: break; default: builder.error("unknown disposition value"); @@ -330,8 +352,8 @@ void getVersion(uint32_t *major, uint32_t *minor) { *minor = MinorVersion; } -struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) { - SharedCacheBuilder* builder = new SharedCacheBuilder(options); +struct MRMSharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) { + MRMSharedCacheBuilder* builder = new MRMSharedCacheBuilder(options); // Check the option struct values are valid validiateBuildOptions(options, *builder); @@ -339,10 +361,10 @@ struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* optio return builder; } -bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) { +bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) { __block bool success = false; builder->runSync(^() { - if (builder->state != SharedCacheBuilder::AcceptingFiles) { + if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) { builder->error("Cannot add file: '%s' as we have already started building", path); return; } @@ -373,6 +395,11 @@ bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data builder->dirtyDataOrderFileData = std::string((char*)data, size); success = true; return; + case ObjCOptimizationsFile: + builder->objcOptimizationsFileData = data; + builder->objcOptimizationsFileLength = size; + success = true; + return; default: builder->error("unknown file flags value"); break; @@ -388,10 +415,10 @@ bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data return success; } -bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath) { +bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, const char* toPath) { __block bool success = false; builder->runSync(^() { - if (builder->state != SharedCacheBuilder::AcceptingFiles) { + if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) { builder->error("Cannot add file: '%s' as we have already started building", fromPath); return; } @@ -415,24 +442,46 @@ bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const return success; } -static bool platformExcludeLocalSymbols(Platform platform) { +static DyldSharedCache::LocalSymbolsMode platformExcludeLocalSymbols(Platform platform) { switch (platform) { case Platform::unknown: case Platform::macOS: - return false; + return DyldSharedCache::LocalSymbolsMode::keep; case Platform::iOS: case Platform::tvOS: case Platform::watchOS: case Platform::bridgeOS: - return true; + return DyldSharedCache::LocalSymbolsMode::unmap; case Platform::iOSMac: case Platform::iOS_simulator: case Platform::tvOS_simulator: case Platform::watchOS_simulator: - return false; + return DyldSharedCache::LocalSymbolsMode::keep; } } +static DyldSharedCache::LocalSymbolsMode excludeLocalSymbols(const BuildOptions_v1* options) { + if ( options->version >= 2 ) { + const BuildOptions_v2* v2 = (const BuildOptions_v2*)options; + if ( v2->optimizeForSize ) + return DyldSharedCache::LocalSymbolsMode::strip; + } + + // Old build options always use the platform default + return platformExcludeLocalSymbols(options->platform); +} + +static bool optimizeDyldDlopens(const BuildOptions_v1* options) { + // Old builds always default to dyld3 optimisations + if ( options->version < 2 ) { + return true; + } + + // If we want to optimize for size instead of speed, then disable dyld3 dlopen closures + const BuildOptions_v2* v2 = (const BuildOptions_v2*)options; + return !v2->optimizeForSize; +} + static DyldSharedCache::CodeSigningDigestMode platformCodeSigningDigestMode(Platform platform) { switch (platform) { case Platform::unknown: @@ -481,19 +530,25 @@ static const char* dispositionName(Disposition disposition) { } } -bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { +// This is a JSON file containing the list of classes for which +// we should try to build IMP caches. +dyld3::json::Node parseObjcOptimizationsFile(Diagnostics& diags, const void* data, size_t length) { + return dyld3::json::readJSON(diags, data, length); +} + +bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) { __block bool success = false; builder->runSync(^() { - if (builder->state != SharedCacheBuilder::AcceptingFiles) { + if (builder->state != MRMSharedCacheBuilder::AcceptingFiles) { builder->error("Builder has already been run"); return; } - builder->state = SharedCacheBuilder::Building; + builder->state = MRMSharedCacheBuilder::Building; if (builder->fileSystem.fileCount() == 0) { builder->error("Cannot run builder with no files"); } - Diagnostics diag; + __block Diagnostics diag; std::vector aliases = builder->fileSystem.getResolvedSymlinks(diag); if (diag.hasError()) { diag.verbose("Symlink resolver error: %s\n", diag.errorMessage().c_str()); @@ -504,42 +559,49 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { return; } - __block std::vector inputFiles; + __block std::vector inputFiles; builder->fileSystem.forEachFileInfo(^(const char* path, FileFlags fileFlags) { - CacheBuilder::InputFile::State state = CacheBuilder::InputFile::Unset; + SharedCacheBuilder::InputFile::State state = SharedCacheBuilder::InputFile::Unset; switch (fileFlags) { case FileFlags::NoFlags: - state = CacheBuilder::InputFile::Unset; + state = SharedCacheBuilder::InputFile::Unset; break; case FileFlags::MustBeInCache: - state = CacheBuilder::InputFile::MustBeIncluded; + state = SharedCacheBuilder::InputFile::MustBeIncluded; break; case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf: - state = CacheBuilder::InputFile::MustBeExcludedIfUnused; + state = SharedCacheBuilder::InputFile::MustBeExcludedIfUnused; break; case FileFlags::RequiredClosure: - state = CacheBuilder::InputFile::MustBeIncluded; + state = SharedCacheBuilder::InputFile::MustBeIncluded; break; case FileFlags::DylibOrderFile: case FileFlags::DirtyDataOrderFile: + case FileFlags::ObjCOptimizationsFile: builder->error("Order files should not be in the file system"); return; } - inputFiles.emplace_back((CacheBuilder::InputFile){ path, state }); + inputFiles.emplace_back((SharedCacheBuilder::InputFile){ path, state }); }); auto addCacheConfiguration = ^(bool isOptimized) { for (uint64_t i = 0; i != builder->options->numArchs; ++i) { + // HACK: Skip i386 for macOS + if ( (builder->options->platform == Platform::macOS) && (strcmp(builder->options->archs[i], "i386") == 0 ) ) + continue; auto options = std::make_unique((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/"; + if ( builder->options->platform == Platform::macOS ) + cacheSuffix = ""; + std::string runtimePath = (builder->options->platform == Platform::macOS) ? MACOSX_MRM_DYLD_SHARED_CACHE_DIR : IPHONE_DYLD_SHARED_CACHE_DIR; options->outputFilePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i] + cacheSuffix; options->outputMapFilePath = options->outputFilePath + ".json"; options->archs = &dyld3::GradedArchs::forName(builder->options->archs[i]); options->platform = (dyld3::Platform)builder->options->platform; - options->excludeLocalSymbols = platformExcludeLocalSymbols(builder->options->platform); + options->localSymbolMode = excludeLocalSymbols(builder->options); options->optimizeStubs = isOptimized; - options->optimizeObjC = true; + options->optimizeDyldDlopens = optimizeDyldDlopens(builder->options); + options->optimizeDyldLaunches = true; options->codeSigningDigestMode = platformCodeSigningDigestMode(builder->options->platform); options->dylibsRemovedDuringMastering = true; options->inodesAreSameAsRuntime = false; @@ -551,8 +613,9 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { options->loggingPrefix = std::string(builder->options->deviceName) + dispositionName(builder->options->disposition) + "." + builder->options->archs[i] + cacheSuffix; options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData); options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData); + options->objcOptimizations = parseObjcOptimizationsFile(diag, builder->objcOptimizationsFileData, builder->objcOptimizationsFileLength); - auto cacheBuilder = std::make_unique(*options.get(), builder->fileSystem); + auto cacheBuilder = std::make_unique(*options.get(), builder->fileSystem); builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles }); } }; @@ -561,8 +624,13 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { switch (builder->options->disposition) { case Disposition::Unknown: case Disposition::InternalDevelopment: - addCacheConfiguration(false); - addCacheConfiguration(true); + // HACK: MRM for the mac should only get development, even if it requested both + if (builder->options->platform == Platform::macOS) { + addCacheConfiguration(false); + } else { + addCacheConfiguration(false); + addCacheConfiguration(true); + } break; case Disposition::Customer: addCacheConfiguration(true); @@ -574,7 +642,7 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { // FIXME: This step can run in parallel. for (auto& buildInstance : builder->builders) { - CacheBuilder* cacheBuilder = buildInstance.builder.get(); + SharedCacheBuilder* cacheBuilder = buildInstance.builder.get(); cacheBuilder->build(buildInstance.inputFiles, aliases); // First put the warnings in to a vector to own them. @@ -599,7 +667,12 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { if (cacheBuilder->errorMessage().empty()) { cacheBuilder->writeBuffer(buildInstance.cacheData, buildInstance.cacheSize); - buildInstance.jsonMap = cacheBuilder->getMapFileBuffer(builder->options->deviceName); + buildInstance.jsonMap = cacheBuilder->getMapFileJSONBuffer(builder->options->deviceName); + if ( buildInstance.options->platform == dyld3::Platform::macOS ) { + // For compatibility with update_dyld_shared_cache, put a .map file next to the shared cache + buildInstance.macOSMap = cacheBuilder->getMapFileBuffer(); + buildInstance.macOSMapPath = buildInstance.options->outputFilePath + ".map"; + } buildInstance.cdHash = cacheBuilder->cdHashFirst(); buildInstance.uuid = cacheBuilder->uuid(); switch (buildInstance.options->codeSigningDigestMode) { @@ -613,14 +686,24 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { buildInstance.cdHashType = "sha1"; break; } + + // Track the dylibs which were included in this cache + cacheBuilder->forEachCacheDylib(^(const std::string &path) { + builder->dylibsInCaches[path.c_str()].insert(&buildInstance); + }); + cacheBuilder->forEachCacheSymlink(^(const std::string &path) { + builder->dylibsInCaches[path.c_str()].insert(&buildInstance); + }); } + // Free the cache builder now so that we don't keep too much memory resident + cacheBuilder->deleteBuffer(); + buildInstance.builder.reset(); } + // Now that we have run all of the builds, collect the results // First push file results for each of the shared caches we built for (auto& buildInstance : builder->builders) { - CacheBuilder* cacheBuilder = buildInstance.builder.get(); - CacheResult cacheBuildResult; cacheBuildResult.version = 1; cacheBuildResult.loggingPrefix = buildInstance.options->loggingPrefix.c_str(); @@ -634,7 +717,7 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { builder->cacheResultStorage.emplace_back(cacheBuildResult); - if (!cacheBuilder->errorMessage().empty()) + if (!buildInstance.errors.empty()) continue; FileResult cacheFileResult; @@ -647,11 +730,23 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { cacheFileResult.hashType = buildInstance.cdHashType.c_str(); cacheFileResult.hash = buildInstance.cdHash.c_str(); + builder->fileResultBuffers.push_back({ builder->fileResultStorage.size(), true }); builder->fileResultStorage.emplace_back(cacheFileResult); - cacheBuilder->forEachCacheDylib(^(const std::string &path) { - ++builder->dylibsInCaches[path.c_str()]; - }); + // Add a file result for the .map file + if ( !buildInstance.macOSMap.empty() ) { + FileResult cacheFileResult; + cacheFileResult.version = 1; + cacheFileResult.path = buildInstance.macOSMapPath.c_str(); + cacheFileResult.behavior = AddFile; + cacheFileResult.data = (const uint8_t*)buildInstance.macOSMap.data(); + cacheFileResult.size = buildInstance.macOSMap.size(); + cacheFileResult.hashArch = buildInstance.options->archs->name(); + cacheFileResult.hashType = buildInstance.cdHashType.c_str(); + cacheFileResult.hash = buildInstance.cdHash.c_str(); + + builder->fileResultStorage.emplace_back(cacheFileResult); + } } // Copy from the storage to the vector we can return to the API. @@ -664,59 +759,112 @@ bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) { // 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 : builder->dylibsInCaches) { - if (dylibAndCount.second == numCaches) { - builder->filesToRemove.push_back(dylibAndCount.first.c_str()); + const char* pathToRemove = dylibAndCount.first.c_str(); + + if ( builder->options->platform == Platform::macOS ) { + // macOS has to leave the simulator support binaries on disk + if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_kernel.dylib") == 0 ) + continue; + if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_platform.dylib") == 0 ) + continue; + if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_pthread.dylib") == 0 ) + continue; + } + + if (dylibAndCount.second.size() == numCaches) { + builder->filesToRemove.push_back(pathToRemove); + } else { + // File is not in every cache, so likely has perhaps only x86_64h slice + // but we built both x86_64 and x86_64h caches. + // We may still delete it if its in all caches it's eligible for, ie, we + // assume the cache builder knows about all possible arch's on the system and + // can delete anything it knows can't run + bool canDeletePath = true; + for (auto& buildInstance : builder->builders) { + if ( dylibAndCount.second.count(&buildInstance) != 0 ) + continue; + // This builder didn't get this image. See if the image was ineligible + // based on slide, ie, that dyld at runtime couldn't load this anyway, so + // so removing it from disk won't hurt + Diagnostics loaderDiag; + const dyld3::GradedArchs* archs = buildInstance.options->archs; + dyld3::Platform platform = buildInstance.options->platform; + char realerPath[MAXPATHLEN]; + dyld3::closure::LoadedFileInfo fileInfo = dyld3::MachOAnalyzer::load(loaderDiag, builder->fileSystem, + pathToRemove, *archs, platform, realerPath); + if ( (platform == dyld3::Platform::macOS) && loaderDiag.hasError() ) { + // Try again with iOSMac + loaderDiag.clearError(); + fileInfo = dyld3::MachOAnalyzer::load(loaderDiag, builder->fileSystem, + pathToRemove, *archs, dyld3::Platform::iOSMac, realerPath); + } + + // We don't need the file content now, as we only needed to know if this file could be loaded + builder->fileSystem.unloadFile(fileInfo); + + if ( loaderDiag.hasError() || (fileInfo.fileContent == nullptr) ) { + // This arch/platform combination couldn't load this path, so we can remove it + continue; + } + + // This arch was compatible, so the dylib was rejected from this cache for some other reason, eg, + // cache overflow. We need to keep it on-disk + canDeletePath = false; + break; + } + if ( canDeletePath ) + builder->filesToRemove.push_back(pathToRemove); } } // Quit if we had any errors. for (auto& buildInstance : builder->builders) { - CacheBuilder* cacheBuilder = buildInstance.builder.get(); - if (!cacheBuilder->errorMessage().empty()) + if (!buildInstance.errors.empty()) return; } - builder->state = SharedCacheBuilder::FinishedBuilding; + builder->state = MRMSharedCacheBuilder::FinishedBuilding; success = true; }); return success; } -const char* const* getErrors(const struct SharedCacheBuilder* builder, uint64_t* errorCount) { +const char* const* getErrors(const struct MRMSharedCacheBuilder* builder, uint64_t* errorCount) { if (builder->errors.empty()) return nullptr; *errorCount = builder->errors.size(); return builder->errors.data(); } -const struct FileResult* const* getFileResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) { +const struct FileResult* const* getFileResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount) { if (builder->fileResults.empty()) return nullptr; *resultCount = builder->fileResults.size(); return builder->fileResults.data(); } -const struct CacheResult* const* getCacheResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) { +const struct CacheResult* const* getCacheResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount) { if (builder->cacheResults.empty()) return nullptr; *resultCount = builder->cacheResults.size(); return builder->cacheResults.data(); } -const char* const* getFilesToRemove(const struct SharedCacheBuilder* builder, uint64_t* fileCount) { +const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder* builder, uint64_t* fileCount) { if (builder->filesToRemove.empty()) return nullptr; *fileCount = builder->filesToRemove.size(); return builder->filesToRemove.data(); } -void destroySharedCacheBuilder(struct SharedCacheBuilder* builder) { - for (auto& buildInstance : builder->builders) { - CacheBuilder* cacheBuilder = buildInstance.builder.get(); - cacheBuilder->deleteBuffer(); - } - for (auto &fileResult : builder->fileResultStorage) { - free((void*)fileResult.data); +void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder) { + for (auto &indexAndIsDataMalloced : builder->fileResultBuffers) { + FileResult& fileResult = builder->fileResultStorage[indexAndIsDataMalloced.first]; + if (indexAndIsDataMalloced.second) { + free((void*)fileResult.data); + } else { + vm_deallocate(mach_task_self(), (vm_address_t)fileResult.data, fileResult.size); + } fileResult.data = nullptr; } delete builder; diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.h b/dyld3/shared-cache/mrm_shared_cache_builder.h index 33c4a69..e566fc0 100644 --- a/dyld3/shared-cache/mrm_shared_cache_builder.h +++ b/dyld3/shared-cache/mrm_shared_cache_builder.h @@ -42,7 +42,7 @@ enum Platform { tvOS = 3, // PLATFORM_TVOS watchOS = 4, // PLATFORM_WATCHOS bridgeOS = 5, // PLATFORM_BRIDGEOS - iOSMac = 6, // PLATFORM_IOSMAC + iOSMac = 6, // PLATFORM_MACCATALYST iOS_simulator = 7, // PLATFORM_IOSIMULATOR tvOS_simulator = 8, // PLATFORM_TVOSSIMULATOR watchOS_simulator = 9 // PLATFORM_WATCHOSSIMULATOR @@ -66,7 +66,8 @@ enum FileFlags // These are for the order files DylibOrderFile = 100, - DirtyDataOrderFile = 101 + DirtyDataOrderFile = 101, + ObjCOptimizationsFile = 102, }; struct BuildOptions_v1 @@ -82,6 +83,22 @@ struct BuildOptions_v1 bool isLocallyBuiltCache; }; +// This is available when getVersion() returns 1.2 or higher +struct BuildOptions_v2 +{ + uint64_t version; // Future proofing, set to 2 + 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; + // Added in v2 + bool optimizeForSize; +}; + enum FileBehavior { AddFile = 0, // New file: uid, gid, mode, data, cdhash fields must be set @@ -115,38 +132,38 @@ struct CacheResult const char* mapJSON; }; -struct SharedCacheBuilder; +struct MRMSharedCacheBuilder; __API_AVAILABLE(macos(10.12)) void getVersion(uint32_t *major, uint32_t *minor); __API_AVAILABLE(macos(10.12)) -struct SharedCacheBuilder* createSharedCacheBuilder(const struct BuildOptions_v1* options); +struct MRMSharedCacheBuilder* 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); +bool addFile(struct MRMSharedCacheBuilder* 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); +bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, const char* toPath); __API_AVAILABLE(macos(10.12)) -bool runSharedCacheBuilder(struct SharedCacheBuilder* builder); +bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder); __API_AVAILABLE(macos(10.12)) -const char* const* getErrors(const struct SharedCacheBuilder* builder, uint64_t* errorCount); +const char* const* getErrors(const struct MRMSharedCacheBuilder* builder, uint64_t* errorCount); __API_AVAILABLE(macos(10.12)) -const struct FileResult* const* getFileResults(struct SharedCacheBuilder* builder, uint64_t* resultCount); +const struct FileResult* const* getFileResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount); __API_AVAILABLE(macos(10.12)) -const struct CacheResult* const* getCacheResults(struct SharedCacheBuilder* builder, uint64_t* resultCount); +const struct CacheResult* const* getCacheResults(struct MRMSharedCacheBuilder* builder, uint64_t* resultCount); __API_AVAILABLE(macos(10.12)) -const char* const* getFilesToRemove(const struct SharedCacheBuilder* builder, uint64_t* fileCount); +const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder* builder, uint64_t* fileCount); __API_AVAILABLE(macos(10.12)) -void destroySharedCacheBuilder(struct SharedCacheBuilder* builder); +void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder); #ifdef __cplusplus } diff --git a/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm b/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm deleted file mode 100644 index 49e58a2..0000000 --- a/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm +++ /dev/null @@ -1,282 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- - * - * Copyright (c) 2016 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "Manifest.h" -#include "FileUtils.h" -#include "BuilderUtils.h" - -#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL - -#if !__has_feature(objc_arc) -#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks -#endif - -static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.cache-builder.build", DISPATCH_QUEUE_CONCURRENT); - -#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/" - -void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables) -{ - auto copy_state = copyfile_state_alloc(); - mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755); - (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); - - if (!manifest.dylibOrderFile().empty()) { - (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); - } - - if (!manifest.dirtyDataOrderFile().empty()) { - (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); - } - - std::set uuids; - std::map copy_pairs; - - manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) { - manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) { - auto results = manifest.configuration(config).architecture(arch).results; - for (const auto& image : results.dylibs) { - uuids.insert(image.first); - } - for (const auto& image : results.bundles) { - uuids.insert(image.first); - } - for (const auto& image : results.executables) { - uuids.insert(image.first); - } - }); - }); - - for (auto& uuid : uuids) { - auto buildPath = manifest.buildPathForUUID(uuid); - auto installPath = manifest.runtimePathForUUID(uuid); - assert(!buildPath.empty() && !installPath.empty()); - copy_pairs.insert(std::make_pair(installPath, buildPath)); - } - - for (const auto& copy_pair : copy_pairs) { - std::string from = realPath(copy_pair.second); - std::string to = dylibCachePath + "/Root/" + copy_pair.first; - mkpath_np(dirPath(to).c_str(), 0755); - int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); - diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str()); - - } - copyfile_state_free(copy_state); - - fprintf(stderr, "[Artifact] dylibs copied\n"); -} - -void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest) -{ - manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt"); - manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt"); - manifest.setMetabomFile("./Metadata/metabom.bom"); - - for (auto& projects : manifest.projects()) { - manifest.addProjectSource(projects.first, "./Root", true); - } -} - -int main(int argc, const char* argv[]) -{ - @autoreleasepool { - __block Diagnostics diags; - bool verbose = false; - std::string masterDstRoot; - std::string dylibCacheDir; - std::string resultPath; - std::string manifestPath; - bool preflight = false; - __block bool allBuildsSucceeded = true; - bool skipWrites = false; - bool skipBuilds = false; - bool agileChooseSHA256CdHash = false; - time_t mytime = time(0); - fprintf(stderr, "Started: %s", asctime(localtime(&mytime))); - - // parse command line options - for (int i = 1; i < argc; ++i) { - const char* arg = argv[i]; - if (arg[0] == '-') { - if (strcmp(arg, "-debug") == 0) { - verbose = true; - diags = Diagnostics(true); - } else if (strcmp(arg, "-skip_writes") == 0) { - skipWrites = true; - } else if (strcmp(arg, "-skip_builds") == 0) { - skipBuilds = true; - } else if (strcmp(arg, "-delete_writes") == 0) { - skipWrites = true; - } else if (strcmp(arg, "-dylib_cache") == 0) { - dylibCacheDir = argv[++i]; - } else if (strcmp(arg, "-preflight") == 0) { - preflight = true; - skipWrites = true; - } else if (strcmp(arg, "-master_dst_root") == 0) { - masterDstRoot = argv[++i]; - if (masterDstRoot.empty()) { - diags.error("-master_dst_root missing path argument"); - } - } else if (strcmp(arg, "-results") == 0) { - resultPath = argv[++i]; - } else if (strcmp(arg, "-plist") == 0) { - manifestPath = argv[++i]; - } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) { - agileChooseSHA256CdHash = true; - } else { - // usage(); - diags.error("unknown option: %s", arg); - } - } else { - manifestPath = argv[i]; - } - } - - if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) { - skipBuilds = true; - } - - if (diags.hasError()) { - printf("%s\n", diags.errorMessage().c_str()); - exit(-1); - } - - dispatch_async(dispatch_get_main_queue(), ^{ - if (manifestPath.empty()) { - fprintf(stderr, "mainfest path argument is required\n"); - exit(-1); - } - - (void)chdir(dirname(strdup(manifestPath.c_str()))); - __block auto manifest = dyld3::Manifest(diags, manifestPath); - - if (manifest.build().empty()) { - fprintf(stderr, "No version found in manifest\n"); - exit(-1); - } - - fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str()); - - if (masterDstRoot.empty()) { - fprintf(stderr, "-master_dst_root required path argument\n"); - exit(-1); - } - - if (manifest.version() < 4) { - fprintf(stderr, "must specify valid manifest file\n"); - exit(-1); - } - - struct rlimit rl = { OPEN_MAX, OPEN_MAX }; - (void)setrlimit(RLIMIT_NOFILE, &rl); - - manifest.calculateClosure(); - - if (!skipWrites && !skipBuilds) { - (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755); - dispatch_group_async(buildGroup(), build_queue, ^{ - createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true); - }); - } - - if (!dylibCacheDir.empty()) { - dispatch_group_async(buildGroup(), build_queue, ^{ - createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false); - }); - } - - if (!skipBuilds) { - dispatch_group_async(buildGroup(), build_queue, ^{ - makeBoms(manifest, masterDstRoot); - }); - allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites, - agileChooseSHA256CdHash, true, false); - } - - manifest.write(resultPath); - - addArtifactPaths(diags, manifest); - if (!dylibCacheDir.empty()) { - manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist"); - manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json"); - } - - if (!skipWrites) { - mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755); - auto copy_state = copyfile_state_alloc(); - (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL); - copyfile_state_free(copy_state); - manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist"); - manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json"); - } - - dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER); - time_t mytime = time(0); - fprintf(stderr, "Finished: %s", asctime(localtime(&mytime))); - - if (preflight && !allBuildsSucceeded) { - exit(-1); - } - - exit(0); - }); - } - dispatch_main(); - return 0; -} diff --git a/dyld3/shared-cache/update_dyld_shared_cache.cpp b/dyld3/shared-cache/update_dyld_shared_cache.cpp index df0644d..dd5801f 100644 --- a/dyld3/shared-cache/update_dyld_shared_cache.cpp +++ b/dyld3/shared-cache/update_dyld_shared_cache.cpp @@ -25,9 +25,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -40,997 +37,11 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "FileUtils.h" -#include "StringUtils.h" -#include "DyldSharedCache.h" -#include "MachOFile.h" -#include "MachOAnalyzer.h" -#include "ClosureFileSystemPhysical.h" - -struct MappedMachOsByCategory -{ - const dyld3::GradedArchs& archs; - std::vector dylibsForCache; - std::vector otherDylibsAndBundles; - std::vector mainExecutables; - std::unordered_set badZippered; -}; - -static const char* sAllowedPrefixes[] = { - "/bin/", - "/sbin/", - "/usr/", - "/System/", - "/Library/Apple/System/", - "/Library/Apple/usr/", - "/System/Applications/App Store.app/", - "/System/Applications/Automator.app/", - "/System/Applications/Calculator.app/", - "/System/Applications/Calendar.app/", - "/System/Applications/Chess.app/", - "/System/Applications/Contacts.app/", - "/System/Applications/Dashboard.app/", - "/System/Applications/Dictionary.app/", - "/System/Applications/FaceTime.app/", - "/System/Applications/Font Book.app/", - "/System/Applications/Image Capture.app/", - "/System/Applications/Launchpad.app/", - "/System/Applications/Mail.app/", - "/System/Applications/Maps.app/", - "/System/Applications/Messages.app/", - "/System/Applications/Mission Control.app/", - "/System/Applications/Notes.app/", - "/System/Applications/Photo Booth.app/", - "/System/Applications/Preview.app/", - "/System/Applications/QuickTime Player.app/", - "/System/Applications/Reminders.app/", - "/Applications/Safari.app/", - "/System/Applications/Siri.app/", - "/System/Applications/Stickies.app/", - "/System/Applications/System Preferences.app/", - "/System/Applications/TextEdit.app/", - "/System/Applications/Time Machine.app/", - "/System/Applications/iBooks.app/", - "/System/Applications/iTunes.app/", - "/System/Applications/Utilities/Activity Monitor.app", - "/System/Applications/Utilities/AirPort Utility.app", - "/System/Applications/Utilities/Audio MIDI Setup.app", - "/System/Applications/Utilities/Bluetooth File Exchange.app", - "/System/Applications/Utilities/Boot Camp Assistant.app", - "/System/Applications/Utilities/ColorSync Utility.app", - "/System/Applications/Utilities/Console.app", - "/System/Applications/Utilities/Digital Color Meter.app", - "/System/Applications/Utilities/Disk Utility.app", - "/System/Applications/Utilities/Grab.app", - "/System/Applications/Utilities/Grapher.app", - "/System/Applications/Utilities/Keychain Access.app", - "/System/Applications/Utilities/Migration Assistant.app", - "/System/Applications/Utilities/Script Editor.app", - "/System/Applications/Utilities/System Information.app", - "/System/Applications/Utilities/Terminal.app", - "/System/Applications/Utilities/VoiceOver Utility.app", - "/Library/CoreMediaIO/Plug-Ins/DAL/" // temp until plugins moved or closured working -}; - -static const char* sDontUsePrefixes[] = { - "/usr/share", - "/usr/local/", - "/System/Library/Assets", - "/System/Library/StagedFrameworks", - "/Library/Apple/System/Library/StagedFrameworks", - "/System/Library/Kernels/", - "/bin/zsh", // until is fixed - "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker", // these load third party plugins - "/usr/bin/mdimport", // these load third party plugins -}; - - -static bool verbose = false; - - - - -static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, - bool requireSIP, dev_t rootFS, std::vector& 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; - if ( startsWith(runtimePath, "/usr/lib/system/introspection/") ) - return false; - -#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER - // Only use files on the same volume as the boot volume - if (statBuf.st_dev != rootFS) { - if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: warning: skipping overlay file '%s' which is not on the root volume\n", runtimePath.c_str()); - return false; - } -#endif - - 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; - for (MappedMachOsByCategory& file : files) { - Diagnostics diag; - char realerPath[MAXPATHLEN]; - dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, dyld3::Platform::macOS, realerPath); - if (diag.hasError() ) { - // Try again with iOSMac - diag.clearError(); - loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, dyld3::Platform::iOSMac, realerPath); - } - const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent; - if ( ma != nullptr ) { - bool issetuid = false; - const uint64_t sliceLen = loadedFileInfo.sliceLen; - const bool isSipProtected = loadedFileInfo.isSipProtected; - if ( ma->isDynamicExecutable() ) { - // When SIP enabled, only build closures for SIP protected programs - if ( !requireSIP || isSipProtected ) { - //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, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino); - } - } - else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) { - if (verbose) - fprintf(stderr, "update_dyld_shared_cache: warning dylib located at '%s' cannot be placed in cache because: %s\n", runtimePath.c_str(), msg); - }) ) { - // when SIP is enabled, only dylib protected by SIP can go in cache - if ( !requireSIP || isSipProtected ) - file.dylibsForCache.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino); - else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) ) - file.otherDylibsAndBundles.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.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 ( dyld3::MachOFile::isSharedCacheEligiblePath(runtimePath.c_str()) ) - fprintf(stderr, "update_dyld_shared_cache: warning @rpath install name for system framework: %s\n", runtimePath.c_str()); - } - } - if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) ) { - // Only add a dlopen closure for objc trampolines. The rest should have been shared cache eligible. - bool addClosure = false; - if ( ma->isDylib() ) { - std::string installName = ma->installName(); - addClosure = installName == "/usr/lib/libobjc-trampolines.dylib"; - } else { - addClosure = true; - } - if (addClosure) - file.otherDylibsAndBundles.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino); - } - } - result = true; - } - } - - return result; -} - -static void findAllFiles(const dyld3::closure::FileSystem& fileSystem, const std::vector& pathPrefixes, - bool requireSIP, dev_t rootFS, std::vector& files) -{ - std::unordered_set skipDirs; - for (const char* s : sDontUsePrefixes) - skipDirs.insert(s); - - __block std::unordered_set alreadyUsed; - bool multiplePrefixes = (pathPrefixes.size() > 1); - for (const std::string& prefix : pathPrefixes) { - // get all files from overlay for this search dir - for (const char* searchDir : sAllowedPrefixes ) { - iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) { - // ignore files that don't have 'x' bit set (all runnable mach-o files do) - const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH); - if ( !hasXBit && !endsWith(path, ".dylib") ) - return; - - // ignore files too small - if ( statBuf.st_size < 0x3000 ) - return; - - // don't add paths already found using previous prefix - if ( multiplePrefixes && (alreadyUsed.count(path) != 0) ) - return; - - // if the file is mach-o, add to list - if ( addIfMachO(fileSystem, path, statBuf, requireSIP, rootFS, files) ) { - if ( multiplePrefixes ) - alreadyUsed.insert(path); - } - }); - } - } -} - -static const char* sReceiptLocations[] = { - "/System/Library/Receipts", - "/Library/Apple/System/Library/Receipts" -}; - -static void findOSFilesViaBOMS(const dyld3::closure::FileSystem& fileSystem, const std::vector& pathPrefixes, - bool requireSIP, dev_t rootFS, std::vector& files) -{ - __block std::unordered_set runtimePathsFound; - __block bool foundUsableBom = false; - for (const std::string& prefix : pathPrefixes) { - for (const char* dirToIterate : sReceiptLocations ) { - iterateDirectoryTree(prefix, dirToIterate, ^(const std::string&) { return false; }, ^(const std::string& path, const struct stat& statBuf) { - if ( !contains(path, "com.apple.pkg.") ) - return; - if ( !endsWith(path, ".bom") ) - return; - std::string fullPath = prefix + path; - BOMBom bom = BOMBomOpenWithSys(fullPath.c_str(), false, NULL); - if ( bom == nullptr ) - return; - BOMFSObject rootFso = BOMBomGetRootFSObject(bom); - if ( rootFso == nullptr ) { - BOMBomFree(bom); - return; - } - BOMBomEnumerator e = BOMBomEnumeratorNew(bom, rootFso); - if ( e == nullptr ) { - fprintf(stderr, "Can't get enumerator for BOM root FSObject\n"); - return; - } - BOMFSObjectFree(rootFso); - //fprintf(stderr, "using BOM %s\n", path.c_str()); - foundUsableBom = true; - while (BOMFSObject fso = BOMBomEnumeratorNext(e)) { - if ( BOMFSObjectIsBinaryObject(fso) ) { - const char* runPath = BOMFSObjectPathName(fso); - if ( (runPath[0] == '.') && (runPath[1] == '/') ) - ++runPath; - // update_dyld_shared_cache needs to fold away /S/L/Templates/Data - if (strncmp(runPath, "/System/Library/Templates/Data/", 31) == 0 ) - runPath = &runPath[30]; - if ( runtimePathsFound.count(runPath) == 0 ) { - // only add files from sAllowedPrefixes and not in sDontUsePrefixes - bool inSearchDir = false; - for (const char* searchDir : sAllowedPrefixes ) { - if ( strncmp(searchDir, runPath, strlen(searchDir)) == 0 ) { - inSearchDir = true; - break; - } - } - if ( inSearchDir ) { - bool inSkipDir = false; - for (const char* skipDir : sDontUsePrefixes) { - if ( strncmp(skipDir, runPath, strlen(skipDir)) == 0 ) { - inSkipDir = true; - break; - } - } - if ( !inSkipDir ) { - for (const std::string& prefix2 : pathPrefixes) { - struct stat statBuf2; - std::string fullPath2 = prefix2 + runPath; - if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) { - if ( addIfMachO(fileSystem, runPath, statBuf2, requireSIP, rootFS, files) ) { - runtimePathsFound.insert(runPath); - break; - } - } - } - } - } - } - } - BOMFSObjectFree(fso); - } - - BOMBomEnumeratorFree(e); - BOMBomFree(bom); - }); - } - } - - if (!foundUsableBom) - fprintf(stderr, "update_dyld_shared_cache: warning: No usable BOM files were found in '/System/Library/Receipts'\n"); -} - - -static bool dontCache(const std::string& volumePrefix, const std::string& archName, - const std::unordered_set& pathsWithDuplicateInstallName, - const std::unordered_set& badZippered, - const DyldSharedCache::MappedMachO& aFile, bool warn, - const std::unordered_set& skipDylibs) -{ - if ( skipDylibs.count(aFile.runtimePath) ) - return true; - if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/QuickTime/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/Tcl/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/Perl/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/MonitorPanels/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/Accessibility/") ) - return true; - if ( startsWith(aFile.runtimePath, "/usr/local/") ) - return true; - - // anything inside a .app bundle is specific to app, so should not be in shared cache - if ( aFile.runtimePath.find(".app/") != std::string::npos ) - return true; - - if ( archName == "i386" ) { - if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") ) - return true; - if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") ) - return true; - } - - if ( aFile.runtimePath.find("//") != std::string::npos ) { - if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); - return true; - } - - const char* installName = aFile.mh->installName(); - if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) { - // if a dylib moves and a symlink is installed into its place, bom iterator will see both and issue a warning - struct stat statBuf; - bool isSymLink = ( (lstat(aFile.runtimePath.c_str(), &statBuf) == 0) && S_ISLNK(statBuf.st_mode) ); - if (!isSymLink && warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); - return true; - } - - if (badZippered.count(aFile.runtimePath)) { - return true; - } - - if ( aFile.runtimePath != installName ) { - // see if install name is a symlink to actual path - std::string fullInstall = volumePrefix + installName; - char resolvedPath[PATH_MAX]; - if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) { - std::string resolvedSymlink = resolvedPath; - if ( !volumePrefix.empty() ) { - resolvedSymlink = resolvedSymlink.substr(volumePrefix.size()); - } - if ( aFile.runtimePath == resolvedSymlink ) { - return false; - } - } - // 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; - } - return false; -} - -static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set& skipDylibs, - MappedMachOsByCategory& fileSet, bool warn) -{ - std::unordered_set pathsWithDuplicateInstallName; - - std::unordered_map installNameToFirstPath; - for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { - const char* installName = aFile.mh->installName(); - auto pos = installNameToFirstPath.find(installName); - if ( pos == installNameToFirstPath.end() ) { - installNameToFirstPath[installName] = aFile.runtimePath; - } - else { - pathsWithDuplicateInstallName.insert(aFile.runtimePath); - pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]); - } - } - - std::unordered_map macOSPathToTwinPath; - for (const auto& entry : installNameToFirstPath) { - if ( startsWith(entry.first, "/System/iOSSupport/") ) { - std::string tail = entry.first.substr(18); - if ( installNameToFirstPath.count(tail) != 0 ) { - macOSPathToTwinPath.insert({ tail, entry.first }); - } - } - } - - for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { - if ( aFile.mh->isZippered() ) { - aFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { - auto macOSAndTwinPath = macOSPathToTwinPath.find(loadPath); - if ( macOSAndTwinPath != macOSPathToTwinPath.end() ) { - if ( warn ) { - fprintf(stderr, "update_dyld_shared_cache: warning: evicting UIKitForMac binary: %s as it is linked by zippered binary %s\n", - macOSAndTwinPath->second.c_str(), aFile.runtimePath.c_str()); - } - fileSet.badZippered.insert(macOSAndTwinPath->second); - } - }); - } - } - - for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { - if ( dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, fileSet.badZippered, aFile, true, skipDylibs) ){ - // don't build dlopen closures for symlinks to something in the dyld cache - if ( pathsWithDuplicateInstallName.count(aFile.runtimePath) == 0 ) - fileSet.otherDylibsAndBundles.push_back(aFile); - } - } - fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(), - [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, fileSet.badZippered, aFile, false, skipDylibs); }), - fileSet.dylibsForCache.end()); -} - -static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet) -{ - // other OS dylibs should not contain dylibs that are embedded in some .app bundle - fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(), - [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }), - fileSet.otherDylibsAndBundles.end()); -} -static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCategory& fileSet) -{ - // don't build closures for xcode shims in /usr/bin (e.g. /usr/bin/clang) which re-exec themselves to a tool inside Xcode.app - fileSet.mainExecutables.erase(std::remove_if(fileSet.mainExecutables.begin(), fileSet.mainExecutables.end(), - [&](const DyldSharedCache::MappedMachO& aFile) { - if ( !startsWith(aFile.runtimePath, "/usr/bin/") ) - return false; - __block bool isXcodeShim = false; - 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; - }); - return isXcodeShim; - }), fileSet.mainExecutables.end()); -} - -static bool existingCacheUpToDate(const std::string& existingCache, const std::vector& currentDylibs) -{ - // if no existing cache, it is not up-to-date - int fd = ::open(existingCache.c_str(), O_RDONLY); - if ( fd < 0 ) - return false; - struct stat statbuf; - if ( ::fstat(fd, &statbuf) == -1 ) { - ::close(fd); - return false; - } - - // build map of found dylibs - std::unordered_map currentDylibMap; - for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { - //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str()); - currentDylibMap[aFile.runtimePath] = &aFile; - } - - // make sure all dylibs in existing cache have same mtime and inode as found dylib - __block bool foundMismatch = false; - const uint64_t cacheMapLen = statbuf.st_size; - void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0); - if ( p != MAP_FAILED ) { - const DyldSharedCache* cache = (DyldSharedCache*)p; - cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) { - bool foundMatch = false; - auto pos = currentDylibMap.find(installName); - if ( pos != currentDylibMap.end() ) { - const DyldSharedCache::MappedMachO* foundDylib = pos->second; - if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) { - foundMatch = true; - } - } - if ( !foundMatch ) { - // use slow path and look for any dylib with a matching inode and mtime - bool foundSlow = false; - for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { - if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) { - foundSlow = true; - break; - } - } - if ( !foundSlow ) { - foundMismatch = true; - if ( verbose ) - fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName); - } - } - }); - ::munmap(p, cacheMapLen); - } - - ::close(fd); - - return !foundMismatch; -} - - -inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) -{ - return (uint32_t)(abstime/1000/1000); -} - -static bool runningOnHaswell() -{ - // check system is capable of running x86_64h code - struct host_basic_info info; - mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - mach_port_t hostPort = mach_host_self(); - kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); - mach_port_deallocate(mach_task_self(), hostPort); - - return ( (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H) ); -} - -#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER -static std::string currentToolRealPath() -{ - 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 ) - return resolvedCurToolPath; - else - return curToolPath; - } - return "/usr/bin/update_dyld_shared_cache"; -} -#endif - -#define TERMINATE_IF_LAST_ARG( s ) \ - do { \ - if ( i == argc - 1 ) { \ - fprintf(stderr, s ); \ - return 1; \ - } \ - } while ( 0 ) int main(int argc, const char* argv[], const char* envp[]) { - std::string rootPath; - std::string overlayPath; - std::string dylibListFile; - bool universal = false; - bool force = false; - bool searchDisk = false; - bool dylibsRemoved = false; - std::string cacheDir; - std::unordered_set archStrs; - std::unordered_set skipDylibs; - - // parse command line options - for (int i = 1; i < argc; ++i) { - const char* arg = argv[i]; - if (strcmp(arg, "-debug") == 0) { - verbose = true; - } - else if (strcmp(arg, "-verbose") == 0) { - verbose = true; - } - else if (strcmp(arg, "-dont_map_local_symbols") == 0) { - //We are going to ignore this - } - else if (strcmp(arg, "-dylib_list") == 0) { - TERMINATE_IF_LAST_ARG("-dylib_list missing argument"); - dylibListFile = argv[++i]; - } - else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) { - TERMINATE_IF_LAST_ARG("-root missing path argument\n"); - rootPath = argv[++i]; - } - else if (strcmp(arg, "-overlay") == 0) { - TERMINATE_IF_LAST_ARG("-overlay missing path argument\n"); - overlayPath = argv[++i]; - } - else if (strcmp(arg, "-cache_dir") == 0) { - TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); - cacheDir = argv[++i]; - } - else if (strcmp(arg, "-arch") == 0) { - TERMINATE_IF_LAST_ARG("-arch missing argument\n"); - archStrs.insert(argv[++i]); - } - else if (strcmp(arg, "-search_disk") == 0) { - searchDisk = true; - } - else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) { - dylibsRemoved = true; - } - else if (strcmp(arg, "-force") == 0) { - force = true; - } - else if (strcmp(arg, "-sort_by_name") == 0) { - //No-op, we always do this now - } - else if (strcmp(arg, "-universal_boot") == 0) { - universal = true; - } - else if (strcmp(arg, "-skip") == 0) { - TERMINATE_IF_LAST_ARG("-skip missing argument\n"); - skipDylibs.insert(argv[++i]); - } - else { - //usage(); - fprintf(stderr, "update_dyld_shared_cache: unknown option: %s\n", arg); - return 1; - } - } - - if ( !rootPath.empty() & !overlayPath.empty() ) { - fprintf(stderr, "-root and -overlay cannot be used together\n"); - return 1; - } - // canonicalize rootPath - if ( !rootPath.empty() ) { - char resolvedPath[PATH_MAX]; - if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { - rootPath = resolvedPath; - } - // when building closures for boot volume, pathPrefixes should be empty - if ( rootPath == "/" ) { - rootPath = ""; - } - } - // canonicalize overlayPath - if ( !overlayPath.empty() ) { - char resolvedPath[PATH_MAX]; - if ( realpath(overlayPath.c_str(), resolvedPath) != NULL ) { - overlayPath = resolvedPath; - } - } - -#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER - // update_dyld_shared_cache -root should re-exec() itself to a newer version - std::string newTool; - if ( !rootPath.empty() ) - newTool = rootPath + "/usr/bin/update_dyld_shared_cache_root_mode"; - 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 ) { - // don't re-exec if we are already running that tool - if ( newTool != currentToolRealPath() ) { - argv[0] = newTool.c_str(); - execve(newTool.c_str(), (char**)argv, (char**)envp); - fprintf(stderr, "update_dyld_shared_cache: error: could not find '%s/usr/bin/update_dyld_shared_cache_root_mode' in target volume\n", rootPath.c_str()); - return 1; - } - } - if ( !rootPath.empty() ) { - // could be old macOS dmg, try old tool name - newTool = rootPath + "/usr/bin/update_dyld_shared_cache"; - if ( stat(newTool.c_str(), &newToolStatBuf) == 0 ) { - // don't re-exec if we are already running that tool - if ( newTool != currentToolRealPath() ) { - argv[0] = newTool.c_str(); - execve(newTool.c_str(), (char**)argv, (char**)envp); - } - } - fprintf(stderr, "update_dyld_shared_cache: error: could not find '%s/usr/bin/update_dyld_shared_cache_root_mode' in target volume\n", rootPath.c_str()); - return 1; - } - } -#else - if ( rootPath.empty() ) { - fprintf(stderr, "update_dyld_shared_cache_root_mode: error: -root option missing\n"); - return 1; - } -#endif - - // Find the boot volume so that we can ensure all overlays are on the same volume - struct stat rootStatBuf; - if ( stat(rootPath == "" ? "/" : rootPath.c_str(), &rootStatBuf) != 0 ) { - fprintf(stderr, "update_dyld_shared_cache: error: could not stat root file system because '%s'\n", strerror(errno)); - return 1; - } - dev_t rootFS = rootStatBuf.st_dev; - - - // - // pathPrefixes for three modes: - // 1) no options: { "" } // search only boot volume - // 2) -overlay: { overlay, "" } // search overlay, then boot volume - // 3) -root: { root } // search only -root volume - // - std::vector pathPrefixes; - if ( !overlayPath.empty() ) { - // Only add the overlay path if it exists, and is the same volume as the root - struct stat overlayStatBuf; - if ( stat(overlayPath.c_str(), &overlayStatBuf) != 0 ) { - fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because '%s'\n", overlayPath.c_str(), strerror(errno)); - overlayPath.clear(); - } - else { - char resolvedOverlayPath[PATH_MAX]; - if ( realpath(overlayPath.c_str(), resolvedOverlayPath) != NULL ) { - overlayPath = resolvedOverlayPath; - } - else { - fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because realpath() failed\n", overlayPath.c_str()); - overlayPath.clear(); - } - } - if ( !overlayPath.empty() ) - pathPrefixes.push_back(overlayPath); - } - pathPrefixes.push_back(rootPath); - - // build FileSystem object - const char* fsRoot = rootPath.empty() ? nullptr : rootPath.c_str(); - const char* fsOverlay = overlayPath.empty() ? nullptr : overlayPath.c_str(); - dyld3::closure::FileSystemPhysical fileSystem(fsRoot, fsOverlay); - - // normalize output directory - 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; - else if ( !overlayPath.empty() ) - cacheDir = overlayPath + MACOSX_DYLD_SHARED_CACHE_DIR; - else - cacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; - } - int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); - if ( (err != 0) && (err != EEXIST) ) { - fprintf(stderr, "update_dyld_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir.c_str(), err); - return 1; - } - // make sure cacheDir is always a real path, so it can be checked later to see if it changed - char resolvedCachePath[PATH_MAX]; - ::realpath(cacheDir.c_str(), resolvedCachePath); - cacheDir = resolvedCachePath; - -#if BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER - bool requireDylibsBeRootlessProtected = false; -#else - bool requireDylibsBeRootlessProtected = isProtectedBySIPExceptDyld(cacheDir); - if ( requireDylibsBeRootlessProtected && !overlayPath.empty() && !isProtectedBySIP(overlayPath.c_str()) ) { - fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because it is not SIP protected\n", overlayPath.c_str()); - overlayPath.clear(); - pathPrefixes.clear(); - pathPrefixes.push_back(rootPath); - } -#endif - - if ( archStrs.empty() ) { - // check if OS has enough i386 to make a shared cache - char realerPath[MAXPATHLEN]; - Diagnostics testDiag; - const char* foundationPath = "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation"; - dyld3::closure::LoadedFileInfo foundationInfo = dyld3::MachOAnalyzer::load(testDiag, fileSystem, foundationPath, dyld3::GradedArchs::i386, dyld3::Platform::macOS, realerPath); - bool foundationHas32bit = (foundationInfo.fileContent != NULL); - if ( foundationHas32bit ) - fileSystem.unloadFile(foundationInfo); - - if ( universal ) { - // -universal_boot should make all possible dyld caches - if ( foundationHas32bit ) - archStrs.insert("i386"); - archStrs.insert("x86_64"); - archStrs.insert("x86_64h"); - } - else { - // just make caches for this machine - if ( foundationHas32bit ) - archStrs.insert("i386"); - archStrs.insert(runningOnHaswell() ? "x86_64h" : "x86_64"); - } - } - - uint64_t t1 = mach_absolute_time(); - - // find all mach-o files for requested architectures - __block std::vector allFileSets; - if ( archStrs.count("x86_64") ) - allFileSets.push_back({dyld3::GradedArchs::x86_64}); - if ( archStrs.count("x86_64h") ) - allFileSets.push_back({dyld3::GradedArchs::x86_64h}); - if ( archStrs.count("i386") ) - allFileSets.push_back({dyld3::GradedArchs::i386}); - if ( searchDisk ) - findAllFiles(fileSystem, pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets); - else { - std::unordered_set runtimePathsFound; - findOSFilesViaBOMS(fileSystem, pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets); - } - - // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache - for (MappedMachOsByCategory& fileSet : allFileSets) { - pruneCachedDylibs(rootPath, skipDylibs, fileSet, verbose); - pruneOtherDylibs(rootPath, fileSet); - pruneExecutables(rootPath, fileSet); - } - - uint64_t t2 = mach_absolute_time(); - if ( verbose ) { - if ( searchDisk ) - fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); - else - fprintf(stderr, "time to read BOM and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); - } - - // build caches in parallel on machines with at leat 4GB of RAM - uint64_t memSize = 0; - size_t sz = sizeof(memSize);; - bool buildInParallel = false; - if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) { - if ( memSize >= 0x100000000ULL ) - buildInParallel = true; - } - dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) - : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL); - - // build all caches - __block bool cacheBuildFailure = false; - __block bool wroteSomeCacheFile = false; - dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) { - MappedMachOsByCategory& fileSet = allFileSets[index]; - const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archs.name(); - - DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) { - if ( skipDylibs.count(runtimePath) ) - return DyldSharedCache::MappedMachO(); - if (fileSet.badZippered.count(runtimePath)) { - return DyldSharedCache::MappedMachO(); - } - for (const std::string& prefix : pathPrefixes) { - std::string fullPath = prefix + runtimePath; - struct stat statBuf; - if ( stat(fullPath.c_str(), &statBuf) == 0 ) { - 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 mappedFiles; - mappedFiles.push_back({fileSet.archs}); - if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, rootFS, 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(); - } - } - } - } - return DyldSharedCache::MappedMachO(); - }; - size_t startCount = fileSet.dylibsForCache.size(); - std::vector>> excludes; - DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, fileSet.badZippered, loader, excludes); - for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) { - fprintf(stderr, "update_dyld_shared_cache: warning: %s not in .bom, but adding required dylib %s\n", fileSet.archs.name(), fileSet.dylibsForCache[i].runtimePath.c_str()); - } - for (auto& exclude : excludes) { - std::string reasons = "(\""; - for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) { - reasons += *i; - if (i != --exclude.second.end()) { - reasons += "\", \""; - } - } - reasons += "\")"; - fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str()); - fileSet.otherDylibsAndBundles.push_back(exclude.first); - } - - // check if cache is already up to date - if ( !force ) { - if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) ) - return; - } - - // add any extra dylibs needed which were not in .bom - fprintf(stderr, "update_dyld_shared_cache: %s incorporating %lu OS dylibs, tracking %lu others, building closures for %lu executables\n", - fileSet.archs.name(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size()); - //for (const DyldSharedCache::MappedMachO& aFile : fileSet.otherDylibsAndBundles) { - // fprintf(stderr, " %s\n", aFile.runtimePath.c_str()); - //} - - - // build cache new cache file - DyldSharedCache::CreateOptions options; - options.outputFilePath = outFile; - options.outputMapFilePath = cacheDir + "/dyld_shared_cache_" + fileSet.archs.name() + ".map"; - options.archs = &fileSet.archs; - options.platform = dyld3::Platform::macOS; - options.excludeLocalSymbols = false; - options.optimizeStubs = false; - options.optimizeObjC = true; - options.codeSigningDigestMode = DyldSharedCache::SHA256only; - options.dylibsRemovedDuringMastering = dylibsRemoved; - options.inodesAreSameAsRuntime = true; - options.cacheSupportsASLR = (&fileSet.archs != &dyld3::GradedArchs::i386); - options.forSimulator = false; - options.isLocallyBuiltCache = true; - options.verbose = verbose; - options.evictLeafDylibsOnOverflow = true; - DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSystem, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); - - // print any warnings - for (const std::string& warn : results.warnings) { - fprintf(stderr, "update_dyld_shared_cache: warning: %s %s\n", fileSet.archs.name(), warn.c_str()); - } - if ( results.errorMessage.empty() ) { - wroteSomeCacheFile = true; - } - else { - fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str()); - cacheBuildFailure = true; - } - }); - - - // Save off spintrace data - if ( wroteSomeCacheFile ) { - void* h = dlopen("/usr/lib/libdscsym.dylib", 0); - if ( h != nullptr ) { - typedef int (*dscym_func)(const char*); - dscym_func func = (dscym_func)dlsym(h, "dscsym_save_dscsyms_for_current_caches"); - std::string nuggetRoot = rootPath; - if ( nuggetRoot.empty() ) - nuggetRoot = overlayPath; - if ( nuggetRoot.empty() ) - nuggetRoot = "/"; - (*func)(nuggetRoot.c_str()); - } - } - - - // we could unmap all input files, but tool is about to quit - - return (cacheBuildFailure ? 1 : 0); + fprintf(stderr, "This tool is deprecated.\n"); + return 0; } diff --git a/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp b/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp index 586ff5e..b1a680f 100644 --- a/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp +++ b/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include "FileUtils.h" #include "StringUtils.h" @@ -100,7 +101,7 @@ static const char* sMacOSBinaries[] = { static bool verbose = false; -static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, std::vector& files, dyld3::Platform platform) +static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, std::vector& files, dyld3::Platform platform, Diagnostics& diag) { // don't precompute closure info for any debug or profile dylibs if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_asan.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") ) @@ -110,13 +111,12 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std:: bool result = false; for (MappedMachOsByCategory& file : files) { - Diagnostics diag; char realerPath[MAXPATHLEN]; dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, platform, realerPath); const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent; if ( ma != nullptr ) { - bool sipProtected = false; // isProtectedBySIP(fd); + bool sipProtected = false; bool issetuid = false; const uint64_t sliceLen = loadedFileInfo.sliceLen; if ( ma->isDynamicExecutable() ) { @@ -126,7 +126,9 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std:: result = true; #endif } - else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) { + else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) { + diag.error("Dylib located at '%s' cannot be placed in cache because: '%s'", loadedFileInfo.path, msg); + }) ) { file.dylibsForCache.emplace_back(runtimePath, ma, sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino); result = true; } @@ -167,7 +169,8 @@ static void findAllFiles(const dyld3::closure::FileSystem& fileSystem, const std return; } // if the file is mach-o, add to list - if ( addIfMachO(fileSystem, path, statBuf, files, platform) ) { + Diagnostics diag; + if ( addIfMachO(fileSystem, path, statBuf, files, platform, diag) ) { if ( multiplePrefixes ) alreadyUsed.insert(path); } @@ -182,7 +185,10 @@ static void addMacOSHostLibs(std::vector& allFileSets, d for (const char* path : sMacOSHostLibs) { struct stat statBuf; if ( stat(path, &statBuf) == 0 ) { - addIfMachO(fileSystem, path, statBuf, allFileSets, dyld3::Platform::macOS); + Diagnostics diag; + addIfMachO(fileSystem, path, statBuf, allFileSets, dyld3::Platform::macOS, diag); + if ( diag.hasError() ) + fprintf(stderr, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", path, diag.errorMessage().c_str()); } } } @@ -194,7 +200,10 @@ static void addMacOSBinaries(const dyld3::closure::FileSystem& fileSystem, const std::string fullPath = prefix + path; struct stat statBuf; if ( stat(fullPath.c_str(), &statBuf) == 0 ) { - addIfMachO(fileSystem, path, statBuf, files, dyld3::Platform::macOS); + Diagnostics diag; + addIfMachO(fileSystem, path, statBuf, files, dyld3::Platform::macOS, diag); + if ( diag.hasError() ) + fprintf(stderr, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", fullPath.c_str(), diag.errorMessage().c_str()); } } } @@ -305,6 +314,14 @@ static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCate } +static std::string getOrderFileContent(const std::string& orderFile) +{ + std::ifstream fstream(orderFile); + std::stringstream stringBuf; + stringBuf << fstream.rdbuf(); + return stringBuf.str(); +} + static bool existingCacheUpToDate(const std::string& existingCache, const std::vector& currentDylibs) { // if no existing cache, it is not up-to-date @@ -363,6 +380,26 @@ static bool existingCacheUpToDate(const std::string& existingCache, const std::v return !foundMismatch; } +static void addFileSets(const std::unordered_set& allowedArchs, std::unordered_set& requestedArchs, std::vector& fileSets) +{ + if ( requestedArchs.empty() ) { +#if __arm64__ + requestedArchs.insert("arm64"); +#elif __x86_64__ + requestedArchs.insert("x86_64"); + requestedArchs.insert("i386"); +#else + #error unknown platform +#endif + } + + for (auto& requested : requestedArchs) { + if ( allowedArchs.find(requested) != allowedArchs.end() ) { + const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(requested.c_str(), false); + fileSets.push_back({archs}); + } + } +} inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) { @@ -388,6 +425,7 @@ int main(int argc, const char* argv[], const char* envp[]) std::string dirtyDataOrderFile; dyld3::Platform platform = dyld3::Platform::iOS_simulator; std::unordered_set skipDylibs; + std::unordered_set requestedArchs; // parse command line options for (int i = 1; i < argc; ++i) { @@ -426,6 +464,10 @@ int main(int argc, const char* argv[], const char* envp[]) TERMINATE_IF_LAST_ARG("-dirty_data_order_file missing path argument\n"); dirtyDataOrderFile = argv[++i]; } + else if (strcmp(arg, "-arch") == 0) { + TERMINATE_IF_LAST_ARG("-arch missing arch argument\n"); + requestedArchs.insert(argv[++i]); + } else if (strcmp(arg, "-force") == 0) { force = true; } @@ -440,11 +482,11 @@ int main(int argc, const char* argv[], const char* envp[]) } } - if ( rootPath.empty()) { + if ( rootPath.empty() ) { fprintf(stderr, "-root should be specified\n"); return 1; } - if (cacheDir.empty()) { + if ( cacheDir.empty() ) { fprintf(stderr, "-cache_dir should be specified\n"); return 1; } @@ -453,6 +495,12 @@ int main(int argc, const char* argv[], const char* envp[]) if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { rootPath = resolvedPath; } + + // canonicalize cacheDir. + // Later, path is checked against real path name before writing cache file to avoid TOCTU race condition. + if ( realpath(cacheDir.c_str(), resolvedPath) != NULL ) { + cacheDir = resolvedPath; + } // Find the boot volume so that we can ensure all overlays are on the same volume struct stat rootStatBuf; @@ -476,23 +524,45 @@ int main(int argc, const char* argv[], const char* envp[]) return 1; } + std::string dylibOrderFileContent; + if ( !dylibOrderFile.empty() ) { + dylibOrderFileContent = getOrderFileContent(dylibOrderFile); + } + + std::string dirtyDataOrderFileContent; + if ( !dirtyDataOrderFile.empty() ) { + dirtyDataOrderFileContent = getOrderFileContent(dirtyDataOrderFile); + } uint64_t t1 = mach_absolute_time(); __block std::vector allFileSets; + std::unordered_set allowedArchs; switch ( platform ) { case dyld3::Platform::iOS_simulator: - allFileSets.push_back({dyld3::GradedArchs::x86_64}); + allowedArchs.insert("x86_64"); + allowedArchs.insert("arm64"); break; case dyld3::Platform::watchOS_simulator: - allFileSets.push_back({dyld3::GradedArchs::i386}); + allowedArchs.insert("i386"); + allowedArchs.insert("x86_64"); + allowedArchs.insert("arm64"); + break; case dyld3::Platform::tvOS_simulator: - allFileSets.push_back({dyld3::GradedArchs::x86_64}); + allowedArchs.insert("x86_64"); + allowedArchs.insert("arm64"); break; default: assert(0 && "invalid platform"); break; } + + addFileSets(allowedArchs, requestedArchs, allFileSets); + if ( allFileSets.empty() ) { + fprintf(stderr, "update_dyld_sim_shared_cache: error: no valid architecture specified\n"); + return 1; + } + findAllFiles(fileSystem, pathPrefixes, allFileSets, platform); addMacOSHostLibs(allFileSets, platform); addMacOSBinaries(fileSystem, pathPrefixes, allFileSets); @@ -526,7 +596,7 @@ int main(int argc, const char* argv[], const char* envp[]) MappedMachOsByCategory& fileSet = allFileSets[index]; const std::string outFile = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name(); - DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) { + DyldSharedCache::MappedMachO (^loader)(const std::string&, Diagnostics&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath, Diagnostics& diag) { if ( skipDylibs.count(runtimePath) ) return DyldSharedCache::MappedMachO(); @@ -554,7 +624,7 @@ int main(int argc, const char* argv[], const char* envp[]) std::vector mappedFiles; mappedFiles.push_back({fileSet.archs}); - if ( addIfMachO(fileSystem, runtimePath, statBuf, mappedFiles, platform) ) { + if ( addIfMachO(fileSystem, runtimePath, statBuf, mappedFiles, platform, diag) ) { if ( !mappedFiles.back().dylibsForCache.empty() ) { if ( verbose ) fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str()); @@ -581,7 +651,7 @@ int main(int argc, const char* argv[], const char* envp[]) } } reasons += "\")"; - fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str()); + fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str()); fileSet.otherDylibsAndBundles.push_back(exclude.first); } @@ -598,9 +668,10 @@ int main(int argc, const char* argv[], const char* envp[]) options.outputMapFilePath = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name() + ".map"; options.archs = &fileSet.archs; options.platform = platform; - options.excludeLocalSymbols = false; + options.localSymbolMode = DyldSharedCache::LocalSymbolsMode::keep; options.optimizeStubs = false; - options.optimizeObjC = true; + options.optimizeDyldDlopens = false; // don't add dyld3 closures to simulator cache + options.optimizeDyldLaunches = false; // don't add dyld3 closures to simulator cache options.codeSigningDigestMode = DyldSharedCache::SHA256only; options.dylibsRemovedDuringMastering = dylibsRemoved; options.inodesAreSameAsRuntime = true; @@ -609,8 +680,8 @@ int main(int argc, const char* argv[], const char* envp[]) options.isLocallyBuiltCache = true; options.verbose = verbose; options.evictLeafDylibsOnOverflow = true; - options.dylibOrdering = parseOrderFile(dylibOrderFile); - options.dirtyDataSegmentOrdering = parseOrderFile(dirtyDataOrderFile); + options.dylibOrdering = parseOrderFile(dylibOrderFileContent); + options.dirtyDataSegmentOrdering = parseOrderFile(dirtyDataOrderFileContent); DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSystem, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); // print any warnings diff --git a/dyld_kernel.h b/dyld_kernel.h deleted file mode 100644 index 16b93f2..0000000 --- a/dyld_kernel.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2016 Apple Computer, Inc. All rights reserved. - * - * @APPLE_OSREFERENCE_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. The rights granted to you under the License - * may not be used to create, or enable the creation or redistribution of, - * unlawful or unlicensed copies of an Apple operating system, or to - * circumvent, violate, or enable the circumvention or violation of, any - * terms of an Apple operating system software license agreement. - * - * 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_OSREFERENCE_LICENSE_HEADER_END@ - */ - -#ifndef _MACH_DYLIB_INFO_H_ -#define _MACH_DYLIB_INFO_H_ - -#include -#include -#include -#include -#include - -/* These definitions must be kept in sync with the ones in - * osfmk/mach/mach_types.defs. - */ - -struct dyld_kernel_image_info { - uuid_t uuid; - fsobj_id_t fsobjid; - fsid_t fsid; - uint64_t load_addr; -}; - -struct dyld_kernel_process_info { - struct dyld_kernel_image_info cache_image_info; - uint64_t timestamp; // mach_absolute_time of last time dyld change to image list - uint32_t imageCount; // number of images currently loaded into process - uint32_t initialImageCount; // number of images statically loaded into process (before any dlopen() calls) - uint8_t dyldState; // one of dyld_process_state_* values - boolean_t no_cache; // process is running without a dyld cache - boolean_t private_cache; // process is using a private copy of its dyld cache -}; - -/* typedefs so our MIG is sane */ - -typedef struct dyld_kernel_image_info dyld_kernel_image_info_t; -typedef struct dyld_kernel_process_info dyld_kernel_process_info_t; -typedef dyld_kernel_image_info_t *dyld_kernel_image_info_array_t; - -#endif /* _MACH_DYLIB_INFO_H_ */ diff --git a/hell.c b/hell.c deleted file mode 100644 index b2925a4..0000000 --- a/hell.c +++ /dev/null @@ -1,11 +0,0 @@ -// This file is plain wrong, broken and horrible. -// The problem is Apple's ld fails to resolve some global variables in the static libraries we link into dyld. -// I have confirmed the problem even on macOS. -// -// If the sought-after symbol is defined (really defined) in a static library, then ld fails to find it. -// When it is defined in an object file passed on the command line, ld has no problem. - -void* __cleanup; -char __gdtoa_locks[32]; -void* _libkernel_functions[16]; - diff --git a/include/dlfcn_private.h b/include/dlfcn_private.h new file mode 100644 index 0000000..3f55277 --- /dev/null +++ b/include/dlfcn_private.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _DLFCN_PRIVATE_H_ +#define _DLFCN_PRIVATE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Internal interface for dlopen; intended to help audit internal use of + * dlopen. + */ +#if __i386__ +/* + * i386 makes tail calls difficult, and RTLD_NEXT requires that dlopen_audited + * tail call dlopen. Just directly call dlopen on i386. + */ +#define dlopen_audited(__path, __mode) dlopen(__path, __mode) +#else +extern void * dlopen_audited(const char * __path, int __mode) __DYLDDL_DRIVERKIT_UNAVAILABLE; +#endif + + +/* + * Sometimes dlopen() looks at who called it (such as for @rpath and @loader_path). + * This SPI allows you to simulate dlopen() being called by other code. + * Available in macOS 11.0 and iOS 14.0 and later. + */ +extern void* dlopen_from(const char* __path, int __mode, void* __addressInCaller) __DYLDDL_DRIVERKIT_UNAVAILABLE; + + +#ifdef __cplusplus +} +#endif + +#endif /* _DLFCN_PRIVATE_H_ */ diff --git a/include/mach-o/dyld.h b/include/mach-o/dyld.h index 4e8f31f..a2e641c 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -112,6 +112,15 @@ extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr) __O */ extern void _tlv_bootstrap(void) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) DYLD_DRIVERKIT_UNAVAILABLE ; + +/* + * Dylibs that are incorporated into the dyld cache are removed from disk. That means code + * cannot stat() the file to see if it "exists". This function is like a stat() call that checks if a + * path is to a dylib that was removed from disk and is incorporated into the active dyld cache. + */ +extern bool _dyld_shared_cache_contains_path(const char* path) __API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0), bridgeos(5.0)) DYLD_DRIVERKIT_UNAVAILABLE; + + /* * The following dyld API's are deprecated as of Mac OS X 10.5. They are either * no longer necessary or are superceeded by dlopen and friends in . @@ -153,22 +162,22 @@ typedef struct __NSObjectFileImage* NSObjectFileImage; /* NSObjectFileImage can only be used with MH_BUNDLE files */ -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlclose()"); +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlclose()"); -extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()"); +extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()"); typedef struct __NSModule* NSModule; -extern const char* NSNameOfModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern const char* NSLibraryNameForModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSNameOfModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSLibraryNameForModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); #define NSLINKMODULE_OPTION_NONE 0x0 #define NSLINKMODULE_OPTION_BINDNOW 0x1 #define NSLINKMODULE_OPTION_PRIVATE 0x2 @@ -176,27 +185,27 @@ extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* modu #define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8 #define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10 -extern bool NSUnLinkModule(NSModule module, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern bool NSUnLinkModule(NSModule module, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); #define NSUNLINKMODULE_OPTION_NONE 0x0 #define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 #define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2 /* symbol API */ typedef struct __NSSymbol* NSSymbol; -extern bool NSIsSymbolNameDefined(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); -extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); -extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); -extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); -extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); -extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +extern bool NSIsSymbolNameDefined(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 -extern const char* NSNameOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern void * NSAddressOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); -extern NSModule NSModuleForSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dladdr()"); +extern const char* NSNameOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern void * NSAddressOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +extern NSModule NSModuleForSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dladdr()"); /* error handling API */ typedef enum { @@ -224,7 +233,7 @@ typedef enum { NSOtherErrorInvalidArgs } NSOtherErrorNumbers; -extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()"); typedef struct { void (*undefined)(const char* symbolName); @@ -233,27 +242,27 @@ typedef struct { const char* fileName, const char* errorString); } NSLinkEditErrorHandlers; -extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); -extern bool NSAddLibrary(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()"); -extern bool NSAddLibraryWithSearching(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); +extern bool NSAddLibrary(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()"); +extern bool NSAddLibraryWithSearching(const char* pathName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); #define NSADDIMAGE_OPTION_NONE 0x0 #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 #define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 #define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 -extern bool _dyld_present(void) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "always true"); -extern bool _dyld_launched_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "moot"); -extern bool _dyld_all_twolevel_modules_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "moot"); -extern bool _dyld_bind_fully_image_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)"); -extern bool _dyld_image_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_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) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +extern bool _dyld_present(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "always true"); +extern bool _dyld_launched_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "moot"); +extern bool _dyld_all_twolevel_modules_prebound(void) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_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) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) DYLD_DRIVERKIT_UNAVAILABLE __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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); -extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) DYLD_DRIVERKIT_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) DYLD_DRIVERKIT_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()"); #if __cplusplus diff --git a/include/mach-o/dyld.modulemap b/include/mach-o/dyld.modulemap new file mode 100644 index 0000000..02bb277 --- /dev/null +++ b/include/mach-o/dyld.modulemap @@ -0,0 +1,4 @@ +module MachO.dyld [system] [extern_c] { + header "dyld.h" + export * +} diff --git a/include/mach-o/dyld_images.h b/include/mach-o/dyld_images.h index 1c7ba7f..dcf2dc8 100644 --- a/include/mach-o/dyld_images.h +++ b/include/mach-o/dyld_images.h @@ -84,6 +84,19 @@ struct dyld_uuid_info { uuid_t imageUUID; /* UUID of image */ }; +#define DYLD_AOT_IMAGE_KEY_SIZE 32 +struct dyld_aot_image_info { + const struct mach_header* x86LoadAddress; + const struct mach_header* aotLoadAddress; + const uint64_t aotImageSize; + const uint8_t aotImageKey[DYLD_AOT_IMAGE_KEY_SIZE]; // uniquely identifying SHA-256 key for this aot +}; + +struct dyld_aot_shared_cache_info { + const uintptr_t cacheBaseAddress; + uuid_t cacheUUID; +}; + typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]); /* for use in dyld_all_image_infos.errorKind field */ @@ -154,7 +167,14 @@ struct dyld_all_image_infos { /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */ uintptr_t compact_dyld_image_info_addr; size_t compact_dyld_image_info_size; - uint32_t platform; // FIXME: really a dyld_platform_t, but those aren't exposed here. + uint32_t platform; // FIXME: really a dyld_platform_t, but those aren't exposed here. + + /* the following field is only in version 17 (macOS 10.16) and later */ + uint32_t aotInfoCount; + const struct dyld_aot_image_info* aotInfoArray; + uint64_t aotInfoArrayChangeTimestamp; + uintptr_t aotSharedCacheBaseAddress; + uint8_t aotSharedCacheUUID[16]; }; /* diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 8a89995..dede2fb 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -113,6 +113,12 @@ extern const char* dyld_image_path_containing_address(const void* addr); // Exists in Mac OS X 10.11 and later extern const struct mach_header* dyld_image_header_containing_address(const void* addr); +// +// Return the mach header of the process +// +// Exists in Mac OS X 10.16 and later +extern const struct mach_header* _dyld_get_prog_image_header(void); + typedef uint32_t dyld_platform_t; typedef struct { @@ -121,339 +127,66 @@ typedef struct { } 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)); +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)); + +// It is intended that most code in the OS will use the version set constants, which will correctly deal with secret and future +// platforms. For example: + +// if (dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) { +// New behaviour for programs built against the iOS 12, tvOS 12, watchOS 5, macOS 10.14, or bridgeOS 3 (or newer) SDKs +// } else { +// Old behaviour +// } + +// In cases where more precise control is required (such as APIs that were added to varions platforms in different years) +// the os specific values may be used instead. Unlike the version set constants, the platform specific ones will only ever +// return true if the running binary is the platform being testsed, allowing conditions to be built for specific platforms +// and releases that came out at different times. For example: + +// if (dyld_program_sdk_at_least(dyld_platform_version_iOS_12_0) +// || dyld_program_sdk_at_least(dyld_platform_version_watchOS_6_0)) { +// New behaviour for programs built against the iOS 12 (fall 2018), watchOS 6 (fall 2019) (or newer) SDKs +// } else { +// Old behaviour all other platforms, as well as older iOSes and watchOSes +// } + +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)); +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)); +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)); +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)); -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)); +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)); +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. -#if TARGET_OS_OSX -#define dyld_fall_2018_os_versions dyld_platform_version_macOS_10_14 -#define dyld_late_fall_2018_os_versions dyld_platform_version_macOS_10_14_1 -#define dyld_spring_2019_os_versions dyld_platform_version_macOS_10_14_4 -#define dyld_summer_2019_os_versions dyld_platform_version_macOS_10_14_5 -#define dyld_late_summer_2019_os_versions dyld_platform_version_macOS_10_14_6 -#define dyld_fall_2019_os_versions dyld_platform_version_macOS_10_15 -#define dyld_autumn_2019_os_versions dyld_platform_version_macOS_10_15 -#define dyld_late_fall_2019_os_versions dyld_platform_version_macOS_10_15_1 -#define dyld_winter_2019_os_versions dyld_platform_version_macOS_10_15_1 -#endif /* TARGET_OS_OSX */ -#if TARGET_OS_IOS -#define dyld_fall_2018_os_versions dyld_platform_version_iOS_12_0 -#define dyld_late_fall_2018_os_versions dyld_platform_version_iOS_12_1 -#define dyld_spring_2019_os_versions dyld_platform_version_iOS_12_2 -#define dyld_summer_2019_os_versions dyld_platform_version_iOS_12_3 -#define dyld_late_summer_2019_os_versions dyld_platform_version_iOS_12_4 -#define dyld_fall_2019_os_versions dyld_platform_version_iOS_13_0 -#define dyld_autumn_2019_os_versions dyld_platform_version_iOS_13_1 -#define dyld_late_fall_2019_os_versions dyld_platform_version_iOS_13_2 -#define dyld_winter_2019_os_versions dyld_platform_version_iOS_13_3 -#endif /* TARGET_OS_IOS */ -#if TARGET_OS_TV -#define dyld_fall_2018_os_versions dyld_platform_version_tvOS_12_0 -#define dyld_late_fall_2018_os_versions dyld_platform_version_tvOS_12_1 -#define dyld_spring_2019_os_versions dyld_platform_version_tvOS_12_2 -#define dyld_summer_2019_os_versions dyld_platform_version_tvOS_12_3 -#define dyld_late_summer_2019_os_versions dyld_platform_version_tvOS_12_4 -#define dyld_fall_2019_os_versions dyld_platform_version_tvOS_13_0 -#define dyld_autumn_2019_os_versions dyld_platform_version_tvOS_13_0 -#define dyld_late_fall_2019_os_versions dyld_platform_version_tvOS_13_2 -#define dyld_winter_2019_os_versions dyld_platform_version_tvOS_13_3 -#endif /* TARGET_OS_TV */ -#if TARGET_OS_WATCH -#define dyld_fall_2018_os_versions dyld_platform_version_watchOS_5_0 -#define dyld_late_fall_2018_os_versions dyld_platform_version_watchOS_5_1 -#define dyld_spring_2019_os_versions dyld_platform_version_watchOS_5_2 -#define dyld_summer_2019_os_versions dyld_platform_version_watchOS_5_2 -#define dyld_late_summer_2019_os_versions dyld_platform_version_watchOS_5_3 -#define dyld_fall_2019_os_versions dyld_platform_version_watchOS_6_0 -#define dyld_autumn_2019_os_versions dyld_platform_version_watchOS_6_0 -#define dyld_late_fall_2019_os_versions dyld_platform_version_watchOS_6_1 -#define dyld_winter_2019_os_versions dyld_platform_version_watchOS_6_1 -#endif /* TARGET_OS_WATCH */ -#if TARGET_OS_BRIDGE -#define dyld_fall_2018_os_versions dyld_platform_version_bridgeOS_3_0 -#define dyld_late_fall_2018_os_versions dyld_platform_version_bridgeOS_3_1 -#define dyld_spring_2019_os_versions dyld_platform_version_bridgeOS_3_4 -#define dyld_summer_2019_os_versions dyld_platform_version_bridgeOS_3_4 -#define dyld_late_summer_2019_os_versions dyld_platform_version_bridgeOS_3_4 -#define dyld_fall_2019_os_versions dyld_platform_version_bridgeOS_4_0 -#define dyld_autumn_2019_os_versions dyld_platform_version_bridgeOS_4_0 -#define dyld_late_fall_2019_os_versions dyld_platform_version_bridgeOS_4_1 -#define dyld_winter_2019_os_versions dyld_platform_version_bridgeOS_4_1 -#endif /* TARGET_OS_BRIDGE */ +// Because we now have so many different OSes with different versions these version set values are intended to +// to provide a more convenient way to version check. They may be used instead of platform specific version in +// dyld_sdk_at_least(), dyld_minos_at_least(), dyld_program_sdk_at_least(), and dyld_program_minos_at_least(). +// Since they are references into a lookup table they MUST NOT be used by any code that does not ship as part of +// the OS, as the values may change and the tables in older OSes may not have the necessary values for back +// deployed binaries. These values are future proof against new platforms being added, and any checks against +// platforms that did not exist at the epoch of a version set will return true since all versions of that platform +// are inherently newer. -static dyld_build_version_t dyld_platform_version_macOS_10_0 = { .platform = PLATFORM_MACOS, .version = 0x000a0000 }; -static dyld_build_version_t dyld_platform_version_macOS_10_1 = { .platform = PLATFORM_MACOS, .version = 0x000a0100 }; -static dyld_build_version_t dyld_platform_version_macOS_10_2 = { .platform = PLATFORM_MACOS, .version = 0x000a0200 }; -static dyld_build_version_t dyld_platform_version_macOS_10_3 = { .platform = PLATFORM_MACOS, .version = 0x000a0300 }; -static dyld_build_version_t dyld_platform_version_macOS_10_4 = { .platform = PLATFORM_MACOS, .version = 0x000a0400 }; -static dyld_build_version_t dyld_platform_version_macOS_10_5 = { .platform = PLATFORM_MACOS, .version = 0x000a0500 }; -static dyld_build_version_t dyld_platform_version_macOS_10_6 = { .platform = PLATFORM_MACOS, .version = 0x000a0600 }; -static dyld_build_version_t dyld_platform_version_macOS_10_7 = { .platform = PLATFORM_MACOS, .version = 0x000a0700 }; -static dyld_build_version_t dyld_platform_version_macOS_10_8 = { .platform = PLATFORM_MACOS, .version = 0x000a0800 }; -static dyld_build_version_t dyld_platform_version_macOS_10_9 = { .platform = PLATFORM_MACOS, .version = 0x000a0900 }; -static dyld_build_version_t dyld_platform_version_macOS_10_10 = { .platform = PLATFORM_MACOS, .version = 0x000a0a00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_10_2 = { .platform = PLATFORM_MACOS, .version = 0x000a0a02 }; -static dyld_build_version_t dyld_platform_version_macOS_10_10_3 = { .platform = PLATFORM_MACOS, .version = 0x000a0a03 }; -static dyld_build_version_t dyld_platform_version_macOS_10_11 = { .platform = PLATFORM_MACOS, .version = 0x000a0b00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_11_2 = { .platform = PLATFORM_MACOS, .version = 0x000a0b02 }; -static dyld_build_version_t dyld_platform_version_macOS_10_11_3 = { .platform = PLATFORM_MACOS, .version = 0x000a0b03 }; -static dyld_build_version_t dyld_platform_version_macOS_10_11_4 = { .platform = PLATFORM_MACOS, .version = 0x000a0b04 }; -static dyld_build_version_t dyld_platform_version_macOS_10_12 = { .platform = PLATFORM_MACOS, .version = 0x000a0c00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_12_1 = { .platform = PLATFORM_MACOS, .version = 0x000a0c01 }; -static dyld_build_version_t dyld_platform_version_macOS_10_12_2 = { .platform = PLATFORM_MACOS, .version = 0x000a0c02 }; -static dyld_build_version_t dyld_platform_version_macOS_10_12_4 = { .platform = PLATFORM_MACOS, .version = 0x000a0c04 }; -static dyld_build_version_t dyld_platform_version_macOS_10_13 = { .platform = PLATFORM_MACOS, .version = 0x000a0d00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_13_1 = { .platform = PLATFORM_MACOS, .version = 0x000a0d01 }; -static dyld_build_version_t dyld_platform_version_macOS_10_13_2 = { .platform = PLATFORM_MACOS, .version = 0x000a0d02 }; -static dyld_build_version_t dyld_platform_version_macOS_10_13_4 = { .platform = PLATFORM_MACOS, .version = 0x000a0d04 }; -static dyld_build_version_t dyld_platform_version_macOS_10_14 = { .platform = PLATFORM_MACOS, .version = 0x000a0e00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_14_1 = { .platform = PLATFORM_MACOS, .version = 0x000a0e01 }; -static dyld_build_version_t dyld_platform_version_macOS_10_14_4 = { .platform = PLATFORM_MACOS, .version = 0x000a0e04 }; -static dyld_build_version_t dyld_platform_version_macOS_10_14_5 = { .platform = PLATFORM_MACOS, .version = 0x000a0e05 }; -static dyld_build_version_t dyld_platform_version_macOS_10_14_6 = { .platform = PLATFORM_MACOS, .version = 0x000a0e06 }; -static dyld_build_version_t dyld_platform_version_macOS_10_15 = { .platform = PLATFORM_MACOS, .version = 0x000a0f00 }; -static dyld_build_version_t dyld_platform_version_macOS_10_15_1 = { .platform = PLATFORM_MACOS, .version = 0x000a0f01 }; - -static dyld_build_version_t dyld_platform_version_iOS_2_0 = { .platform = PLATFORM_IOS, .version = 0x00020000 }; -static dyld_build_version_t dyld_platform_version_iOS_2_1 = { .platform = PLATFORM_IOS, .version = 0x00020100 }; -static dyld_build_version_t dyld_platform_version_iOS_2_2 = { .platform = PLATFORM_IOS, .version = 0x00020200 }; -static dyld_build_version_t dyld_platform_version_iOS_3_0 = { .platform = PLATFORM_IOS, .version = 0x00030000 }; -static dyld_build_version_t dyld_platform_version_iOS_3_1 = { .platform = PLATFORM_IOS, .version = 0x00030100 }; -static dyld_build_version_t dyld_platform_version_iOS_3_2 = { .platform = PLATFORM_IOS, .version = 0x00030200 }; -static dyld_build_version_t dyld_platform_version_iOS_4_0 = { .platform = PLATFORM_IOS, .version = 0x00040000 }; -static dyld_build_version_t dyld_platform_version_iOS_4_1 = { .platform = PLATFORM_IOS, .version = 0x00040100 }; -static dyld_build_version_t dyld_platform_version_iOS_4_2 = { .platform = PLATFORM_IOS, .version = 0x00040200 }; -static dyld_build_version_t dyld_platform_version_iOS_4_3 = { .platform = PLATFORM_IOS, .version = 0x00040300 }; -static dyld_build_version_t dyld_platform_version_iOS_5_0 = { .platform = PLATFORM_IOS, .version = 0x00050000 }; -static dyld_build_version_t dyld_platform_version_iOS_5_1 = { .platform = PLATFORM_IOS, .version = 0x00050100 }; -static dyld_build_version_t dyld_platform_version_iOS_6_0 = { .platform = PLATFORM_IOS, .version = 0x00060000 }; -static dyld_build_version_t dyld_platform_version_iOS_6_1 = { .platform = PLATFORM_IOS, .version = 0x00060100 }; -static dyld_build_version_t dyld_platform_version_iOS_7_0 = { .platform = PLATFORM_IOS, .version = 0x00070000 }; -static dyld_build_version_t dyld_platform_version_iOS_7_1 = { .platform = PLATFORM_IOS, .version = 0x00070100 }; -static dyld_build_version_t dyld_platform_version_iOS_8_0 = { .platform = PLATFORM_IOS, .version = 0x00080000 }; -static dyld_build_version_t dyld_platform_version_iOS_8_1 = { .platform = PLATFORM_IOS, .version = 0x00080100 }; -static dyld_build_version_t dyld_platform_version_iOS_8_2 = { .platform = PLATFORM_IOS, .version = 0x00080200 }; -static dyld_build_version_t dyld_platform_version_iOS_8_3 = { .platform = PLATFORM_IOS, .version = 0x00080300 }; -static dyld_build_version_t dyld_platform_version_iOS_8_4 = { .platform = PLATFORM_IOS, .version = 0x00080400 }; -static dyld_build_version_t dyld_platform_version_iOS_9_0 = { .platform = PLATFORM_IOS, .version = 0x00090000 }; -static dyld_build_version_t dyld_platform_version_iOS_9_1 = { .platform = PLATFORM_IOS, .version = 0x00090100 }; -static dyld_build_version_t dyld_platform_version_iOS_9_2 = { .platform = PLATFORM_IOS, .version = 0x00090200 }; -static dyld_build_version_t dyld_platform_version_iOS_9_3 = { .platform = PLATFORM_IOS, .version = 0x00090300 }; -static dyld_build_version_t dyld_platform_version_iOS_10_0 = { .platform = PLATFORM_IOS, .version = 0x000a0000 }; -static dyld_build_version_t dyld_platform_version_iOS_10_1 = { .platform = PLATFORM_IOS, .version = 0x000a0100 }; -static dyld_build_version_t dyld_platform_version_iOS_10_2 = { .platform = PLATFORM_IOS, .version = 0x000a0200 }; -static dyld_build_version_t dyld_platform_version_iOS_10_3 = { .platform = PLATFORM_IOS, .version = 0x000a0300 }; -static dyld_build_version_t dyld_platform_version_iOS_11_0 = { .platform = PLATFORM_IOS, .version = 0x000b0000 }; -static dyld_build_version_t dyld_platform_version_iOS_11_1 = { .platform = PLATFORM_IOS, .version = 0x000b0100 }; -static dyld_build_version_t dyld_platform_version_iOS_11_2 = { .platform = PLATFORM_IOS, .version = 0x000b0200 }; -static dyld_build_version_t dyld_platform_version_iOS_11_3 = { .platform = PLATFORM_IOS, .version = 0x000b0300 }; -static dyld_build_version_t dyld_platform_version_iOS_11_4 = { .platform = PLATFORM_IOS, .version = 0x000b0400 }; -static dyld_build_version_t dyld_platform_version_iOS_12_0 = { .platform = PLATFORM_IOS, .version = 0x000c0000 }; -static dyld_build_version_t dyld_platform_version_iOS_12_1 = { .platform = PLATFORM_IOS, .version = 0x000c0100 }; -static dyld_build_version_t dyld_platform_version_iOS_12_2 = { .platform = PLATFORM_IOS, .version = 0x000c0200 }; -static dyld_build_version_t dyld_platform_version_iOS_12_3 = { .platform = PLATFORM_IOS, .version = 0x000c0300 }; -static dyld_build_version_t dyld_platform_version_iOS_12_4 = { .platform = PLATFORM_IOS, .version = 0x000c0400 }; -static dyld_build_version_t dyld_platform_version_iOS_13_0 = { .platform = PLATFORM_IOS, .version = 0x000d0000 }; -static dyld_build_version_t dyld_platform_version_iOS_13_1 = { .platform = PLATFORM_IOS, .version = 0x000d0100 }; -static dyld_build_version_t dyld_platform_version_iOS_13_2 = { .platform = PLATFORM_IOS, .version = 0x000d0200 }; -static dyld_build_version_t dyld_platform_version_iOS_13_3 = { .platform = PLATFORM_IOS, .version = 0x000d0300 }; - -static dyld_build_version_t dyld_platform_version_watchOS_1_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00010000 }; -static dyld_build_version_t dyld_platform_version_watchOS_2_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00020000 }; -static dyld_build_version_t dyld_platform_version_watchOS_2_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00020100 }; -static dyld_build_version_t dyld_platform_version_watchOS_2_2 = { .platform = PLATFORM_WATCHOS, .version = 0x00020200 }; -static dyld_build_version_t dyld_platform_version_watchOS_3_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00030000 }; -static dyld_build_version_t dyld_platform_version_watchOS_3_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00030100 }; -static dyld_build_version_t dyld_platform_version_watchOS_3_1_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00030101 }; -static dyld_build_version_t dyld_platform_version_watchOS_3_2 = { .platform = PLATFORM_WATCHOS, .version = 0x00030200 }; -static dyld_build_version_t dyld_platform_version_watchOS_4_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00040000 }; -static dyld_build_version_t dyld_platform_version_watchOS_4_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00040100 }; -static dyld_build_version_t dyld_platform_version_watchOS_4_2 = { .platform = PLATFORM_WATCHOS, .version = 0x00040200 }; -static dyld_build_version_t dyld_platform_version_watchOS_4_3 = { .platform = PLATFORM_WATCHOS, .version = 0x00040300 }; -static dyld_build_version_t dyld_platform_version_watchOS_5_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00050000 }; -static dyld_build_version_t dyld_platform_version_watchOS_5_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00050100 }; -static dyld_build_version_t dyld_platform_version_watchOS_5_2 = { .platform = PLATFORM_WATCHOS, .version = 0x00050200 }; -static dyld_build_version_t dyld_platform_version_watchOS_5_3 = { .platform = PLATFORM_WATCHOS, .version = 0x00050300 }; -static dyld_build_version_t dyld_platform_version_watchOS_6_0 = { .platform = PLATFORM_WATCHOS, .version = 0x00060000 }; -static dyld_build_version_t dyld_platform_version_watchOS_6_1 = { .platform = PLATFORM_WATCHOS, .version = 0x00060100 }; - -static dyld_build_version_t dyld_platform_version_tvOS_9_0 = { .platform = PLATFORM_TVOS, .version = 0x00090000 }; -static dyld_build_version_t dyld_platform_version_tvOS_9_1 = { .platform = PLATFORM_TVOS, .version = 0x00090100 }; -static dyld_build_version_t dyld_platform_version_tvOS_9_2 = { .platform = PLATFORM_TVOS, .version = 0x00090200 }; -static dyld_build_version_t dyld_platform_version_tvOS_10_0 = { .platform = PLATFORM_TVOS, .version = 0x000a0000 }; -static dyld_build_version_t dyld_platform_version_tvOS_10_0_1 = { .platform = PLATFORM_TVOS, .version = 0x000a0001 }; -static dyld_build_version_t dyld_platform_version_tvOS_10_1 = { .platform = PLATFORM_TVOS, .version = 0x000a0100 }; -static dyld_build_version_t dyld_platform_version_tvOS_10_2 = { .platform = PLATFORM_TVOS, .version = 0x000a0200 }; -static dyld_build_version_t dyld_platform_version_tvOS_11_0 = { .platform = PLATFORM_TVOS, .version = 0x000b0000 }; -static dyld_build_version_t dyld_platform_version_tvOS_11_1 = { .platform = PLATFORM_TVOS, .version = 0x000b0100 }; -static dyld_build_version_t dyld_platform_version_tvOS_11_2 = { .platform = PLATFORM_TVOS, .version = 0x000b0200 }; -static dyld_build_version_t dyld_platform_version_tvOS_11_3 = { .platform = PLATFORM_TVOS, .version = 0x000b0300 }; -static dyld_build_version_t dyld_platform_version_tvOS_11_4 = { .platform = PLATFORM_TVOS, .version = 0x000b0400 }; -static dyld_build_version_t dyld_platform_version_tvOS_12_0 = { .platform = PLATFORM_TVOS, .version = 0x000c0000 }; -static dyld_build_version_t dyld_platform_version_tvOS_12_1 = { .platform = PLATFORM_TVOS, .version = 0x000c0100 }; -static dyld_build_version_t dyld_platform_version_tvOS_12_2 = { .platform = PLATFORM_TVOS, .version = 0x000c0200 }; -static dyld_build_version_t dyld_platform_version_tvOS_12_3 = { .platform = PLATFORM_TVOS, .version = 0x000c0300 }; -static dyld_build_version_t dyld_platform_version_tvOS_12_4 = { .platform = PLATFORM_TVOS, .version = 0x000c0400 }; -static dyld_build_version_t dyld_platform_version_tvOS_13_0 = { .platform = PLATFORM_TVOS, .version = 0x000d0000 }; -static dyld_build_version_t dyld_platform_version_tvOS_13_2 = { .platform = PLATFORM_TVOS, .version = 0x000d0200 }; -static dyld_build_version_t dyld_platform_version_tvOS_13_3 = { .platform = PLATFORM_TVOS, .version = 0x000d0300 }; - -static dyld_build_version_t dyld_platform_version_bridgeOS_2_0 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00020000 }; -static dyld_build_version_t dyld_platform_version_bridgeOS_3_0 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00030000 }; -static dyld_build_version_t dyld_platform_version_bridgeOS_3_1 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00030100 }; -static dyld_build_version_t dyld_platform_version_bridgeOS_3_4 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00030400 }; -static dyld_build_version_t dyld_platform_version_bridgeOS_4_0 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00040000 }; -static dyld_build_version_t dyld_platform_version_bridgeOS_4_1 = { .platform = PLATFORM_BRIDGEOS, .version = 0x00040100 }; - -// Convienence constants for return values from dyld_get_sdk_version() and friends. - -#define DYLD_MACOSX_VERSION_10_0 0x000a0000 -#define DYLD_MACOSX_VERSION_10_1 0x000a0100 -#define DYLD_MACOSX_VERSION_10_2 0x000a0200 -#define DYLD_MACOSX_VERSION_10_3 0x000a0300 -#define DYLD_MACOSX_VERSION_10_4 0x000a0400 -#define DYLD_MACOSX_VERSION_10_5 0x000a0500 -#define DYLD_MACOSX_VERSION_10_6 0x000a0600 -#define DYLD_MACOSX_VERSION_10_7 0x000a0700 -#define DYLD_MACOSX_VERSION_10_8 0x000a0800 -#define DYLD_MACOSX_VERSION_10_9 0x000a0900 -#define DYLD_MACOSX_VERSION_10_10 0x000a0a00 -#define DYLD_MACOSX_VERSION_10_10_2 0x000a0a02 -#define DYLD_MACOSX_VERSION_10_10_3 0x000a0a03 -#define DYLD_MACOSX_VERSION_10_11 0x000a0b00 -#define DYLD_MACOSX_VERSION_10_11_2 0x000a0b02 -#define DYLD_MACOSX_VERSION_10_11_3 0x000a0b03 -#define DYLD_MACOSX_VERSION_10_11_4 0x000a0b04 -#define DYLD_MACOSX_VERSION_10_12 0x000a0c00 -#define DYLD_MACOSX_VERSION_10_12_1 0x000a0c01 -#define DYLD_MACOSX_VERSION_10_12_2 0x000a0c02 -#define DYLD_MACOSX_VERSION_10_12_4 0x000a0c04 -#define DYLD_MACOSX_VERSION_10_13 0x000a0d00 -#define DYLD_MACOSX_VERSION_10_13_1 0x000a0d01 -#define DYLD_MACOSX_VERSION_10_13_2 0x000a0d02 -#define DYLD_MACOSX_VERSION_10_13_4 0x000a0d04 -#define DYLD_MACOSX_VERSION_10_14 0x000a0e00 -#define DYLD_MACOSX_VERSION_10_14_1 0x000a0e01 -#define DYLD_MACOSX_VERSION_10_14_4 0x000a0e04 -#define DYLD_MACOSX_VERSION_10_14_5 0x000a0e05 -#define DYLD_MACOSX_VERSION_10_14_6 0x000a0e06 -#define DYLD_MACOSX_VERSION_10_15 0x000a0f00 -#define DYLD_MACOSX_VERSION_10_15_1 0x000a0f01 - -#define DYLD_IOS_VERSION_2_0 0x00020000 -#define DYLD_IOS_VERSION_2_1 0x00020100 -#define DYLD_IOS_VERSION_2_2 0x00020200 -#define DYLD_IOS_VERSION_3_0 0x00030000 -#define DYLD_IOS_VERSION_3_1 0x00030100 -#define DYLD_IOS_VERSION_3_2 0x00030200 -#define DYLD_IOS_VERSION_4_0 0x00040000 -#define DYLD_IOS_VERSION_4_1 0x00040100 -#define DYLD_IOS_VERSION_4_2 0x00040200 -#define DYLD_IOS_VERSION_4_3 0x00040300 -#define DYLD_IOS_VERSION_5_0 0x00050000 -#define DYLD_IOS_VERSION_5_1 0x00050100 -#define DYLD_IOS_VERSION_6_0 0x00060000 -#define DYLD_IOS_VERSION_6_1 0x00060100 -#define DYLD_IOS_VERSION_7_0 0x00070000 -#define DYLD_IOS_VERSION_7_1 0x00070100 -#define DYLD_IOS_VERSION_8_0 0x00080000 -#define DYLD_IOS_VERSION_8_1 0x00080100 -#define DYLD_IOS_VERSION_8_2 0x00080200 -#define DYLD_IOS_VERSION_8_3 0x00080300 -#define DYLD_IOS_VERSION_8_4 0x00080400 -#define DYLD_IOS_VERSION_9_0 0x00090000 -#define DYLD_IOS_VERSION_9_1 0x00090100 -#define DYLD_IOS_VERSION_9_2 0x00090200 -#define DYLD_IOS_VERSION_9_3 0x00090300 -#define DYLD_IOS_VERSION_10_0 0x000a0000 -#define DYLD_IOS_VERSION_10_1 0x000a0100 -#define DYLD_IOS_VERSION_10_2 0x000a0200 -#define DYLD_IOS_VERSION_10_3 0x000a0300 -#define DYLD_IOS_VERSION_11_0 0x000b0000 -#define DYLD_IOS_VERSION_11_1 0x000b0100 -#define DYLD_IOS_VERSION_11_2 0x000b0200 -#define DYLD_IOS_VERSION_11_3 0x000b0300 -#define DYLD_IOS_VERSION_11_4 0x000b0400 -#define DYLD_IOS_VERSION_12_0 0x000c0000 -#define DYLD_IOS_VERSION_12_1 0x000c0100 -#define DYLD_IOS_VERSION_12_2 0x000c0200 -#define DYLD_IOS_VERSION_12_3 0x000c0300 -#define DYLD_IOS_VERSION_12_4 0x000c0400 -#define DYLD_IOS_VERSION_13_0 0x000d0000 -#define DYLD_IOS_VERSION_13_1 0x000d0100 -#define DYLD_IOS_VERSION_13_2 0x000d0200 -#define DYLD_IOS_VERSION_13_3 0x000d0300 - -#define DYLD_WATCHOS_VERSION_1_0 0x00010000 -#define DYLD_WATCHOS_VERSION_2_0 0x00020000 -#define DYLD_WATCHOS_VERSION_2_1 0x00020100 -#define DYLD_WATCHOS_VERSION_2_2 0x00020200 -#define DYLD_WATCHOS_VERSION_3_0 0x00030000 -#define DYLD_WATCHOS_VERSION_3_1 0x00030100 -#define DYLD_WATCHOS_VERSION_3_1_1 0x00030101 -#define DYLD_WATCHOS_VERSION_3_2 0x00030200 -#define DYLD_WATCHOS_VERSION_4_0 0x00040000 -#define DYLD_WATCHOS_VERSION_4_1 0x00040100 -#define DYLD_WATCHOS_VERSION_4_2 0x00040200 -#define DYLD_WATCHOS_VERSION_4_3 0x00040300 -#define DYLD_WATCHOS_VERSION_5_0 0x00050000 -#define DYLD_WATCHOS_VERSION_5_1 0x00050100 -#define DYLD_WATCHOS_VERSION_5_2 0x00050200 -#define DYLD_WATCHOS_VERSION_5_3 0x00050300 -#define DYLD_WATCHOS_VERSION_6_0 0x00060000 -#define DYLD_WATCHOS_VERSION_6_1 0x00060100 - -#define DYLD_TVOS_VERSION_9_0 0x00090000 -#define DYLD_TVOS_VERSION_9_1 0x00090100 -#define DYLD_TVOS_VERSION_9_2 0x00090200 -#define DYLD_TVOS_VERSION_10_0 0x000a0000 -#define DYLD_TVOS_VERSION_10_0_1 0x000a0001 -#define DYLD_TVOS_VERSION_10_1 0x000a0100 -#define DYLD_TVOS_VERSION_10_2 0x000a0200 -#define DYLD_TVOS_VERSION_11_0 0x000b0000 -#define DYLD_TVOS_VERSION_11_1 0x000b0100 -#define DYLD_TVOS_VERSION_11_2 0x000b0200 -#define DYLD_TVOS_VERSION_11_3 0x000b0300 -#define DYLD_TVOS_VERSION_11_4 0x000b0400 -#define DYLD_TVOS_VERSION_12_0 0x000c0000 -#define DYLD_TVOS_VERSION_12_1 0x000c0100 -#define DYLD_TVOS_VERSION_12_2 0x000c0200 -#define DYLD_TVOS_VERSION_12_3 0x000c0300 -#define DYLD_TVOS_VERSION_12_4 0x000c0400 -#define DYLD_TVOS_VERSION_13_0 0x000d0000 -#define DYLD_TVOS_VERSION_13_2 0x000d0200 -#define DYLD_TVOS_VERSION_13_3 0x000d0300 - -#define DYLD_BRIDGEOS_VERSION_2_0 0x00020000 -#define DYLD_BRIDGEOS_VERSION_3_0 0x00030000 -#define DYLD_BRIDGEOS_VERSION_3_1 0x00030100 -#define DYLD_BRIDGEOS_VERSION_3_4 0x00030400 -#define DYLD_BRIDGEOS_VERSION_4_0 0x00040000 -#define DYLD_BRIDGEOS_VERSION_4_1 0x00040100 +//@VERSION_DEFS@ // // This finds the SDK version a binary was built against. @@ -536,6 +269,8 @@ extern bool dyld_shared_cache_some_image_overridden(void); // // Returns if the process is setuid or is code signed with entitlements. +// NOTE: It is safe to call this prior to malloc being initialized. This function +// is guaranteed to not call malloc, or depend on its state. // // Exists in Mac OS X 10.9 and later extern bool dyld_process_is_restricted(void); @@ -556,6 +291,14 @@ extern const char* dyld_shared_cache_file_path(void); // Exists in Mac OS X 10.15 and later extern bool dyld_has_inserted_or_interposing_libraries(void); +// +// Return true if dyld contains a fix for a specific identifier. Intended for staging breaking SPI +// changes +// +// Exists in macOS 10.16, iOS 14, tvOS14, watchOS 7 and later + +extern bool _dyld_has_fix_for_radar(const char *rdar); + // // for OpenGL to tell dyld it is ok to deallocate a memory based image when done. @@ -579,7 +322,7 @@ extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dy struct dyld_shared_cache_dylib_text_info { - uint64_t version; // current version 1 + uint64_t version; // current version 2 // following fields all exist in version 1 uint64_t loadAddressUnslid; uint64_t textSegmentSize; @@ -669,7 +412,7 @@ extern bool _dyld_shared_cache_is_locally_built(void); // // Exists in Mac OS X 10.15 and later // Exists in iOS 13.0 and later -extern bool dyld_need_closure(const char* execPath, const char* tempDir); +extern bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir); struct dyld_image_uuid_offset { @@ -720,6 +463,30 @@ extern void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount extern void _dyld_register_driverkit_main(void (*mainFunc)(void)); +// +// This is similar to _dyld_shared_cache_contains_path(), except that it returns the canonical +// shared cache path for the given path. +// +// Exists in macOS 10.16 and later +// Exists in iOS 14.0 and later +extern const char* _dyld_shared_cache_real_path(const char* path); + + +// +// Dyld has a number of modes. This function returns the mode for the current process. +// dyld2 is the classic "interpreter" way to run. +// dyld3 runs by compiling down and caching what dyld needs to do into a "closure". +// +// Exists in macOS 10.16 and later +// Exists in iOS 14.0 and later +// +#define DYLD_LAUNCH_MODE_USING_CLOSURE 0x00000001 // if 0, then running in classic dyld2 mode +#define DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH 0x00000002 // launch was slow, to build closure +#define DYLD_LAUNCH_MODE_CLOSURE_SAVED_TO_FILE 0x00000004 // next launch will be faster +#define DYLD_LAUNCH_MODE_CLOSURE_FROM_OS 0x00000008 // closure built into dyld cache +#define DYLD_LAUNCH_MODE_MINIMAL_CLOSURE 0x00000010 // closure does not contain fix ups +extern uint32_t _dyld_launch_mode(void); + // // When dyld must terminate a process because of a required dependent dylib @@ -808,6 +575,18 @@ extern void _dyld_for_each_objc_protocol(const char* protocolName, // objects are destroyed before global objects. extern void _tlv_exit(void); +typedef enum { + dyld_objc_string_kind +} DyldObjCConstantKind; + +// CF constants such as CFString's can be moved in to a contiguous range of +// shared cache memory. This returns true if the given pointer is to an object of +// the given kind. +// +// Exists in Mac OS X 10.16 and later +// Exists in iOS 14.0 and later +extern bool _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr); + // temp exports to keep tapi happy, until ASan stops using dyldVersionNumber extern double dyldVersionNumber; diff --git a/include/mach-o/dyld_process_info.h b/include/mach-o/dyld_process_info.h index 5e5c840..4f23a3e 100644 --- a/include/mach-o/dyld_process_info.h +++ b/include/mach-o/dyld_process_info.h @@ -28,7 +28,6 @@ #include #include #include -#include //FIXME we should include dyld_priv.h, but we need to do this to workaround a header search path bug in tapi typedef uint32_t dyld_platform_t; @@ -63,6 +62,12 @@ struct dyld_process_cache_info { }; typedef struct dyld_process_cache_info dyld_process_cache_info; +struct dyld_process_aot_cache_info { + uuid_t cacheUUID; + uint64_t cacheBaseAddress; +}; +typedef struct dyld_process_aot_cache_info dyld_process_aot_cache_info; + enum { dyld_process_state_not_started = 0x00, // process is suspended, dyld has not started running yet dyld_process_state_dyld_initialized = 0x10, // dyld has initialzed itself @@ -106,9 +111,15 @@ extern void _dyld_process_info_get_state(dyld_process_info info, dyld_process_s // fill in struct with info about dyld cache in use by process extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); +// fill in struct with info about aot cache in use by process +extern void _dyld_process_info_get_aot_cache(dyld_process_info info, dyld_process_aot_cache_info* aotCacheInfo); + // iterate all images in process extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); +// iterate all aot images in process +extern void _dyld_process_info_for_each_aot_image(dyld_process_info info, bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos); + // iterate all segments in an image extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); diff --git a/include/mach-o/fixup-chains.h b/include/mach-o/fixup-chains.h index 907a0e7..601ffc0 100644 --- a/include/mach-o/fixup-chains.h +++ b/include/mach-o/fixup-chains.h @@ -23,7 +23,7 @@ */ #ifndef __MACH_O_FIXUP_CHAINS__ -#define __MACH_O_FIXUP_CHAINS__ +#define __MACH_O_FIXUP_CHAINS__ 6 #include @@ -88,20 +88,28 @@ struct dyld_chained_starts_offsets // values for dyld_chained_starts_in_segment.pointer_format enum { - DYLD_CHAINED_PTR_ARM64E = 1, - DYLD_CHAINED_PTR_64 = 2, - DYLD_CHAINED_PTR_32 = 3, - DYLD_CHAINED_PTR_32_CACHE = 4, - DYLD_CHAINED_PTR_32_FIRMWARE = 5, + DYLD_CHAINED_PTR_ARM64E = 1, // stride 8, unauth target is vmaddr + DYLD_CHAINED_PTR_64 = 2, // target is vmaddr + DYLD_CHAINED_PTR_32 = 3, + DYLD_CHAINED_PTR_32_CACHE = 4, + DYLD_CHAINED_PTR_32_FIRMWARE = 5, + DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset + DYLD_CHAINED_PTR_ARM64E_OFFSET = 7, // old name + DYLD_CHAINED_PTR_ARM64E_KERNEL = 7, // stride 4, unauth target is vm offset + DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8, + DYLD_CHAINED_PTR_ARM64E_USERLAND = 9, // stride 8, unauth target is vm offset + DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10, // stride 4, unauth target is vmaddr + DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches + DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12, // stride 8, unauth target is vm offset, 24-bit bind }; // DYLD_CHAINED_PTR_ARM64E struct dyld_chained_ptr_arm64e_rebase { - uint64_t target : 43, // vmaddr + uint64_t target : 43, high8 : 8, - next : 11, // 8-byte stide + next : 11, // 4 or 8-byte stide bind : 1, // == 0 auth : 1; // == 0 }; @@ -111,8 +119,8 @@ struct dyld_chained_ptr_arm64e_bind { uint64_t ordinal : 16, zero : 16, - addend : 19, - next : 11, // 8-byte stide + addend : 19, // +/-256K + next : 11, // 4 or 8-byte stide bind : 1, // == 1 auth : 1; // == 0 }; @@ -124,7 +132,7 @@ struct dyld_chained_ptr_arm64e_auth_rebase diversity : 16, addrDiv : 1, key : 2, - next : 11, // 8-byte stide + next : 11, // 4 or 8-byte stide bind : 1, // == 0 auth : 1; // == 1 }; @@ -137,20 +145,46 @@ struct dyld_chained_ptr_arm64e_auth_bind diversity : 16, addrDiv : 1, key : 2, + next : 11, // 4 or 8-byte stide + bind : 1, // == 1 + auth : 1; // == 1 +}; + +// DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET +struct dyld_chained_ptr_64_rebase +{ + uint64_t target : 36, // 64GB max image size (DYLD_CHAINED_PTR_64 => vmAddr, DYLD_CHAINED_PTR_64_OFFSET => runtimeOffset) + high8 : 8, // top 8 bits set to this (DYLD_CHAINED_PTR_64 => after slide added, DYLD_CHAINED_PTR_64_OFFSET => before slide added) + reserved : 7, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 0 +}; + + +// DYLD_CHAINED_PTR_ARM64E_USERLAND24 +struct dyld_chained_ptr_arm64e_bind24 +{ + uint64_t ordinal : 24, + zero : 8, + addend : 19, // +/-256K + next : 11, // 8-byte stide + bind : 1, // == 1 + auth : 1; // == 0 +}; + +// DYLD_CHAINED_PTR_ARM64E_USERLAND24 +struct dyld_chained_ptr_arm64e_auth_bind24 +{ + uint64_t ordinal : 24, + zero : 8, + diversity : 16, + addrDiv : 1, + key : 2, next : 11, // 8-byte stide bind : 1, // == 1 auth : 1; // == 1 }; -// DYLD_CHAINED_PTR_64 -struct dyld_chained_ptr_64_rebase -{ - uint64_t target : 36, // vmaddr, 64GB max image size - high8 : 8, // top 8 bits set to this after slide added - reserved : 7, // all zeros - next : 12, // 4-byte stride - bind : 1; // == 0 -}; // DYLD_CHAINED_PTR_64 struct dyld_chained_ptr_64_bind @@ -162,6 +196,18 @@ struct dyld_chained_ptr_64_bind bind : 1; // == 1 }; +// DYLD_CHAINED_PTR_64_KERNEL_CACHE, DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE +struct dyld_chained_ptr_64_kernel_cache_rebase +{ + uint64_t target : 30, // basePointers[cacheLevel] + target + cacheLevel : 2, // what level of cache to bind to (indexes a mach_header array) + diversity : 16, + addrDiv : 1, + key : 2, + next : 12, // 1 or 4-byte stide + isAuth : 1; // 0 -> not authenticated. 1 -> authenticated +}; + // DYLD_CHAINED_PTR_32 // Note: for DYLD_CHAINED_PTR_32 some non-pointer values are co-opted into the chain // as out of range rebases. If an entry in the chain is > max_valid_pointer, then it diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp deleted file mode 100644 index e229bb3..0000000 --- a/launch-cache/CacheFileAbstraction.hpp +++ /dev/null @@ -1,442 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef __DYLD_CACHE_ABSTRACTION__ -#define __DYLD_CACHE_ABSTRACTION__ - -#include "dyld_cache_format.h" - -#include "FileAbstraction.hpp" -#include "Architectures.hpp" - -template -class dyldCacheHeader { -public: - const char* magic() const INLINE { return fields.magic; } - void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } - - uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } - void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } - - uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } - void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } - - uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } - void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } - - uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } - void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } - - uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } - void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } - - uint64_t codeSignatureOffset() const INLINE { return E::get64(fields.codeSignatureOffset); } - void set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); } - - uint64_t codeSignatureSize() const INLINE { return E::get64(fields.codeSignatureSize); } - void set_codeSignatureSize(uint64_t value) INLINE { E::set64(fields.codeSignatureSize, value); } - - uint64_t slideInfoOffset() const INLINE { return E::get64(fields.slideInfoOffset); } - void set_slideInfoOffset(uint64_t value) INLINE { E::set64(fields.slideInfoOffset, value); } - - uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } - void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } - - uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } - void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } - - uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } - void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } - - const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } - - uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); } - void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); } - - uint32_t branchPoolsOffset() const INLINE { return E::get32(fields.branchPoolsOffset); } - void set_branchPoolsOffset(uint32_t value) INLINE { E::set32(fields.branchPoolsOffset, value); } - - uint32_t branchPoolsCount() const INLINE { return E::get32(fields.branchPoolsCount); } - void set_branchPoolsCount(uint32_t value) INLINE { E::set32(fields.branchPoolsCount, value); } - - uint64_t accelerateInfoAddr() const INLINE { return E::get64(fields.accelerateInfoAddr); } - void set_accelerateInfoAddr(uint64_t value) INLINE { E::set64(fields.accelerateInfoAddr, value); } - - uint64_t accelerateInfoSize() const INLINE { return E::get64(fields.accelerateInfoSize); } - void set_accelerateInfoSize(uint64_t value) INLINE { E::set64(fields.accelerateInfoSize, value); } - - uint64_t imagesTextOffset() const INLINE { return E::get64(fields.imagesTextOffset); } - void set_imagesTextOffset(uint64_t value) INLINE { E::set64(fields.imagesTextOffset, value); } - - uint64_t imagesTextCount() const INLINE { return E::get64(fields.imagesTextCount); } - void set_imagesTextCount(uint64_t value) INLINE { E::set64(fields.imagesTextCount, value); } - -private: - dyld_cache_header fields; -}; - - - -template -class dyldCacheFileMapping { -public: - uint64_t address() const INLINE { return E::get64(fields.address); } - void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } - - uint64_t size() const INLINE { return E::get64(fields.size); } - void set_size(uint64_t value) INLINE { E::set64(fields.size, value); } - - uint64_t file_offset() const INLINE { return E::get64(fields.fileOffset); } - void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, value); } - - uint32_t max_prot() const INLINE { return E::get32(fields.maxProt); } - void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.maxProt, value); } - - uint32_t init_prot() const INLINE { return E::get32(fields.initProt); } - void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); } - -private: - dyld_cache_mapping_info fields; -}; - - -template -class dyldCacheImageInfo { -public: - uint64_t address() const INLINE { return E::get64(fields.address); } - void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } - - uint64_t modTime() const INLINE { return E::get64(fields.modTime); } - void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } - - uint64_t inode() const INLINE { return E::get64(fields.inode); } - void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } - - uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } - void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } - -private: - dyld_cache_image_info fields; -}; - -template -class dyldCacheImageTextInfo { -public: - const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } - - uint64_t loadAddress() const INLINE { return E::get64(fields.loadAddress); } - void set_loadAddress(uint64_t value) INLINE { E::set64(fields.loadAddress, value); } - - uint32_t textSegmentSize() const INLINE { return E::get32(fields.textSegmentSize); } - void set_textSegmentSize(uint32_t value) INLINE { E::set32(fields.textSegmentSize, value); } - - uint32_t pathOffset() const INLINE { return E::get32(fields.pathOffset); } - void set_pathOffset(uint32_t value) INLINE { E::set32(fields.pathOffset, value); } - -private: - dyld_cache_image_text_info fields; -}; - - - -template -class dyldCacheImageInfoExtra { -public: - uint64_t exportsTrieAddr() const INLINE { return E::get64(fields.exportsTrieAddr); } - void set_exportsTrieAddr(uint64_t value) INLINE { E::set64(fields.exportsTrieAddr, value); } - - uint64_t weakBindingsAddr() const INLINE { return E::get64(fields.weakBindingsAddr); } - void set_weakBindingsAddr(uint64_t value) INLINE { E::set64(fields.weakBindingsAddr, value); } - - uint32_t exportsTrieSize() const INLINE { return E::get32(fields.exportsTrieSize); } - void set_exportsTrieSize(uint32_t value) INLINE { E::set32(fields.exportsTrieSize, value); } - - uint32_t weakBindingsSize() const INLINE { return E::get32(fields.weakBindingsSize); } - void set_weakBindingsSize(uint32_t value) INLINE { E::set32(fields.weakBindingsSize, value); } - - uint32_t dependentsStartArrayIndex() const INLINE { return E::get32(fields.dependentsStartArrayIndex); } - void set_dependentsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.dependentsStartArrayIndex, value); } - - uint32_t reExportsStartArrayIndex() const INLINE { return E::get32(fields.reExportsStartArrayIndex); } - void set_reExportsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.reExportsStartArrayIndex, value); } - -private: - dyld_cache_image_info_extra fields; -}; - - -template -class dyldCacheAcceleratorInfo { -public: - uint32_t version() const INLINE { return E::get32(fields.version); } - void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } - - uint32_t imageExtrasCount() const INLINE { return E::get32(fields.imageExtrasCount); } - void set_imageExtrasCount(uint32_t value) INLINE { E::set32(fields.imageExtrasCount, value); } - - uint32_t imagesExtrasOffset() const INLINE { return E::get32(fields.imagesExtrasOffset); } - void set_imagesExtrasOffset(uint32_t value) INLINE { E::set32(fields.imagesExtrasOffset, value); } - - uint32_t bottomUpListOffset() const INLINE { return E::get32(fields.bottomUpListOffset); } - void set_bottomUpListOffset(uint32_t value) INLINE { E::set32(fields.bottomUpListOffset, value); } - - uint32_t dylibTrieOffset() const INLINE { return E::get32(fields.dylibTrieOffset); } - void set_dylibTrieOffset(uint32_t value) INLINE { E::set32(fields.dylibTrieOffset, value); } - - uint32_t dylibTrieSize() const INLINE { return E::get32(fields.dylibTrieSize); } - void set_dylibTrieSize(uint32_t value) INLINE { E::set32(fields.dylibTrieSize, value); } - - uint32_t initializersOffset() const INLINE { return E::get32(fields.initializersOffset); } - void set_initializersOffset(uint32_t value) INLINE { E::set32(fields.initializersOffset, value); } - - uint32_t initializersCount() const INLINE { return E::get32(fields.initializersCount); } - void set_initializersCount(uint32_t value) INLINE { E::set32(fields.initializersCount, value); } - - uint32_t dofSectionsOffset() const INLINE { return E::get32(fields.dofSectionsOffset); } - void set_dofSectionsOffset(uint32_t value) INLINE { E::set32(fields.dofSectionsOffset, value); } - - uint32_t dofSectionsCount() const INLINE { return E::get32(fields.dofSectionsCount); } - void set_dofSectionsCount(uint32_t value) INLINE { E::set32(fields.dofSectionsCount, value); } - - uint32_t reExportListOffset() const INLINE { return E::get32(fields.reExportListOffset); } - void set_reExportListOffset(uint32_t value) INLINE { E::set32(fields.reExportListOffset, value); } - - uint32_t reExportCount() const INLINE { return E::get32(fields.reExportCount); } - void set_reExportCount(uint32_t value) INLINE { E::set32(fields.reExportCount, value); } - - uint32_t depListOffset() const INLINE { return E::get32(fields.depListOffset); } - void set_depListOffset(uint32_t value) INLINE { E::set32(fields.depListOffset, value); } - - uint32_t depListCount() const INLINE { return E::get32(fields.depListCount); } - void set_depListCount(uint32_t value) INLINE { E::set32(fields.depListCount, value); } - - uint32_t rangeTableOffset() const INLINE { return E::get32(fields.rangeTableOffset); } - void set_rangeTableOffset(uint32_t value) INLINE { E::set32(fields.rangeTableOffset, value); } - - uint32_t rangeTableCount() const INLINE { return E::get32(fields.rangeTableCount); } - void set_rangeTableCount(uint32_t value) INLINE { E::set32(fields.rangeTableCount, value); } - - uint64_t dyldSectionAddr() const INLINE { return E::get64(fields.dyldSectionAddr); } - void set_dyldSectionAddr(uint64_t value) INLINE { E::set64(fields.dyldSectionAddr, value); } - - -private: - dyld_cache_accelerator_info fields; -}; - - -template -class dyldCacheAcceleratorInitializer { -public: - uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } - void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } - - uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } - void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } - -private: - dyld_cache_accelerator_initializer fields; -}; - - -template -class dyldCacheAcceleratorRangeEntry { -public: - uint64_t startAddress() const INLINE { return E::get64(fields.startAddress); } - void set_startAddress(uint64_t value) INLINE { E::set64(fields.startAddress, value); } - - uint32_t size() const INLINE { return E::get32(fields.size); } - void set_size(uint32_t value) INLINE { E::set32(fields.size, value); } - - uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } - void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } - -private: - dyld_cache_range_entry fields; -}; - -template -class dyldCacheAcceleratorDOFEntry { -public: - uint64_t sectionAddress() const INLINE { return E::get64(fields.sectionAddress); } - void set_sectionAddress(uint64_t value) INLINE { E::set64(fields.sectionAddress, value); } - - uint32_t sectionSize() const INLINE { return E::get32(fields.sectionSize); } - void set_sectionSize(uint32_t value) INLINE { E::set32(fields.sectionSize, value); } - - uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } - void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } - -private: - dyld_cache_accelerator_dof fields; -}; - - -template -class dyldCacheSlideInfo { -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 toc_offset() const INLINE { return E::get32(fields.toc_offset); } - void set_toc_offset(uint32_t value) INLINE { E::set32(fields.toc_offset, value); } - - uint32_t toc_count() const INLINE { return E::get32(fields.toc_count); } - void set_toc_count(uint32_t value) INLINE { E::set32(fields.toc_count, value); } - - uint32_t entries_offset() const INLINE { return E::get32(fields.entries_offset); } - void set_entries_offset(uint32_t value) INLINE { E::set32(fields.entries_offset, value); } - - uint32_t entries_count() const INLINE { return E::get32(fields.entries_count); } - void set_entries_count(uint32_t value) INLINE { E::set32(fields.entries_count, value); } - - uint32_t entries_size() const INLINE { return E::get32(fields.entries_size); } - void set_entries_size(uint32_t value) INLINE { E::set32(fields.entries_size, value); } - - uint16_t toc(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); } - void set_toc(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); } - -private: - dyld_cache_slide_info fields; -}; - - -struct dyldCacheSlideInfoEntry { - uint8_t bits[4096/(8*4)]; // 128-byte bitmap -}; - - -template -class dyldCacheSlideInfo2 { -public: - uint32_t version() const INLINE { return E::get32(fields.version); } - void set_version(uint32_t value) INLINE { E::set32(fields.version, value); } - - uint32_t page_starts_offset() const INLINE { return E::get32(fields.page_starts_offset); } - void set_page_starts_offset(uint32_t value) INLINE { E::set32(fields.page_starts_offset, value); } - - uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); } - void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); } - - uint32_t page_extras_offset() const INLINE { return E::get32(fields.page_extras_offset); } - void set_page_extras_offset(uint32_t value) INLINE { E::set32(fields.page_extras_offset, value); } - - uint32_t page_extras_count() const INLINE { return E::get32(fields.page_extras_count); } - void set_page_extras_count(uint32_t value) INLINE { E::set32(fields.page_extras_count, value); } - - uint32_t page_size() const INLINE { return E::get32(fields.page_size); } - void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); } - - uint64_t delta_mask() const INLINE { return E::get64(fields.delta_mask); } - void set_delta_mask(uint64_t value) INLINE { E::set64(fields.delta_mask, value); } - - uint64_t value_add() const INLINE { return E::get64(fields.value_add); } - void set_value_add(uint64_t value) INLINE { E::set64(fields.value_add, value); } - - uint16_t page_starts(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); } - void set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); } - - uint16_t page_extras(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); } - void set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); } - - -private: - dyld_cache_slide_info2 fields; -}; - - -template -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 -class dyldCacheLocalSymbolsInfo { -public: - uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } - void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } - - uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } - void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } - - uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } - void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } - - uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } - void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } - - uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } - void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } - - uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } - void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } - -private: - dyld_cache_local_symbols_info fields; -}; - - -template -class dyldCacheLocalSymbolEntry { -public: - uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } - void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } - - uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } - void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } - - uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } - void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } - -private: - dyld_cache_local_symbols_entry fields; -}; - - - - -#endif // __DYLD_CACHE_ABSTRACTION__ - - diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp deleted file mode 100644 index 1d73d74..0000000 --- a/launch-cache/FileAbstraction.hpp +++ /dev/null @@ -1,163 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef __FILE_ABSTRACTION__ -#define __FILE_ABSTRACTION__ - - -#include -#include -#include - -#ifdef __OPTIMIZE__ -#define INLINE __attribute__((always_inline)) -#else -#define INLINE -#endif - -// -// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants -// -// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 -// -// -// get16() read a 16-bit number from an E endian struct -// set16() write a 16-bit number to an E endian struct -// get32() read a 32-bit number from an E endian struct -// set32() write a 32-bit number to an E endian struct -// get64() read a 64-bit number from an E endian struct -// set64() write a 64-bit number to an E endian struct -// -// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) -// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) -// -// getBitsRaw() read a bit field from a struct with native endianness -// setBitsRaw() write a bit field from a struct with native endianness -// - -class BigEndian -{ -public: - static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } - static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } - - static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } - static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } - - static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } - static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } - - static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } - static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } - - static uint32_t getBits(const uint32_t& from, - uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } - static void setBits(uint32_t& into, uint32_t value, - uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } - - static uint32_t getBitsRaw(const uint32_t& from, - uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< -class Pointer32 -{ -public: - typedef uint32_t uint_t; - typedef _E E; - - static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } - static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } - - // Round to a P-size boundary - template - static T round_up(T value) { return (value+3) & ~(T)3; } - template - static T round_down(T value) { return value & ~(T)3; } -}; - - -template -class Pointer64 -{ -public: - typedef uint64_t uint_t; - typedef _E E; - - static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } - static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } - - // Round to a P-size boundary - template - static T round_up(T value) { return (value+7) & ~(T)7; } - template - static T round_down(T value) { return value & ~(T)7; } -}; - - - - - -#endif // __FILE_ABSTRACTION__ - - diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp deleted file mode 100644 index 6c95c13..0000000 --- a/launch-cache/MachOFileAbstraction.hpp +++ /dev/null @@ -1,902 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ -*/ -#ifndef __MACH_O_FILE_ABSTRACTION__ -#define __MACH_O_FILE_ABSTRACTION__ - -#include -#include -#include -#include - -// suport older versions of mach-o/loader.h - - - -#define ARM64_RELOC_UNSIGNED 0 // for pointers - - - - -#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F - -#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 -#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 -#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 -#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 -#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 -#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 -#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 -#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 -#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 -#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A -#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B -#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C - - -#include "FileAbstraction.hpp" -#include "Architectures.hpp" - -// utility to pair together a cpu-type and cpu-sub-type -struct ArchPair -{ - uint32_t arch; - uint32_t subtype; - - ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} - - bool operator<(const ArchPair& other) const { - if ( this->arch != other.arch ) - return (this->arch < other.arch); - return (this->subtype < other.subtype); - } - - bool operator==(const ArchPair& other) const { - return this->arch == other.arch && this->subtype == other.subtype; - } -}; - - -// -// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness -// - -// -// mach-o load command -// -template -class macho_load_command { -public: - uint32_t cmd() const INLINE { return E::get32(command.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } - - typedef typename P::E E; -private: - load_command command; -}; - - -// -// mach-o segment load command -// -template struct macho_segment_content {}; -template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; -template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; -template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; -template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; - -template -class macho_segment_command { -public: - uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } - - const char* segname() const INLINE { return segment.fields.segname; } - void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } - - uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } - void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } - - uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } - void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } - - uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } - void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } - - uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } - void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } - - uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } - void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } - - uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } - void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } - - uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } - void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } - - uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } - - enum { - CMD = macho_segment_content

::CMD - }; - - typedef typename P::E E; -private: - macho_segment_content

segment; -}; - - -// -// mach-o section -// -template struct macho_section_content {}; -template <> struct macho_section_content > { section fields; }; -template <> struct macho_section_content > { section_64 fields; }; -template <> struct macho_section_content > { section fields; }; -template <> struct macho_section_content > { section_64 fields; }; - -template -class macho_section { -public: - const char* sectname() const INLINE { return section.fields.sectname; } - void set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); } - - const char* segname() const INLINE { return section.fields.segname; } - void set_segname(const char* value) INLINE { strncpy(section.fields.segname, value, 16); } - - uint64_t addr() const INLINE { return P::getP(section.fields.addr); } - void set_addr(uint64_t value) INLINE { P::setP(section.fields.addr, value); } - - uint64_t size() const INLINE { return P::getP(section.fields.size); } - void set_size(uint64_t value) INLINE { P::setP(section.fields.size, value); } - - uint32_t offset() const INLINE { return E::get32(section.fields.offset); } - void set_offset(uint32_t value) INLINE { E::set32(section.fields.offset, value); } - - uint32_t align() const INLINE { return E::get32(section.fields.align); } - void set_align(uint32_t value) INLINE { E::set32(section.fields.align, value); } - - uint32_t reloff() const INLINE { return E::get32(section.fields.reloff); } - void set_reloff(uint32_t value) INLINE { E::set32(section.fields.reloff, value); } - - uint32_t nreloc() const INLINE { return E::get32(section.fields.nreloc); } - void set_nreloc(uint32_t value) INLINE { E::set32(section.fields.nreloc, value); } - - uint32_t flags() const INLINE { return E::get32(section.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(section.fields.flags, value); } - - uint32_t reserved1() const INLINE { return E::get32(section.fields.reserved1); } - void set_reserved1(uint32_t value) INLINE { E::set32(section.fields.reserved1, value); } - - uint32_t reserved2() const INLINE { return E::get32(section.fields.reserved2); } - void set_reserved2(uint32_t value) INLINE { E::set32(section.fields.reserved2, value); } - - typedef typename P::E E; -private: - macho_section_content

section; -}; - - -// -// mach-o dylib load command -// -template -class macho_dylib_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t name_offset() const INLINE { return E::get32(fields.dylib.name.offset); } - void set_name_offset(uint32_t value) INLINE { E::set32(fields.dylib.name.offset, value); } - - uint32_t timestamp() const INLINE { return E::get32(fields.dylib.timestamp); } - void set_timestamp(uint32_t value) INLINE { E::set32(fields.dylib.timestamp, value); } - - uint32_t current_version() const INLINE { return E::get32(fields.dylib.current_version); } - void set_current_version(uint32_t value) INLINE { E::set32(fields.dylib.current_version, value); } - - uint32_t compatibility_version() const INLINE { return E::get32(fields.dylib.compatibility_version); } - void set_compatibility_version(uint32_t value) INLINE { E::set32(fields.dylib.compatibility_version, value); } - - const char* name() const INLINE { return (const char*)&fields + name_offset(); } - void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - dylib_command fields; -}; - - -// -// mach-o dylinker load command -// -template -class macho_dylinker_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t name_offset() const INLINE { return E::get32(fields.name.offset); } - void set_name_offset(uint32_t value) INLINE { E::set32(fields.name.offset, value); } - - const char* name() const INLINE { return (const char*)&fields + name_offset(); } - void set_name_offset() INLINE { set_name_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - dylinker_command fields; -}; - - -// -// mach-o sub_framework load command -// -template -class macho_sub_framework_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t umbrella_offset() const INLINE { return E::get32(fields.umbrella.offset); } - void set_umbrella_offset(uint32_t value) INLINE { E::set32(fields.umbrella.offset, value); } - - const char* umbrella() const INLINE { return (const char*)&fields + umbrella_offset(); } - void set_umbrella_offset() INLINE { set_umbrella_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_framework_command fields; -}; - - -// -// mach-o sub_client load command -// -template -class macho_sub_client_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t client_offset() const INLINE { return E::get32(fields.client.offset); } - void set_client_offset(uint32_t value) INLINE { E::set32(fields.client.offset, value); } - - const char* client() const INLINE { return (const char*)&fields + client_offset(); } - void set_client_offset() INLINE { set_client_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_client_command fields; -}; - - -// -// mach-o sub_umbrella load command -// -template -class macho_sub_umbrella_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t sub_umbrella_offset() const INLINE { return E::get32(fields.sub_umbrella.offset); } - void set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value); } - - const char* sub_umbrella() const INLINE { return (const char*)&fields + sub_umbrella_offset(); } - void set_sub_umbrella_offset() INLINE { set_sub_umbrella_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_umbrella_command fields; -}; - - -// -// mach-o sub_library load command -// -template -class macho_sub_library_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t sub_library_offset() const INLINE { return E::get32(fields.sub_library.offset); } - void set_sub_library_offset(uint32_t value) INLINE { E::set32(fields.sub_library.offset, value); } - - const char* sub_library() const INLINE { return (const char*)&fields + sub_library_offset(); } - void set_sub_library_offset() INLINE { set_sub_library_offset(sizeof(fields)); } - - typedef typename P::E E; -private: - sub_library_command fields; -}; - - -// -// mach-o uuid load command -// -template -class macho_uuid_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - const uint8_t* uuid() const INLINE { return fields.uuid; } - void set_uuid(uint8_t value[16]) INLINE { memcpy(&fields.uuid, value, 16); } - - typedef typename P::E E; -private: - uuid_command fields; -}; - - -// -// mach-o routines load command -// -template struct macho_routines_content {}; -template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; -template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; -template <> struct macho_routines_content > { routines_command fields; enum { CMD = LC_ROUTINES }; }; -template <> struct macho_routines_content > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; }; - -template -class macho_routines_command { -public: - uint32_t cmd() const INLINE { return E::get32(routines.fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(routines.fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(routines.fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(routines.fields.cmdsize, value); } - - uint64_t init_address() const INLINE { return P::getP(routines.fields.init_address); } - void set_init_address(uint64_t value) INLINE { P::setP(routines.fields.init_address, value); } - - uint64_t init_module() const INLINE { return P::getP(routines.fields.init_module); } - void set_init_module(uint64_t value) INLINE { P::setP(routines.fields.init_module, value); } - - uint64_t reserved1() const INLINE { return P::getP(routines.fields.reserved1); } - void set_reserved1(uint64_t value) INLINE { P::setP(routines.fields.reserved1, value); } - - uint64_t reserved2() const INLINE { return P::getP(routines.fields.reserved2); } - void set_reserved2(uint64_t value) INLINE { P::setP(routines.fields.reserved2, value); } - - uint64_t reserved3() const INLINE { return P::getP(routines.fields.reserved3); } - void set_reserved3(uint64_t value) INLINE { P::setP(routines.fields.reserved3, value); } - - uint64_t reserved4() const INLINE { return P::getP(routines.fields.reserved4); } - void set_reserved4(uint64_t value) INLINE { P::setP(routines.fields.reserved4, value); } - - uint64_t reserved5() const INLINE { return P::getP(routines.fields.reserved5); } - void set_reserved5(uint64_t value) INLINE { P::setP(routines.fields.reserved5, value); } - - uint64_t reserved6() const INLINE { return P::getP(routines.fields.reserved6); } - void set_reserved6(uint64_t value) INLINE { P::setP(routines.fields.reserved6, value); } - - typedef typename P::E E; - enum { - CMD = macho_routines_content

::CMD - }; -private: - macho_routines_content

routines; -}; - - -// -// mach-o symbol table load command -// -template -class macho_symtab_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t symoff() const INLINE { return E::get32(fields.symoff); } - void set_symoff(uint32_t value) INLINE { E::set32(fields.symoff, value); } - - uint32_t nsyms() const INLINE { return E::get32(fields.nsyms); } - void set_nsyms(uint32_t value) INLINE { E::set32(fields.nsyms, value); } - - uint32_t stroff() const INLINE { return E::get32(fields.stroff); } - void set_stroff(uint32_t value) INLINE { E::set32(fields.stroff, value); } - - uint32_t strsize() const INLINE { return E::get32(fields.strsize); } - void set_strsize(uint32_t value) INLINE { E::set32(fields.strsize, value); } - - - typedef typename P::E E; -private: - symtab_command fields; -}; - - -// -// mach-o dynamic symbol table load command -// -template -class macho_dysymtab_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t ilocalsym() const INLINE { return E::get32(fields.ilocalsym); } - void set_ilocalsym(uint32_t value) INLINE { E::set32(fields.ilocalsym, value); } - - uint32_t nlocalsym() const INLINE { return E::get32(fields.nlocalsym); } - void set_nlocalsym(uint32_t value) INLINE { E::set32(fields.nlocalsym, value); } - - uint32_t iextdefsym() const INLINE { return E::get32(fields.iextdefsym); } - void set_iextdefsym(uint32_t value) INLINE { E::set32(fields.iextdefsym, value); } - - uint32_t nextdefsym() const INLINE { return E::get32(fields.nextdefsym); } - void set_nextdefsym(uint32_t value) INLINE { E::set32(fields.nextdefsym, value); } - - uint32_t iundefsym() const INLINE { return E::get32(fields.iundefsym); } - void set_iundefsym(uint32_t value) INLINE { E::set32(fields.iundefsym, value); } - - uint32_t nundefsym() const INLINE { return E::get32(fields.nundefsym); } - void set_nundefsym(uint32_t value) INLINE { E::set32(fields.nundefsym, value); } - - uint32_t tocoff() const INLINE { return E::get32(fields.tocoff); } - void set_tocoff(uint32_t value) INLINE { E::set32(fields.tocoff, value); } - - uint32_t ntoc() const INLINE { return E::get32(fields.ntoc); } - void set_ntoc(uint32_t value) INLINE { E::set32(fields.ntoc, value); } - - uint32_t modtaboff() const INLINE { return E::get32(fields.modtaboff); } - void set_modtaboff(uint32_t value) INLINE { E::set32(fields.modtaboff, value); } - - uint32_t nmodtab() const INLINE { return E::get32(fields.nmodtab); } - void set_nmodtab(uint32_t value) INLINE { E::set32(fields.nmodtab, value); } - - uint32_t extrefsymoff() const INLINE { return E::get32(fields.extrefsymoff); } - void set_extrefsymoff(uint32_t value) INLINE { E::set32(fields.extrefsymoff, value); } - - uint32_t nextrefsyms() const INLINE { return E::get32(fields.nextrefsyms); } - void set_nextrefsyms(uint32_t value) INLINE { E::set32(fields.nextrefsyms, value); } - - uint32_t indirectsymoff() const INLINE { return E::get32(fields.indirectsymoff); } - void set_indirectsymoff(uint32_t value) INLINE { E::set32(fields.indirectsymoff, value); } - - uint32_t nindirectsyms() const INLINE { return E::get32(fields.nindirectsyms); } - void set_nindirectsyms(uint32_t value) INLINE { E::set32(fields.nindirectsyms, value); } - - uint32_t extreloff() const INLINE { return E::get32(fields.extreloff); } - void set_extreloff(uint32_t value) INLINE { E::set32(fields.extreloff, value); } - - uint32_t nextrel() const INLINE { return E::get32(fields.nextrel); } - void set_nextrel(uint32_t value) INLINE { E::set32(fields.nextrel, value); } - - uint32_t locreloff() const INLINE { return E::get32(fields.locreloff); } - void set_locreloff(uint32_t value) INLINE { E::set32(fields.locreloff, value); } - - uint32_t nlocrel() const INLINE { return E::get32(fields.nlocrel); } - void set_nlocrel(uint32_t value) INLINE { E::set32(fields.nlocrel, value); } - - typedef typename P::E E; -private: - dysymtab_command fields; -}; - - -// -// mach-o two-level hints load command -// -template -class macho_twolevel_hints_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t offset() const INLINE { return E::get32(fields.offset); } - void set_offset(uint32_t value) INLINE { E::set32(fields.offset, value); } - - uint32_t nhints() const INLINE { return E::get32(fields.nhints); } - void set_nhints(uint32_t value) INLINE { E::set32(fields.nhints, value); } - - typedef typename P::E E; -private: - twolevel_hints_command fields; -}; - - -// -// mach-o threads load command -// -template -class macho_thread_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t flavor() const INLINE { return E::get32(fields_flavor); } - void set_flavor(uint32_t value) INLINE { E::set32(fields_flavor, value); } - - uint32_t count() const INLINE { return E::get32(fields_count); } - void set_count(uint32_t value) INLINE { E::set32(fields_count, value); } - - uint64_t thread_register(uint32_t index) const INLINE { return P::getP(thread_registers[index]); } - void set_thread_register(uint32_t index, uint64_t value) INLINE { P::setP(thread_registers[index], value); } - - typedef typename P::E E; - typedef typename P::uint_t pint_t; -private: - struct thread_command fields; - uint32_t fields_flavor; - uint32_t fields_count; - pint_t thread_registers[1]; -}; - - -// -// mach-o misc data -// -template -class macho_linkedit_data_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t dataoff() const INLINE { return E::get32(fields.dataoff); } - void set_dataoff(uint32_t value) INLINE { E::set32(fields.dataoff, value); } - - uint32_t datasize() const INLINE { return E::get32(fields.datasize); } - void set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value); } - - - typedef typename P::E E; -private: - linkedit_data_command fields; -}; - - -// -// mach-o symbol table entry -// -template struct macho_nlist_content {}; -template <> struct macho_nlist_content > { struct nlist fields; }; -template <> struct macho_nlist_content > { struct nlist_64 fields; }; -template <> struct macho_nlist_content > { struct nlist fields; }; -template <> struct macho_nlist_content > { struct nlist_64 fields; }; - -template -class macho_nlist { -public: - uint32_t n_strx() const INLINE { return E::get32(entry.fields.n_un.n_strx); } - void set_n_strx(uint32_t value) INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); } - - uint8_t n_type() const INLINE { return entry.fields.n_type; } - void set_n_type(uint8_t value) INLINE { entry.fields.n_type = value; } - - uint8_t n_sect() const INLINE { return entry.fields.n_sect; } - void set_n_sect(uint8_t value) INLINE { entry.fields.n_sect = value; } - - uint16_t n_desc() const INLINE { return E::get16(entry.fields.n_desc); } - void set_n_desc(uint16_t value) INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); } - - uint64_t n_value() const INLINE { return P::getP(entry.fields.n_value); } - void set_n_value(uint64_t value) INLINE { P::setP(entry.fields.n_value, value); } - - typedef typename P::E E; -private: - macho_nlist_content

entry; -}; - - - -// -// mach-o relocation info -// -template -class macho_relocation_info { -public: - uint32_t r_address() const INLINE { return E::get32(address); } - void set_r_address(uint32_t value) INLINE { E::set32(address, value); } - - uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } - void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } - - bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } - void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } - - uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } - void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } - - bool r_extern() const INLINE { return E::getBits(other, 27, 1); } - void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } - - uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } - void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } - - void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } - - typedef typename P::E E; -private: - uint32_t address; - uint32_t other; -}; - - -// -// mach-o scattered relocation info -// The bit fields are always in big-endian order (see mach-o/reloc.h) -// -template -class macho_scattered_relocation_info { -public: - bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } - void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } - - bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } - void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } - - uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } - void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } - - uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } - void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } - - uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } - void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } - - uint32_t r_value() const INLINE { return E::get32(value); } - void set_r_value(uint32_t x) INLINE { E::set32(value, x); } - - uint32_t r_other() const INLINE { return other; } - - typedef typename P::E E; -private: - uint32_t other; - uint32_t value; -}; - - -// -// mach-o file header -// -template struct macho_header_content {}; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; - -template -class macho_header { -public: - uint32_t magic() const INLINE { return E::get32(header.fields.magic); } - void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } - - uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } - void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } - - uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } - void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } - - uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } - void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } - - uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } - void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } - - uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } - void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } - - uint32_t flags() const INLINE { return E::get32(header.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } - - uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } - void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } - - const macho_segment_command

* getSegment(const char *segname) const - { - const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); - uint32_t cmd_count = this->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; - if (0 == strncmp(segname, segcmd->segname(), 16)) { - return segcmd; - } - } - cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - return NULL; - } - - const macho_section

* getSection(const char *segname, const char *sectname) const - { - const macho_segment_command

* segcmd = getSegment(segname); - if (!segcmd) return NULL; - - const macho_section

* sectcmd = (macho_section

*)(segcmd+1); - uint32_t section_count = segcmd->nsects(); - for (uint32_t j = 0; j < section_count; ++j) { - if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) { - return sectcmd+j; - } - } - - if (strcmp(segname, "__DATA") == 0) - return getSection("__DATA_CONST", sectname); - return NULL; - } - - const macho_load_command

* getLoadCommand(int query) const - { - const macho_load_command

* cmds = (macho_load_command

*)((uint8_t*)this + sizeof(macho_header

)); - uint32_t cmd_count = this->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == query ) { - return cmd; - } - cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - return NULL; - } - - typedef typename P::E E; -private: - macho_header_content

header; -}; - - - -// -// compressed dyld info load command -// -template -class macho_dyld_info_command { -public: - uint32_t cmd() const INLINE { return E::get32(fields.cmd); } - void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } - - uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } - void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } - - uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } - void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } - - uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } - void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } - - uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } - void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } - - uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } - void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } - - uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } - void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } - - uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } - void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } - - uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } - void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } - - uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } - void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } - - uint32_t export_off() const INLINE { return E::get32(fields.export_off); } - void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } - - uint32_t export_size() const INLINE { return E::get32(fields.export_size); } - void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } - - - typedef typename P::E E; -private: - dyld_info_command fields; -}; - -#ifndef NO_ULEB -inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { - uint64_t result = 0; - int bit = 0; - do { - if (p == end) - throw "malformed uleb128 extends beyond trie"; - - uint64_t slice = *p & 0x7f; - - if (bit >= 64 || slice << bit >> bit != slice) - throw "uleb128 too big for 64-bits"; - else { - result |= (slice << bit); - bit += 7; - } - } - while (*p++ & 0x80); - return result; -} - - -inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) -{ - int64_t result = 0; - int bit = 0; - uint8_t byte; - do { - if (p == end) - throw "malformed sleb128"; - byte = *p++; - result |= (((int64_t)(byte & 0x7f)) << bit); - bit += 7; - } while (byte & 0x80); - // sign extend negative numbers - if ( (byte & 0x40) != 0 ) - result |= (~0ULL) << bit; - return result; -} - -#endif - - -#endif // __MACH_O_FILE_ABSTRACTION__ - - diff --git a/launch-cache/MachOTrie.hpp b/launch-cache/MachOTrie.hpp deleted file mode 100644 index d2f137e..0000000 --- a/launch-cache/MachOTrie.hpp +++ /dev/null @@ -1,399 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2008-2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ -*/ -#ifndef __MACH_O_TRIE__ -#define __MACH_O_TRIE__ - -#include -#include - -#include "MachOFileAbstraction.hpp" - - -namespace mach_o { -namespace trie { - -struct Edge -{ - Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } - ~Edge() { } - const char* fSubString; - struct Node* fChild; - -}; - -struct Node -{ - Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), - fOther(0), fImportedName(NULL), fOrdered(false), - fHaveExportInfo(false), fTrieOffset(0) {} - ~Node() { } - const char* fCummulativeString; - std::vector fChildren; - uint64_t fAddress; - uint64_t fFlags; - uint64_t fOther; - const char* fImportedName; - bool fOrdered; - bool fHaveExportInfo; - uint32_t fTrieOffset; - - void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) { - const char* partialStr = &fullStr[strlen(fCummulativeString)]; - for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - Edge& e = *it; - long subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addSymbol(fullStr, address, flags, other, importName); - return; - } - else { - for (long i=subStringLen-1; i > 0; --i) { - if ( strncmp(e.fSubString, partialStr, i) == 0 ) { - // found a common substring, splice in new node - // was A -> C, now A -> B -> C - char* bNodeCummStr = strdup(e.fChild->fCummulativeString); - bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; - //node* aNode = this; - Node* bNode = new Node(bNodeCummStr); - Node* cNode = e.fChild; - char* abEdgeStr = strdup(e.fSubString); - abEdgeStr[i] = '\0'; - char* bcEdgeStr = strdup(&e.fSubString[i]); - Edge& abEdge = e; - abEdge.fSubString = abEdgeStr; - abEdge.fChild = bNode; - Edge bcEdge(bcEdgeStr, cNode); - bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags, other, importName); - return; - } - } - } - } - - // no commonality with any existing child, make a new edge that is this whole string - Node* newNode = new Node(strdup(fullStr)); - Edge newEdge(strdup(partialStr), newNode); - fChildren.push_back(newEdge); - newNode->fAddress = address; - newNode->fFlags = flags; - newNode->fOther = other; - if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) ) - newNode->fImportedName = importName; - else - newNode->fImportedName = NULL; - newNode->fHaveExportInfo = true; - } - - void addOrderedNodes(const char* name, std::vector& orderedNodes) { - if ( !fOrdered ) { - orderedNodes.push_back(this); - //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); - fOrdered = true; - } - const char* partialStr = &name[strlen(fCummulativeString)]; - for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - Edge& e = *it; - long subStringLen = strlen(e.fSubString); - if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { - // already have matching edge, go down that path - e.fChild->addOrderedNodes(name, orderedNodes); - return; - } - } - } - - // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) - // byte for child node count - // each child: zero terminated substring, uleb128 node offset - bool updateOffset(uint32_t& offset) { - uint32_t nodeSize = 1; // length of export info when no export info - if ( fHaveExportInfo ) { - if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { - nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal - if ( fImportedName != NULL ) - nodeSize += strlen(fImportedName); - ++nodeSize; // trailing zero in imported name - } - else { - nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); - if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) - nodeSize += uleb128_size(fOther); - } - // do have export info, overall node size so far is uleb128 of export info + export info - nodeSize += uleb128_size(nodeSize); - } - // add children - ++nodeSize; // byte for count of chidren - for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - Edge& e = *it; - nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); - } - bool result = (fTrieOffset != offset); - fTrieOffset = offset; - //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); - offset += nodeSize; - // return true if fTrieOffset was changed - return result; - } - - void appendToStream(std::vector& out) { - if ( fHaveExportInfo ) { - if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { - if ( fImportedName != NULL ) { - // nodes with re-export info: size, flags, ordinal, string - uint32_t nodeSize = (uint32_t)(uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1); - out.push_back(nodeSize); - append_uleb128(fFlags, out); - append_uleb128(fOther, out); - append_string(fImportedName, out); - } - else { - // nodes with re-export info: size, flags, ordinal, empty-string - uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1; - out.push_back(nodeSize); - append_uleb128(fFlags, out); - append_uleb128(fOther, out); - out.push_back(0); - } - } - else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { - // nodes with export info: size, flags, address, other - uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther); - out.push_back(nodeSize); - append_uleb128(fFlags, out); - append_uleb128(fAddress, out); - append_uleb128(fOther, out); - } - else { - // nodes with export info: size, flags, address - uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress); - out.push_back(nodeSize); - append_uleb128(fFlags, out); - append_uleb128(fAddress, out); - } - } - else { - // no export info uleb128 of zero is one byte of zero - out.push_back(0); - } - // write number of children - out.push_back(fChildren.size()); - // write each child - for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { - Edge& e = *it; - append_string(e.fSubString, out); - append_uleb128(e.fChild->fTrieOffset, out); - } - } - -private: - static void append_uleb128(uint64_t value, std::vector& out) { - uint8_t byte; - do { - byte = value & 0x7F; - value &= ~0x7F; - if ( value != 0 ) - byte |= 0x80; - out.push_back(byte); - value = value >> 7; - } while( byte >= 0x80 ); - } - - static void append_string(const char* str, std::vector& out) { - for (const char* s = str; *s != '\0'; ++s) - out.push_back(*s); - out.push_back('\0'); - } - - static unsigned int uleb128_size(uint64_t value) { - uint32_t result = 0; - do { - value = value >> 7; - ++result; - } while ( value != 0 ); - return result; - } - - -}; - -inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { - uint64_t result = 0; - int bit = 0; - do { - if (p == end) -#if __EXCEPTIONS - throw "malformed uleb128 extends beyond trie"; -#else - return result; -#endif - uint64_t slice = *p & 0x7f; - - if (bit >= 64 || slice << bit >> bit != slice) -#if __EXCEPTIONS - throw "uleb128 too big for 64-bits"; -#else - return result; -#endif - else { - result |= (slice << bit); - bit += 7; - } - } - while (*p++ & 0x80); - return result; -} - - - -struct Entry -{ - const char* name; - uint64_t address; - uint64_t flags; - uint64_t other; - const char* importName; -}; - - - -inline void makeTrie(const std::vector& entries, std::vector& output) -{ - Node start(strdup("")); - - // make nodes for all exported symbols - for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { - start.addSymbol(it->name, it->address, it->flags, it->other, it->importName); - } - - // create vector of nodes - std::vector orderedNodes; - orderedNodes.reserve(entries.size()*2); - for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { - start.addOrderedNodes(it->name, orderedNodes); - } - - // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized - bool more; - do { - uint32_t offset = 0; - more = false; - for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { - if ( (*it)->updateOffset(offset) ) - more = true; - } - } while ( more ); - - // create trie stream - for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { - (*it)->appendToStream(output); - } -} - -struct EntryWithOffset -{ - uintptr_t nodeOffset; - Entry entry; - - bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } -}; - - - -static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, - char* cummulativeString, int curStrOffset, - std::vector& output) -{ - if ( p >= end ) -#if __EXCEPTIONS - throw "malformed trie, node past end"; -#else - return; -#endif - const uint8_t terminalSize = read_uleb128(p, end); - const uint8_t* children = p + terminalSize; - if ( terminalSize != 0 ) { - EntryWithOffset e; - e.nodeOffset = p-start; - e.entry.name = strdup(cummulativeString); - e.entry.flags = read_uleb128(p, end); - if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { - e.entry.address = 0; - e.entry.other = read_uleb128(p, end); // dylib ordinal - e.entry.importName = (char*)p; - } - else { - e.entry.address = read_uleb128(p, end); - if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) - e.entry.other = read_uleb128(p, end); - else - e.entry.other = 0; - e.entry.importName = NULL; - } - output.push_back(e); - } - const uint8_t childrenCount = *children++; - const uint8_t* s = children; - for (uint8_t i=0; i < childrenCount; ++i) { - int edgeStrLen = 0; - while (*s != '\0') { - cummulativeString[curStrOffset+edgeStrLen] = *s++; - ++edgeStrLen; - } - cummulativeString[curStrOffset+edgeStrLen] = *s++; - uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end); - processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); - } -} - - -inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) -{ - // empty trie has no entries - if ( start == end ) - return; - char cummulativeString[32000]; - std::vector entries; - processExportNode(start, start, end, cummulativeString, 0, entries); - // to preserve tie layout order, sort by node offset - std::sort(entries.begin(), entries.end()); - // copy to output - output.reserve(entries.size()); - for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) - output.push_back(it->entry); -} - - - - -}; // namespace trie -}; // namespace mach_o - - -#endif // __MACH_O_TRIE__ - - diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp deleted file mode 100644 index 2196ac3..0000000 --- a/launch-cache/dsc_iterator.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2009-2012 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include - - -#include "dsc_iterator.h" -#include "dyld_cache_format.h" -#define NO_ULEB -#include "Architectures.hpp" -#include "MachOFileAbstraction.hpp" -#include "CacheFileAbstraction.hpp" -#include "SupportedArchs.h" - -namespace dyld { - - - // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped - template - const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) - { - const dyldCacheHeader* header = (dyldCacheHeader*)cache; - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; - for (uint32_t i=0; i < header->mappingCount(); ++i) { - if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { - uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); - const uint8_t* result = &cache[cacheOffset]; - if ( result < cacheEnd ) - return result; - else - return NULL; - } - } - return NULL; - } - - // call the callback block on each segment in this image - template - int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, uint64_t cache_unslid_base_address, - void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) - { - typedef typename A::P P; - typedef typename A::P::E E; - dyld_shared_cache_dylib_info dylibInfo; - dyld_shared_cache_segment_info segInfo; - dylibInfo.version = 2; - dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment - dylibInfo.machHeader = machHeader; - dylibInfo.path = dylibPath; - dylibInfo.inode = inode; - dylibInfo.modTime = modTime; - const macho_header

* mh = (const macho_header

*)machHeader; - const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); - if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) - return -1; - const uint32_t cmd_count = mh->ncmds(); - const macho_load_command

* cmd = cmds; - // scan for LC_UUID - dylibInfo.uuid = NULL; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == LC_UUID ) { - const uuid_command* uc = (const uuid_command*)cmd; - dylibInfo.uuid = &uc->uuid; - break; - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - // callback for each LC_SEGMENT - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - macho_segment_command

* segCmd = (macho_segment_command

*)cmd; - uint64_t fileOffset = segCmd->fileoff(); - // work around until is fixed - if ( fileOffset == 0 ) { - fileOffset = (machHeader - cache); - } - uint64_t sizem = segCmd->vmsize(); - if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { - // clip LINKEDIT size if bigger than cache file - if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) - sizem = (cacheEnd-cache)-fileOffset; - } - segInfo.version = 2; - segInfo.name = segCmd->segname(); - segInfo.fileOffset = fileOffset; - segInfo.fileSize = sizem; - if ( segCmd->filesize() > segCmd->vmsize() ) - return -1; - segInfo.address = segCmd->vmaddr(); - segInfo.addressOffset = segInfo.address - cache_unslid_base_address; - callback(&dylibInfo, &segInfo); - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - return 0; - } - - - // call walkSegments on each image in the cache - template - int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) - { - // Sanity check there is at least a header - if ( (size > 0) && (size < 0x7000) ) - return -1; - typedef typename A::P::E E; - typedef typename A::P P; - const dyldCacheHeader* header = (dyldCacheHeader*)cache; - const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; - uint64_t unslid_base_address = mappings[0].address(); - uint64_t greatestMappingOffset = 0; - for (uint32_t i=0; i < header->mappingCount(); ++i) { - if ( (size != 0) && (mappings[i].file_offset() > size) ) - return -1; - uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); - if ( (size != 0) && (endOffset > size) ) - return -1; - if ( endOffset > greatestMappingOffset ) - greatestMappingOffset = endOffset; - } - const uint8_t* cacheEnd = &cache[size]; - if ( size == 0 ) { - // Zero size means old API is being used, assume all mapped - cacheEnd = &cache[greatestMappingOffset]; - } - else { - // verifiy mappings are not bigger than size - if ( size < greatestMappingOffset ) - return -1; - } - // verify all image infos are mapped - if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) - return -1; - const uint8_t* firstSeg = NULL; - for (uint32_t i=0; i < header->imagesCount(); ++i) { - const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); - uint64_t inode = dylibs[i].inode(); - uint64_t modTime = dylibs[i].modTime(); - if ( (const uint8_t*)dylibPath > cacheEnd ) - return -1; - const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); - if ( machHeader == NULL ) - return -1; - if ( machHeader > cacheEnd ) - return -1; - if ( firstSeg == NULL ) - firstSeg = machHeader; - int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback); - if ( result != 0 ) - return result; - } - return 0; - } - -} - - -// Given a pointer to an in-memory copy of a dyld shared cache file, -// this routine will call the callback block once for each segment -// in each dylib in the shared cache file. -// Returns -1 if there was an error, otherwise 0. -extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, - void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { - const uint8_t* cache = (uint8_t*)shared_cache_file; - if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); - else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); -#if SUPPORT_ARCH_arm64_32 - else if ( strcmp((char*)cache, "dyld_v1arm64_32") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); -#endif -#if SUPPORT_ARCH_arm64e - else if ( strcmp((char*)cache, "dyld_v1 arm64e") == 0 ) - return dyld::walkImages(cache, shared_cache_size, callback); -#endif - else - return -1; -} - - -// implement old version by calling new version -int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) -{ - return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { - callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); - }); -} - -// implement non-block version by calling block version -int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) -{ - return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, - uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { - (*func)(dylibName, segName, offset, size, mappedddress, slide, userData); - }); -} - - -// implement non-slide version by wrapping slide version in block -int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) -{ - dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, - uint64_t size, uint64_t mappedddress, uint64_t slide) { - callback(dylibName, segName, offset, size, mappedddress); - }; - return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb); -} - -// implement non-slide,non-block version by wrapping slide version in block -int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData) -{ - return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, - uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) { - (*func)(dylibName, segName, offset, size, mappedddress, userData); - }); -} - diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h deleted file mode 100644 index eda7c61..0000000 --- a/launch-cache/dyld_cache_format.h +++ /dev/null @@ -1,375 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2006-2015 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef __DYLD_CACHE_FORMAT__ -#define __DYLD_CACHE_FORMAT__ - -#include -#include -#include -#include - - -struct dyld_cache_header -{ - char magic[16]; // e.g. "dyld_v0 i386" - uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info - uint32_t mappingCount; // number of dyld_cache_mapping_info entries - uint32_t imagesOffset; // file offset to first dyld_cache_image_info - uint32_t imagesCount; // number of dyld_cache_image_info entries - uint64_t dyldBaseAddress; // base address of dyld when cache was built - uint64_t codeSignatureOffset; // file offset of code signature blob - uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) - uint64_t slideInfoOffset; // file offset of kernel slid info - uint64_t slideInfoSize; // size of kernel slid info - uint64_t localSymbolsOffset; // file offset of where local symbols are stored - uint64_t localSymbolsSize; // size of local symbols information - uint8_t uuid[16]; // unique value for each shared cache file - uint64_t cacheType; // 0 for development, 1 for production - uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses - uint32_t branchPoolsCount; // number of uint64_t entries - uint64_t accelerateInfoAddr; // (unslid) address of optimization info - uint64_t accelerateInfoSize; // size of optimization info - uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info - uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries -}; - - -struct dyld_cache_mapping_info { - uint64_t address; - uint64_t size; - uint64_t fileOffset; - uint32_t maxProt; - uint32_t initProt; -}; - -struct dyld_cache_image_info -{ - uint64_t address; - uint64_t modTime; - uint64_t inode; - uint32_t pathFileOffset; - uint32_t pad; -}; - -struct dyld_cache_image_info_extra -{ - uint64_t exportsTrieAddr; // address of trie in unslid cache - uint64_t weakBindingsAddr; - uint32_t exportsTrieSize; - uint32_t weakBindingsSize; - uint32_t dependentsStartArrayIndex; - uint32_t reExportsStartArrayIndex; -}; - - -struct dyld_cache_accelerator_info -{ - uint32_t version; // currently 1 - uint32_t imageExtrasCount; // does not include aliases - uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra - uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes - uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths - uint32_t dylibTrieSize; // size of trie containing all dylib paths - uint32_t initializersOffset; // offset into this chunk to start of initializers list - uint32_t initializersCount; // size of initializers list - uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list - uint32_t dofSectionsCount; // size of initializers list - uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports - uint32_t reExportCount; // size of re-exports - uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) - uint32_t depListCount; // size of dependencies - uint32_t rangeTableOffset; // offset into this chunk to start of ss - uint32_t rangeTableCount; // size of dependencies - uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache -}; - -struct dyld_cache_accelerator_initializer -{ - uint32_t functionOffset; // address offset from start of cache mapping - uint32_t imageIndex; -}; - -struct dyld_cache_range_entry -{ - uint64_t startAddress; // unslid address of start of region - uint32_t size; - uint32_t imageIndex; -}; - -struct dyld_cache_accelerator_dof -{ - uint64_t sectionAddress; // unslid address of start of region - uint32_t sectionSize; - uint32_t imageIndex; -}; - -struct dyld_cache_image_text_info -{ - uuid_t uuid; - uint64_t loadAddress; // unslid address of start of __TEXT - uint32_t textSegmentSize; - uint32_t pathOffset; // offset from start of cache file -}; - -// The rebasing info is to allow the kernel to lazily rebase DATA pages of the -// dyld shared cache. Rebasing is adding the slide to interior pointers. -struct dyld_cache_slide_info -{ - uint32_t version; // currently 1 - uint32_t toc_offset; - uint32_t toc_count; - uint32_t entries_offset; - uint32_t entries_count; - uint32_t entries_size; // currently 128 - // uint16_t toc[toc_count]; - // entrybitmap entries[entries_count]; -}; - - -// The version 2 of the slide info uses a different compression scheme. Since -// only interior pointers (pointers that point within the cache) are rebased -// (slid), we know the possible range of the pointers and thus know there are -// unused bits in each pointer. We use those bits to form a linked list of -// locations needing rebasing in each page. -// -// Definitions: -// -// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size -// pageStarts[] = info + info->page_starts_offset -// pageExtras[] = info + info->page_extras_offset -// valueMask = ~(info->delta_mask) -// deltaShift = __builtin_ctzll(info->delta_mask) - 2 -// -// There are three cases: -// -// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE -// The page contains no values that need rebasing. -// -// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 -// All rebase locations are in one linked list. The offset of the first -// rebase location in the page is pageStarts[pageIndex] * 4. -// -// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA -// Multiple linked lists are needed for all rebase locations in a page. -// The pagesExtras array contains 2 or more entries each of which is the -// start of a new linked list in the page. The first is at: -// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) -// The next is at extrasStartIndex+1. The last is denoted by -// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] -// set. -// -// For 64-bit architectures, there is always enough free bits to encode all -// possible deltas. The info->delta_mask field shows where the delta is located -// in the pointer. That value must be masked off (valueMask) before the slide -// is added to the pointer. -// -// For 32-bit architectures, there are only three bits free (the three most -// significant bits). To extract the delta, you must first subtract value_add -// from the pointer value, then AND with delta_mask, then shift by deltaShift. -// That still leaves a maximum delta to the next rebase location of 28 bytes. -// To reduce the number or chains needed, an optimization was added. Turns -// out zero is common in the DATA region. A zero can be turned into a -// non-rebasing entry in the linked list. The can be done because nothing -// in the shared cache should point out of its dylib to the start of the shared -// cache. -// -// The code for processing a linked list (chain) is: -// -// uint32_t delta = 1; -// while ( delta != 0 ) { -// uint8_t* loc = pageStart + pageOffset; -// uintptr_t rawValue = *((uintptr_t*)loc); -// delta = ((rawValue & deltaMask) >> deltaShift); -// uintptr_t newValue = (rawValue & valueMask); -// if ( newValue != 0 ) { -// newValue += valueAdd; -// newValue += slideAmount; -// } -// *((uintptr_t*)loc) = newValue; -// pageOffset += delta; -// } -// -// -struct dyld_cache_slide_info2 -{ - uint32_t version; // currently 2 - uint32_t page_size; // currently 4096 (may also be 16384) - uint32_t page_starts_offset; - uint32_t page_starts_count; - uint32_t page_extras_offset; - uint32_t page_extras_count; - uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location - uint64_t value_add; - //uint16_t page_starts[page_starts_count]; - //uint16_t page_extras[page_extras_count]; -}; -#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags -#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) -#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing -#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page - - - -// 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 - uint32_t nlistCount; // count of nlist entries - uint32_t stringsOffset; // offset into this chunk of string pool - uint32_t stringsSize; // byte count of string pool - uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry - uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array -}; - -struct dyld_cache_local_symbols_entry -{ - uint32_t dylibOffset; // offset in cache file of start of dylib - uint32_t nlistStartIndex; // start index of locals for this dylib - uint32_t nlistCount; // number of local symbols for this dylib -}; - -struct dyld_cache_image_patches -{ - uint32_t patchExportsStartIndex; - uint32_t patchExportsCount; -}; - -struct dyld_cache_patchable_export -{ - uint32_t cacheOffsetOfImpl; - uint32_t patchLocationsStartIndex; - uint32_t patchLocationsCount; - uint32_t exportNameOffset; -}; - -struct dyld_cache_patchable_location -{ - uint64_t cacheOffset : 32, - addend : 12, // +/- 2048 - authenticated : 1, - usesAddressDiversity : 1, - key : 2, - discriminator : 16; - - dyld_cache_patchable_location(size_t cacheOffset, uint64_t addend); - //dyld_cache_patchable_location(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 dyld_cache_patchable_location& other) const { - return this->cacheOffset == other.cacheOffset; - } -}; - - - -#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" -#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" -#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" -#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" - -static const uint64_t kDyldSharedCacheTypeDevelopment = 0; -static const uint64_t kDyldSharedCacheTypeProduction = 1; - - - - -#endif // __DYLD_CACHE_FORMAT__ - - diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp deleted file mode 100644 index 5f817ad..0000000 --- a/launch-cache/dyld_shared_cache_util.cpp +++ /dev/null @@ -1,1111 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2009-2012 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "DyldSharedCache.h" -#include "Trie.hpp" - -#include "objc-shared-cache.h" - -#if TARGET_OS_OSX -#define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle" -#else -#define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle" -#endif - -// 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) ) { - 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); - - return (DyldSharedCache*)result; -} - -enum Mode { - modeNone, - modeList, - modeMap, - modeDependencies, - modeSlideInfo, - modeVerboseSlideInfo, - modeAcceleratorInfo, - modeTextInfo, - modeLinkEdit, - modeLocalSymbols, - modeJSONMap, - modeJSONDependents, - modeSectionSizes, - modeStrings, - modeInfo, - modeSize, - modeObjCProtocols, - modeExtract -}; - -struct Options { - Mode mode; - const char* dependentsOfPath; - const char* extractionDir; - bool printUUIDs; - bool printVMAddrs; - bool printDylibVersions; - bool printInodes; -}; - - -void usage() { - fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract [ shared-cache-file ] \n"); -} - -static void checkMode(Mode mode) { - if ( mode != modeNone ) { - fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n"); - usage(); - exit(1); - } -} - -static bool isAlias(const char* path, const DyldSharedCache* dyldCache) { - const dyld_cache_header* header = &dyldCache->header; - const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset); - const dyld_cache_mapping_info* textMapping = &mappings[0]; - // paths for aliases are store between cache header and first segment - return path < (char*)textMapping; -} - -int main (int argc, const char* argv[]) { - - const char* sharedCachePath = nullptr; - - Options options; - options.mode = modeNone; - options.printUUIDs = false; - options.printVMAddrs = false; - options.printDylibVersions = false; - options.printInodes = false; - 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] == '-') { - if (strcmp(opt, "-list") == 0) { - checkMode(options.mode); - options.mode = modeList; - } - else if (strcmp(opt, "-dependents") == 0) { - checkMode(options.mode); - options.mode = modeDependencies; - options.dependentsOfPath = argv[++i]; - if ( i >= argc ) { - fprintf(stderr, "Error: option -depdendents requires an argument\n"); - usage(); - exit(1); - } - } - else if (strcmp(opt, "-linkedit") == 0) { - checkMode(options.mode); - options.mode = modeLinkEdit; - } - else if (strcmp(opt, "-info") == 0) { - checkMode(options.mode); - options.mode = modeInfo; - } - else if (strcmp(opt, "-slide_info") == 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; - } - else if (strcmp(opt, "-text_info") == 0) { - checkMode(options.mode); - options.mode = modeTextInfo; - } - else if (strcmp(opt, "-local_symbols") == 0) { - checkMode(options.mode); - options.mode = modeLocalSymbols; - } - else if (strcmp(opt, "-strings") == 0) { - if (options.mode != modeStrings) - checkMode(options.mode); - options.mode = modeStrings; - printStrings = true; - } - else if (strcmp(opt, "-sections") == 0) { - checkMode(options.mode); - options.mode = modeSectionSizes; - } - 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; - } - else if (strcmp(opt, "-json-map") == 0) { - checkMode(options.mode); - options.mode = modeJSONMap; - } - else if (strcmp(opt, "-json-dependents") == 0) { - checkMode(options.mode); - options.mode = modeJSONDependents; - } - else if (strcmp(opt, "-size") == 0) { - checkMode(options.mode); - options.mode = modeSize; - } - else if (strcmp(opt, "-objc-protocols") == 0) { - checkMode(options.mode); - options.mode = modeObjCProtocols; - } - else if (strcmp(opt, "-extract") == 0) { - checkMode(options.mode); - options.mode = modeExtract; - options.extractionDir = argv[++i]; - if ( i >= argc ) { - fprintf(stderr, "Error: option -extract requires a directory argument\n"); - usage(); - exit(1); - } - } - else if (strcmp(opt, "-uuid") == 0) { - options.printUUIDs = true; - } - else if (strcmp(opt, "-inode") == 0) { - options.printInodes = true; - } - else if (strcmp(opt, "-versions") == 0) { - options.printDylibVersions = true; - } - else if (strcmp(opt, "-vmaddr") == 0) { - options.printVMAddrs = true; - } - else { - fprintf(stderr, "Error: unrecognized option %s\n", opt); - usage(); - exit(1); - } - } - else { - sharedCachePath = opt; - } - } - - if ( options.mode == modeNone ) { - fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); - usage(); - exit(1); - } - - if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) { - if ( options.printUUIDs && (options.mode != modeList) ) - fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); - - if ( options.printVMAddrs && (options.mode != modeList) ) - fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); - - if ( options.printDylibVersions && (options.mode != modeDependencies) ) - fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); - - if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { - fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); - usage(); - exit(1); - } - } - - const DyldSharedCache* dyldCache = nullptr; - if ( sharedCachePath != nullptr ) { - dyldCache = mapCacheFile(sharedCachePath); - // mapCacheFile prints an error if something goes wrong, so just return in that case. - if ( dyldCache == nullptr ) - return 1; - } - else { -#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) - size_t cacheLength; - dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength); -#endif - if (dyldCache == nullptr) { - fprintf(stderr, "Could not get in-memory shared cache\n"); - return 1; - } - } - - if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) { - const dyld_cache_header* header = &dyldCache->header; - if ( header->slideInfoOffset == 0 ) { - fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); - exit(1); - } - const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset); - const dyld_cache_mapping_info* dataMapping = &mappings[1]; - uint64_t dataStartAddress = dataMapping->address; - uint64_t dataSize = dataMapping->size; - - const dyld_cache_slide_info* slideInfoHeader = dyldCache->slideInfo(); - 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 dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset); - const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset); - for(int i=0; i < slideInfoHeader->toc_count; ++i) { - printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]); - const dyld_cache_slide_info_entry* entry = &entries[tocs[i]]; - for(int j=0; j < slideInfoHeader->entries_size; ++j) - printf("%02X", entry->bits[j]); - printf("\n"); - } - } - else if ( slideInfoHeader->version == 2 ) { - const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(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 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 = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value); - pageOffset += delta; - } - }; - const uint8_t* dataPagesStart = dyldCache->dataRegionStart(); - if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { - printf("page[% 5d]: no rebasing\n", i); - } - else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { - printf("page[% 5d]: ", i); - int j=(start & 0x3FFF); - bool done = false; - do { - uint16_t aStart = extras[j]; - printf("start=0x%04X ", aStart & 0x3FFF); - 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 ); - 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; - rebaseChain(page, pageStartOffset); - } - } - } - } - else if ( slideInfoHeader->version == 3 ) { - const dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(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 uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add); - const uint8_t* dataSegmentStart = dyldCache->dataRegionStart(); - for (int i=0; i < slideInfo->page_starts_count; ++i) { - uint16_t delta = slideInfo->page_starts[i]; - if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) { - printf("page[% 5d]: no rebasing\n", i); - continue; - } - - printf("page[% 5d]: start=0x%04X\n", i, delta); - if ( options.mode != modeVerboseSlideInfo ) - continue; - - delta = delta/sizeof(uint64_t); // initial offset is byte based - const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size); - const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart; - do { - loc += delta; - delta = loc->plain.offsetToNextPointer; - dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr; - ptr.raw64 = *((uint64_t*)loc); - if ( loc->auth.authenticated ) { - uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase; - uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target); - printf(" [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n", - i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue, - ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false", - ptr.arm64e.keyName()); - } - else { - uint64_t targetValue = ptr.arm64e.unpackTarget(); - printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - 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 = dyldCache->dataRegionStart(); - 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); - } - } - } - } - } - else if ( options.mode == modeInfo ) { - const dyld_cache_header* header = &dyldCache->header; - printf("uuid: "); - if ( header->mappingOffset >= 0x68 ) { - const uint8_t* uuid = header->uuid; - printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", - 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]); - } - else { - printf("n/a\n"); - } - if ( header->mappingOffset >= 0xE0 ) { - // HACK until this uses new header - uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8)); - uint32_t 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 ) - printf("platform: iOS simulator\n"); - else - printf("platform: iOS\n"); - break; - case 3: - if ( simulator ) - printf("platform: tvOS simulator\n"); - else - printf("platform: tvOS\n"); - break; - case 4: - if ( simulator ) - printf("platform: watchOS simulator\n"); - else - printf("platform: watchOS\n"); - break; - case 5: - printf("platform: bridgeOS\n"); - break; - default: - printf("platform: 0x%08X 0x%08X\n", platform, simulator); - } - printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I"); - } - printf("cache type: %s\n", header->cacheType ? "production" : "development"); - printf("image count: %u\n", header->imagesCount); - if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) { - printf("branch pool count: %u\n", header->branchPoolsCount); - } - if ( header->slideInfoSize > 0 ) { - uint32_t pageSize = 0x4000; // fix me for intel - uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize); - uint32_t entropyBits = 32 - __builtin_clz(possibleSlideValues - 1); - printf("ASLR entropy: %u-bits\n", entropyBits); - } - printf("mappings:\n"); - const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset); - for (uint32_t i=0; i < header->mappingCount; ++i) { - if ( mappings[i].initProt & VM_PROT_EXECUTE ) - printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", - mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size, - mappings[i].address, mappings[i].address + mappings[i].size); - else if ( mappings[i].initProt & VM_PROT_WRITE ) - printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", - mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size, - mappings[i].address, mappings[i].address + mappings[i].size); - else if ( mappings[i].initProt & VM_PROT_READ ) - printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", - mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size, - mappings[i].address, mappings[i].address + mappings[i].size); - } - if ( header->codeSignatureOffset != 0 ) { - uint64_t size = header->codeSignatureSize; - 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", - size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size); - } - printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n", - header->slideInfoSize/1024, header->slideInfoOffset, header->slideInfoOffset + header->slideInfoSize); - if ( header->localSymbolsOffset != 0 ) - printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n", - header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize); - if ( (header->mappingOffset >= 0x78) && (header->accelerateInfoSize != 0) ) - printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n", - header->accelerateInfoSize/1024, header->accelerateInfoAddr, header->accelerateInfoAddr + header->accelerateInfoSize); - } - else if ( options.mode == modeAcceleratorInfo ) { - const dyld_cache_header* header = &dyldCache->header; - if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->accelerateInfoSize == 0) ) { - printf("no accelerator info\n"); - } - else { - const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset); - uint64_t aiAddr = header->accelerateInfoAddr; - const dyld_cache_accelerator_info* accelInfo = NULL; - for (uint32_t i=0; i < header->mappingCount; ++i) { - if ( (mappings[i].address <= aiAddr) && (aiAddr < mappings[i].address+mappings[i].size) ) { - uint64_t offset = aiAddr - mappings[i].address + mappings[i].fileOffset; - accelInfo = (dyld_cache_accelerator_info*)((uint8_t*)dyldCache + offset); - } - } - if ( accelInfo == NULL ) { - printf("accelerator info not in any mapped range\n"); - } - else { - const dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset); - const dyld_cache_image_info_extra* imagesExtra = (dyld_cache_image_info_extra*)((char*)accelInfo + accelInfo->imagesExtrasOffset); - const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset); - const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset); - printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount); - for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) { - printf(" image[%3u] %s:\n", i, (char*)dyldCache +images[i].pathFileOffset); - printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr, imagesExtra[i].exportsTrieSize); - if ( imagesExtra[i].weakBindingsSize ) - printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr, imagesExtra[i].weakBindingsSize); - printf(" dependents: "); - for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex; dependencyArray[d] != 0xFFFF; ++d) { - uint16_t depIndex = dependencyArray[d]; - if ( depIndex & 0x8000 ) - printf(" up(%d) ", depIndex & 0x7FFF); - else - printf(" %d ", depIndex); - } - printf("\n"); - printf(" re-exports: "); - for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex; reExportArray[r] != 0xFFFF; ++r) - printf(" %d ", reExportArray[r]); - printf("\n"); - } - printf("libdyld.dylib:\n"); - printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr); - printf("initializers (count=%u):\n", accelInfo->initializersCount); - const dyld_cache_accelerator_initializer* initializers = (dyld_cache_accelerator_initializer*)((char*)accelInfo + accelInfo->initializersOffset); - for (uint32_t i=0; i < accelInfo->initializersCount; ++i) { - printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex, mappings[0].address + initializers[i].functionOffset); - } - printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount); - const dyld_cache_accelerator_dof* dofs = (dyld_cache_accelerator_dof*)((char*)accelInfo + accelInfo->dofSectionsOffset); - for (uint32_t i=0; i < accelInfo->dofSectionsCount; ++i) { - printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex, dofs[i].sectionAddress, dofs[i].sectionAddress+dofs[i].sectionSize); - } - printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount); - const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset); - for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) { - unsigned imageIndex = bottomUpArray[i]; - if ( imageIndex < accelInfo->imageExtrasCount ) - printf(" image[%3u] %s\n", imageIndex, (char*)dyldCache + images[imageIndex].pathFileOffset); - else - printf(" image[%3u] BAD INDEX\n", imageIndex); - } - printf("range table (count=%u):\n", accelInfo->rangeTableCount); - const dyld_cache_range_entry* rangeTable = (dyld_cache_range_entry*)((char*)accelInfo + accelInfo->rangeTableOffset); - for (uint32_t i=0; i < accelInfo->rangeTableCount; ++i) { - const dyld_cache_range_entry& entry = rangeTable[i]; - printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress, entry.startAddress + entry.size, (char*)dyldCache + images[entry.imageIndex].pathFileOffset); - } - printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize); - const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset; - const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize; - std::vector dylibEntries; - if ( !Trie::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) - printf(" malformed dylibs trie\n"); - for (const DylibIndexTrie::Entry& x : dylibEntries) { - printf(" image[%3u] %s\n", x.info.index, x.name.c_str()); - } - } - } - } - else if ( options.mode == modeTextInfo ) { - const dyld_cache_header* header = &dyldCache->header; - if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->imagesTextCount == 0) ) { - printf("no text info\n"); - } - else { - const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)dyldCache + header->imagesTextOffset); - const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header->imagesTextCount]; - printf("dylib text infos (count=%llu):\n", header->imagesTextCount); - for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) { - printf(" 0x%09llX -> 0x%09llX <", p->loadAddress, p->loadAddress + p->textSegmentSize); - for (int i=0; i<16; ++i) { - switch (i) { - case 4: - case 6: - case 8: - case 10: - printf("-"); - break; - } - printf("%02X", p->uuid[i]); - } - printf("> %s\n", (char*)dyldCache + p->pathOffset); - } - } - } - else if ( options.mode == modeLocalSymbols ) { - const dyld_cache_header* header = &dyldCache->header; - if ( header->localSymbolsOffset == 0 ) { - fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n"); - exit(1); - } - const bool is64 = (strstr((char*)dyldCache, "64") != NULL); - const dyld_cache_image_info* imageInfos = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset); - const dyld_cache_local_symbols_info* localsInfo = (dyld_cache_local_symbols_info*)((char*)dyldCache + header->localSymbolsOffset); - const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->nlistOffset); - const uint32_t nlistCount = localsInfo->nlistCount; - const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12; - const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->stringsOffset); - const uint32_t stringsSize = localsInfo->stringsSize; - const uint32_t entriesCount = localsInfo->entriesCount; - const dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)((char*)localsInfo + localsInfo->entriesOffset); - printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize); - printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize); - printf("local symbols by dylib (count=%d):\n", entriesCount); - //const char* stringPool = (char*)dyldCache + stringsFileOffset; - for (int i=0; i < entriesCount; ++i) { - const char* imageName = (char*)dyldCache + imageInfos[i].pathFileOffset; - printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex, entries[i].nlistCount, imageName); -#if 0 - if ( is64 ) { - const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset); - for (int e=0; e < entries[i].nlistCount(); ++e) { - const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e]; - printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]); - printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value); - } - } -#endif - } - } - else if ( options.mode == modeJSONMap ) { - std::string buffer = dyldCache->generateJSONMap("unknown"); - printf("%s\n", buffer.c_str()); - } - else if ( options.mode == modeJSONDependents ) { - std::cout << dyldCache->generateJSONDependents(); - } - 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 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 == modeSectionSizes ) { - __block std::map sectionSizes; - dyldCache->forEachImage(^(const mach_header *mh, const char *installName) { - const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh; - ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo §Info, bool malformedSectionRange, bool &stop) { - std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName; - sectionSizes[section] += sectInfo.sectSize; - }); - }); - for (const auto& keyAndValue : sectionSizes) { - printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str()); - } - } - else if ( options.mode == modeObjCProtocols ) { - if ( dyldCache->objcOpt() == nullptr ) { - fprintf(stderr, "Error: could not get optimized objc\n"); - return 1; - } - objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2(); - if ( protocols == nullptr ) { - fprintf(stderr, "Error: could not get optimized objc protocols\n"); - return 1; - } - - for (uint64_t index = 0; index != protocols->capacity; ++index) { - const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index]; - if ( clshi.clsOffset == 0 ) { - fprintf(stderr, "[% 5lld]\n", index); - continue; - } - const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]); - if ( !clshi.isDuplicate() ) { - fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name); - continue; - } - - // class appears in more than one header - uint32_t count = clshi.duplicateCount(); - fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n", - index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name); - - const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()]; - for (uint32_t i = 0; i < count; i++) { - fprintf(stderr, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset); - } - } - } - else if ( options.mode == modeExtract ) { - char pathBuffer[PATH_MAX]; - uint32_t bufferSize = PATH_MAX; - if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) { - fprintf(stderr, "Error: could not get path of program\n"); - return 1; - } - char* last = strrchr(pathBuffer, '/'); - // The bundle is at a different location on device. Its /usr/lib/dsc_extractor.bundle in the SDK - // but /usr/local/lib/dsc_extractor.bundle on device. - strcpy(last+1, DSC_BUNDLE_REL_PATH); - void* handle = dlopen(pathBuffer, RTLD_LAZY); - if ( handle == NULL ) { - fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer); - return 1; - } - - typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, - void (^progress)(unsigned current, unsigned total)); - - extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); - if ( proc == NULL ) { - fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); - return 1; - } - - int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } ); - return result; - } - else { - switch ( options.mode ) { - case modeList: { - if (options.printInodes) { - dyldCache->forEachImageEntry(^(const char* path, uint64_t mTime, uint64_t inode) { - printf("0x%08llX 0x%08llX ", inode, mTime); - if ( isAlias(path, dyldCache) ) - printf("[alias] %s\n", path); - else - printf("%s\n", path); - }); - } else { - dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) { - if ( options.printVMAddrs ) - printf("0x%08llX ", loadAddressUnslid); - if ( options.printUUIDs ) { - const uint8_t* uuid = (uint8_t*)dylibUUID; - printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ", - 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]); - } - if ( isAlias(installName, dyldCache) ) - printf("[alias] %s\n", installName); - else - printf("%s\n", installName); - }); - } - break; - } - case modeMap: { - __block std::map dataSegNames; - __block std::map dataSegEnds; - dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { - dyld3::MachOFile* mf = (dyld3::MachOFile*)mh; - mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { - if ( isAlias(installName, dyldCache) ) - return; - printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName); - if ( strncmp(info.segName, "__DATA", 6) == 0 ) { - dataSegNames[info.vmAddr] = installName; - dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize; - } - }); - }); - // Enhance dyld_shared_cache_util to show where section alignment added padding - uint64_t lastEnd = 0; - for (const auto& entry : dataSegEnds) { - uint64_t padding = entry.first - lastEnd; - if ( (padding > 32) && (lastEnd != 0) ) { - printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024); - } - lastEnd = entry.second; - } - break; - } - case modeDependencies: { - __block bool dependentTargetFound = false; - dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { - if ( strcmp(options.dependentsOfPath, installName) != 0 ) - return; - dependentTargetFound = true; - - auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) { - if ( options.printDylibVersions ) { - uint32_t compat_vers = compatVersion; - uint32_t current_vers = curVersion; - printf("\t%s", loadPath); - if ( compat_vers != 0xFFFFFFFF ) { - printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", - (compat_vers >> 16), - (compat_vers >> 8) & 0xff, - (compat_vers) & 0xff, - (current_vers >> 16), - (current_vers >> 8) & 0xff, - (current_vers) & 0xff); - } - else { - printf("\n"); - } - } - else { - printf("\t%s\n", loadPath); - } - }; - - dyld3::MachOFile* mf = (dyld3::MachOFile*)mh; - - // First print out our dylib and version. - const char* dylibInstallName; - uint32_t currentVersion; - uint32_t compatVersion; - if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, ¤tVersion) ) { - printDep(dylibInstallName, compatVersion, currentVersion); - } - - // Then the dependent dylibs. - mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { - printDep(loadPath, compatVersion, curVersion); - }); - }); - if (options.dependentsOfPath && !dependentTargetFound) { - fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); - exit(1); - } - break; - } - case modeLinkEdit: { - std::map pageToContent; - auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) { - for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { - std::map::iterator pos = pageToContent.find(p); - if ( pos == pageToContent.end() ) { - pageToContent[p] = strdup(message); - } - else { - const char* oldMessage = pos->second; - char* newMesssage; - asprintf(&newMesssage, "%s, %s", oldMessage, message); - pageToContent[p] = newMesssage; - ::free((void*)oldMessage); - } - } - }; - - dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { - // Filter out symlinks. - if (isAlias(installName, dyldCache)) - return; - dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh; - Diagnostics diag; - dyld3::MachOAnalyzer::LinkEditInfo leInfo; - ma->getLinkEditPointers(diag, leInfo); - - if (diag.hasError()) - return; - - char message[1000]; - const char* shortName = strrchr(installName, '/') + 1; - // add export trie info - if ( leInfo.dyldInfo->export_size != 0 ) { - //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off()); - uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096); - uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096); - sprintf(message, "exports from %s", shortName); - add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message); - } - // add binding info - if ( leInfo.dyldInfo->bind_size != 0 ) { - uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096); - uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096); - sprintf(message, "bindings from %s", shortName); - add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message); - } - // add lazy binding info - if ( leInfo.dyldInfo->lazy_bind_size != 0 ) { - uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096); - uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096); - sprintf(message, "lazy bindings from %s", shortName); - add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message); - } - // add weak binding info - if ( leInfo.dyldInfo->weak_bind_size != 0 ) { - uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096); - uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096); - sprintf(message, "weak bindings from %s", shortName); - add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message); - } - }); - - for (std::map::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) { - printf("0x%08X %s\n", it->first, it->second); - } - break; - } - case modeSize: { - struct TextInfo { - uint64_t textSize; - const char* path; - }; - __block std::vector textSegments; - dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { - // Filter out symlinks. - if (isAlias(installName, dyldCache)) - return; - - dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh; - ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) { - if ( strcmp(info.segName, "__TEXT") != 0 ) - return; - textSegments.push_back({ info.fileSize, installName }); - }); - }); - std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) { - return (left.textSize > right.textSize); - }); - for (std::vector::iterator it = textSegments.begin(); it != textSegments.end(); ++it) { - printf(" 0x%08llX %s\n", it->textSize, it->path); - } - break; - } - case modeNone: - case modeInfo: - case modeSlideInfo: - case modeVerboseSlideInfo: - case modeAcceleratorInfo: - case modeTextInfo: - case modeLocalSymbols: - case modeJSONMap: - case modeJSONDependents: - case modeSectionSizes: - case modeStrings: - case modeObjCProtocols: - case modeExtract: - break; - } - } - return 0; -} - diff --git a/local_test_runner/ContainerizedTestRunner.mm b/local_test_runner/ContainerizedTestRunner.mm new file mode 100644 index 0000000..016b31c --- /dev/null +++ b/local_test_runner/ContainerizedTestRunner.mm @@ -0,0 +1,212 @@ +/* +* Copyright (c) 2019 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@ +*/ + +#import +#include +#include + +#include "test_support.h" + +struct TestInfo +{ + const char* testName; + const char* runLine; +}; + +#include "XCTestGenerated.h" + +@interface ContainerizedTestRunner : XCTestCase +- (void)launchTest:(const char *)test withRunLine:(const char *)runLine; +@end + +@implementation ContainerizedTestRunner +#if 1 ++ (void)registerTest:(const TestInfo &)info withEnv:(const char *)env andExtensions:(const char *)extension { + char *fullRunline = NULL; + asprintf(&fullRunline, "TMPDIR=/tmp/ TEST_OUTPUT=XCTest %s %s", env, info.runLine); + unsigned char hash[CC_SHA1_DIGEST_LENGTH]; + char hashStr[CC_SHA1_DIGEST_LENGTH*2+1]; + CC_SHA1(fullRunline, (CC_LONG)strlen(fullRunline), &hash[0]); + snprintf(&hashStr[0], CC_SHA1_DIGEST_LENGTH*2+1, "%x%x%x%x", hash[0], hash[1], hash[2], hash[3]); + char buffer[4096]; + snprintf(&buffer[0], 4096, "test_%s_%s_%s", info.testName, extension, hashStr); + SEL newSel = sel_registerName(buffer); + IMP newIMP = imp_implementationWithBlock(^(id self) { + [self launchTest:info.testName withRunLine:fullRunline]; + }); + class_addMethod([self class], newSel, newIMP, "v@:"); +} + ++ (void)load { + for (const TestInfo& info : sTests) { + if ( strstr(info.runLine, "run-static") != nullptr ) { + [self registerTest:info withEnv:"" andExtensions:"static"]; + } else { + [self registerTest:info withEnv:"DYLD_USE_CLOSURES=0" andExtensions:"dyld2"]; + [self registerTest:info withEnv:"DYLD_USE_CLOSURES=1" andExtensions:"dyld3"]; + } +#if 0 + [self registerTest:info withEnv:"DYLD_USE_CLOSURES=0 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_leaks"]; + [self registerTest:info withEnv:"DYLD_USE_CLOSURES=1 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_leaks"]; +#endif + }; +} + +#else +// This would be the way to do it if XCTest did not insist on using the selector name for differentiating results in the Xcode UI ++ (NSArray *)testInvocations { + static NSArray *invocations = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableArray *invocationArray = [[NSMutableArray alloc] init]; + for (const TestInfo& info : sTests) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[ContainerizedTestRunner class] + instanceMethodSignatureForSelector:@selector(launchTest:withRunLine:)]]; + invocation.selector = @selector(launchTest:withRunLine:); + [invocation setArgument:(void*)&info.testName atIndex:2]; + [invocation setArgument:(void*)&info.runLine atIndex:3]; + [invocationArray addObject:invocation]; + } + invocations = [NSArray arrayWithArray:invocationArray]; + }); + return [invocations copy]; +} + +- (NSString *) name { + const char *testName = NULL; + const char *runLine = NULL; + [self.invocation getArgument:(void*)&testName atIndex:2]; + [self.invocation getArgument:(void*)&runLine atIndex:3]; + return [NSString stringWithFormat:@"%s: %s", testName, runLine]; +} + +- (NSString *)nameForLegacyLogging +{ + return self.name; +} +#endif + +- (void)setUp { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _process process; + fprintf(stderr, "CHROOT: %s\n", CHROOT_PATH); + process.set_executable_path("/usr/sbin/chroot"); + const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", "/sbin/mount -t devfs devfs /dev", NULL}; + process.set_args(args); + process.launch(); + }); +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void) executeCommandInContainer:(const char *)command { + _process process; + process.set_executable_path("/usr/sbin/chroot"); + const char *args[] = {CHROOT_PATH, "/bin/sh", "-c", command, NULL}; + process.set_args(args); + __block dispatch_data_t output = NULL; + process.set_stdout_handler(^(int fd) { + ssize_t size = 0; + do { + char buffer[16384]; + size = read(fd, &buffer[0], 16384); + if (size == -1) { break; } + dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + if (!output) { + output = data; + } else { + output = dispatch_data_create_concat(output, data); + } + } while (size > 0); + }); + process.set_exit_handler(^(pid_t pid) { + int status = 0; + (void)waitpid(pid, &status, 0); + + int exitStatus = WEXITSTATUS(status); + if (exitStatus != 0) { + NSString *failure = [NSString stringWithFormat:@"Test exited with return code %d\n%s", exitStatus, command]; + XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]]; + XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]]; + [self recordIssue: issue]; + return; + } + if (!output) { + NSString *failure = [NSString stringWithFormat:@"Test did not write any data to stdout\n%s", command]; + XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]]; + XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]]; + [self recordIssue: issue]; + return; + } + NSError *error = nil; + NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:(NSData *)output options:NSPropertyListImmutable format:nil error:&error]; + if (!dict) { + NSString *failure = [NSString stringWithFormat:@"Could not convert stdout \"%@\" to property list. Got Error %@\n%s", output, error, command]; + XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]]; + XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]]; + [self recordIssue: issue]; + return; + } + + if (dict[@"LOGS"]) { + NSLog(@"LOGS:\n%@",[NSString stringWithFormat:@"%@\n", dict[@"LOGS"]]); + } + + if (![dict[@"PASS"] boolValue]) { + NSString *failure = [NSString stringWithFormat:@"%@\n%s", dict[@"INFO"], command]; + XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:dict[@"FILE"] lineNumber:(NSInteger)dict[@"LINE"]]]; + XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]]; + [self recordIssue: issue]; + return; + } + }); + process.launch(); +} + + +- (void) launchTest:(const char *)test withRunLine:(const char *)runLine { + char command[4096]; + snprintf(&command[0], 4096, "cd /AppleInternal/CoreOS/tests/dyld/%s; %s", test, runLine); + [self executeCommandInContainer:command]; +// sudo chroot . /bin/sh -c 'TEST_OUTPUT=BATS /AppleInternal/CoreOS/tests/dyld/dyld_get_sdk_version/sdk-check.exe' + +} + +// +//- (void)testExample { +// // This is an example of a functional test case. +// // Use XCTAssert and related functions to verify your tests produce the correct results. +//} +// +//- (void)testPerformanceExample { +// // This is an example of a performance test case. +// [self measureBlock:^{ +// // Put the code you want to measure the time of here. +// }]; +//} + +@end diff --git a/local_test_runner/Info.plist b/local_test_runner/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/local_test_runner/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/sandbox-dummy.c b/sandbox-dummy.c deleted file mode 100644 index 36bc95f..0000000 --- a/sandbox-dummy.c +++ /dev/null @@ -1,3 +0,0 @@ -int sandbox_check(void) { return 0; } -int SANDBOX_CHECK_NO_REPORT = 0; - diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 65e82e8..764b3b8 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -84,6 +84,9 @@ ImageLoader::ImageLoader(const char* path, unsigned int libCount) fPathOwnedByImage(false), fIsReferencedDownward(false), fWeakSymbolsBound(false) { +#if __x86_64__ + fAotPath = NULL; +#endif if ( fPath != NULL ) fPathHash = hash(fPath); if ( libCount > 512 ) @@ -103,6 +106,10 @@ ImageLoader::~ImageLoader() delete [] fRealPath; if ( fPathOwnedByImage && (fPath != NULL) ) delete [] fPath; +#if __x86_64__ + if ( fAotPath != NULL ) + delete [] fAotPath; +#endif } void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) @@ -389,7 +396,7 @@ uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t a //dyld::log(" interposedAddress: replacee=0x%08llX, replacement=0x%08llX, neverImage=%p, onlyImage=%p, inImage=%p\n", // (uint64_t)it->replacee, (uint64_t)it->replacement, it->neverImage, it->onlyImage, inImage); // replace all references to 'replacee' with 'replacement' - if ( (address == it->replacee) && (inImage != it->neverImage) && ((it->onlyImage == NULL) || (inImage == it->onlyImage)) ) { + if ( (address == it->replacee) && (it->neverImage != inImage) && ((it->onlyImage == NULL) || (it->onlyImage == inImage)) ) { if ( context.verboseInterposing ) { dyld::log("dyld interposing: replace 0x%lX with 0x%lX\n", it->replacee, it->replacement); } @@ -402,12 +409,14 @@ uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t a void ImageLoader::applyInterposingToDyldCache(const LinkContext& context) { if (!context.dyldCache) return; -#if !__arm64e__ // until arm64e cache builder sets builtFromChainedFixups if (!context.dyldCache->header.builtFromChainedFixups) return; -#endif if (fgInterposingTuples.empty()) return; + + // make the cache writable for this block + DyldSharedCache::DataConstScopedWriter patcher(context.dyldCache, mach_task_self(), (context.verboseMapping ? &dyld::log : nullptr)); + // 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; @@ -475,7 +484,7 @@ void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple // dyld should tell the kernel when it is doing root fix-ups void ImageLoader::vmAccountingSetSuspended(const LinkContext& context, bool suspend) { -#if __arm__ || __arm64__ +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR static bool sVmAccountingSuspended = false; if ( suspend == sVmAccountingSuspended ) return; @@ -510,7 +519,7 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr uint64_t t1 = mach_absolute_time(); context.clearAllDepths(); - this->recursiveUpdateDepth(context.imageCount()); + this->updateDepth(context.imageCount()); __block uint64_t t2, t3, t4, t5; { @@ -630,7 +639,8 @@ void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive } } // bind lazies in this image - this->doBindJustLazies(context); + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + this->doBindJustLazies(context, patcher); } } @@ -664,7 +674,19 @@ void ImageLoader::markedUsedRecursive(const std::vector& dynam } -unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) +unsigned int ImageLoader::updateDepth(unsigned int maxDepth) +{ + STACK_ALLOC_ARRAY(ImageLoader*, danglingUpwards, maxDepth); + unsigned int depth = this->recursiveUpdateDepth(maxDepth, danglingUpwards); + for (auto& danglingUpward : danglingUpwards) { + if ( danglingUpward->fDepth != 0) + continue; + danglingUpward->recursiveUpdateDepth(maxDepth, danglingUpwards); + } + return depth; +} + +unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth, dyld3::Array& danglingUpwards) { // the purpose of this phase is to make the images sortable such that // in a sort list of images, every image that an image depends on @@ -677,17 +699,29 @@ unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) unsigned int minDependentDepth = maxDepth; for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); - if ( (dependentImage != NULL) && !libIsUpward(i) ) { - unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth); - if ( d < minDependentDepth ) - minDependentDepth = d; + if ( dependentImage != NULL ) { + if ( libIsUpward(i) ) { + if ( dependentImage->fDepth == 0) { + if ( !danglingUpwards.contains(dependentImage) ) + danglingUpwards.push_back(dependentImage); + } + } else { + unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth, danglingUpwards); + if ( d < minDependentDepth ) + minDependentDepth = d; + } + } + // make sure need to re-bind propagates up + if ( dependentImage != NULL ) { + if ( fAllLibraryChecksumsAndLoadAddressesMatch && !dependentImage->fAllLibraryChecksumsAndLoadAddressesMatch ) { + fAllLibraryChecksumsAndLoadAddressesMatch = false; + } } } - // make me less deep then all my dependents fDepth = minDependentDepth - 1; + } - return fDepth; } @@ -799,22 +833,21 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli // tell each to load its dependents for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); - if ( dependentImage != NULL ) { + if ( dependentImage != NULL ) { dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name); } } - // do deep prebind check if ( fAllLibraryChecksumsAndLoadAddressesMatch ) { for(unsigned int i=0; i < libraryCount(); ++i){ ImageLoader* dependentImage = libImage(i); - if ( dependentImage != NULL ) { + if ( dependentImage != NULL ) { if ( !dependentImage->allDependentLibrariesAsWhenPreBound() ) fAllLibraryChecksumsAndLoadAddressesMatch = false; } } } - + // free rpaths (getRPaths() malloc'ed each string) for(std::vector::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) { const char* str = *it; @@ -912,11 +945,11 @@ void ImageLoader::recursiveMakeDataReadOnly(const LinkContext& context) void ImageLoader::recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload) { - this->recursiveBind(context, forceLazysBound, neverUnload); + this->recursiveBind(context, forceLazysBound, neverUnload, nullptr); vmAccountingSetSuspended(context, false); } -void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent) { // Normally just non-lazy pointers are bound immediately. // The exceptions are: @@ -930,11 +963,15 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound // bind lower level libraries first for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); - if ( dependentImage != NULL ) - dependentImage->recursiveBind(context, forceLazysBound, neverUnload); + if ( dependentImage != NULL ) { + const ImageLoader* reExportParent = nullptr; + if ( libReExported(i) ) + reExportParent = this; + dependentImage->recursiveBind(context, forceLazysBound, neverUnload, reExportParent); + } } // bind this image - this->doBind(context, forceLazysBound); + this->doBind(context, forceLazysBound, parent); // mark if lazys are also bound if ( forceLazysBound || this->usablePrebinding(context) ) fAllLazyPointersBound = true; @@ -1012,7 +1049,12 @@ void ImageLoader::weakBind(const LinkContext& context) new (&context.weakDefMap) dyld3::Map, ImageLoader::HashCString, ImageLoader::EqualCString>(); context.weakDefMapInitialized = true; } -#if __MAC_OS_X_VERSION_MIN_REQUIRED + + // We might have to patch the shared cache __DATA_CONST. In that case, we'll create just a single + // patcher when needed. + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + +#if TARGET_OS_OSX // only do alternate algorithm for dlopen(). Use traditional algorithm for launch if ( !context.linkingMainExecutable ) { // Don't take the memory hit of weak defs on the launch path until we hit a dlopen with more weak symbols to bind @@ -1032,8 +1074,8 @@ void ImageLoader::weakBind(const LinkContext& context) Diagnostics diag; const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader(); - ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) { - uintptr_t targetAddr = (uintptr_t)ma + imageOffset; + ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) { + uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset; if ( isFromExportTrie ) { // Avoid duplicating the string if we already have the symbol name if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() ) @@ -1055,8 +1097,8 @@ void ImageLoader::weakBind(const LinkContext& context) continue; Diagnostics diag; const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader(); - ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) { - uintptr_t targetAddr = (uintptr_t)ma + imageOffset; + ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) { + uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset; if ( isFromExportTrie ) { // Avoid duplicating the string if we already have the symbol name if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() ) @@ -1111,6 +1153,8 @@ void ImageLoader::weakBind(const LinkContext& context) } } if ( (targetAddr != 0) && (coalIterator.image != targetImage) ) { + if ( coalIterator.image->inSharedCache() ) + patcher.makeWriteable(); coalIterator.image->updateUsesCoalIterator(coalIterator, targetAddr, (ImageLoader*)targetImage, 0, context); if (weakDefIt == context.weakDefMap.end()) { if (targetImage->neverUnload()) { @@ -1135,7 +1179,7 @@ void ImageLoader::weakBind(const LinkContext& context) } } else -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED +#endif // TARGET_OS_OSX { // make symbol iterators for each ImageLoader::CoalIterator iterators[count]; @@ -1215,8 +1259,11 @@ void ImageLoader::weakBind(const LinkContext& context) nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), targetAddr, targetImage->getIndexedShortName(targetImageIndex)); } - if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) + if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) { + if ( iterators[i].image->inSharedCache() ) + patcher.makeWriteable(); iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); + } iterators[i].symbolMatches = false; } } @@ -1242,7 +1289,7 @@ void ImageLoader::weakBind(const LinkContext& context) // but if main executable has non-weak override of operator new or delete it needs is handled here for (const char* weakSymbolName : sTreatAsWeak) { const ImageLoader* dummy; - imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy); + imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy, patcher); } } #if __arm64e__ @@ -1255,7 +1302,7 @@ void ImageLoader::weakBind(const LinkContext& context) while ( !coaler.done ) { const ImageLoader* dummy; // a side effect of resolveWeak() is to patch cache - imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy); + imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy, patcher); imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); } } @@ -1299,7 +1346,11 @@ void ImageLoader::weakBindOld(const LinkContext& context) // don't need to do any coalescing if only one image has overrides, or all have already been done if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED + // We might have to patch the shared cache __DATA_CONST. In that case, we'll create just a single + // patcher when needed. + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + +#if TARGET_OS_OSX // only do alternate algorithm for dlopen(). Use traditional algorithm for launch if ( !context.linkingMainExecutable ) { // for all images that need weak binding @@ -1351,6 +1402,8 @@ void ImageLoader::weakBindOld(const LinkContext& context) } } if ( (targetAddr != 0) && (coalIterator.image != targetImage) ) { + if ( coalIterator.image->inSharedCache() ) + patcher.makeWriteable(); coalIterator.image->updateUsesCoalIterator(coalIterator, targetAddr, (ImageLoader*)targetImage, 0, context); if ( context.verboseWeakBind ) dyld::log("dyld: adjusting uses of %s in %s to use definition from %s\n", nameToCoalesce, coalIterator.image->getPath(), targetImage->getPath()); @@ -1360,7 +1413,7 @@ void ImageLoader::weakBindOld(const LinkContext& context) } } else -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED +#endif // TARGET_OS_OSX { // make symbol iterators for each ImageLoader::CoalIterator iterators[count]; @@ -1440,8 +1493,11 @@ void ImageLoader::weakBindOld(const LinkContext& context) nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), targetAddr, targetImage->getIndexedShortName(targetImageIndex)); } - if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) + if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) { + if ( iterators[i].image->inSharedCache() ) + patcher.makeWriteable(); iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); + } iterators[i].symbolMatches = false; } } @@ -1459,20 +1515,21 @@ void ImageLoader::weakBindOld(const LinkContext& context) // but if main executable has non-weak override of operator new or delete it needs is handled here for (const char* weakSymbolName : sTreatAsWeak) { const ImageLoader* dummy; - imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy); + imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy, patcher); } } #if __arm64e__ else { // support traditional arm64 app on an arm64e device // look for weak def symbols in this image which may override the cache + patcher.makeWriteable(); ImageLoader::CoalIterator coaler; imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0); imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); while ( !coaler.done ) { const ImageLoader* dummy; // a side effect of resolveWeak() is to patch cache - imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy); + imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy, patcher); imagesNeedingCoalescing[i]->incrementCoalIterator(coaler); } } @@ -1883,14 +1940,28 @@ intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end) bit += 7; } while (byte & 0x80); // sign extend negative numbers - if ( (byte & 0x40) != 0 ) + if ( ((byte & 0x40) != 0) && (bit < 64) ) result |= (~0ULL) << bit; return (intptr_t)result; } +void ImageLoader::forEachReExportDependent( void (^callback)(const ImageLoader*, bool& stop)) const +{ + bool stop = false; + for (unsigned int i=0; i < libraryCount(); ++i) { + if ( libReExported(i) ) { + if ( ImageLoader* dependentImage = libImage(i) ) { + callback(dependentImage, stop); + } + } + if (stop) + break; + } +} + VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple); -//VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); +VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 1a3d415..60dd9a8 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -51,8 +51,8 @@ #endif #include "DyldSharedCache.h" - #include "Map.h" +#include "PointerAuth.h" #if __arm__ #include @@ -71,7 +71,7 @@ #define LOG_BINDINGS 0 -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 #define SPLIT_SEG_DYLIB_SUPPORT 0 #define PREBOUND_IMAGE_SUPPORT __arm__ @@ -82,7 +82,7 @@ #define SUPPORT_CLASSIC_MACHO __arm__ #define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__) #define INITIAL_IMAGE_COUNT 150 - #define SUPPORT_ACCELERATE_TABLES !TARGET_OS_SIMULATOR + #define SUPPORT_ACCELERATE_TABLES 0 #define SUPPORT_ROOT_PATH TARGET_OS_SIMULATOR #else #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 @@ -90,7 +90,7 @@ #define PREBOUND_IMAGE_SUPPORT __i386__ #define TEXT_RELOC_SUPPORT __i386__ #define SUPPORT_OLD_CRT_INITIALIZATION __i386__ - #define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) + #define SUPPORT_LC_DYLD_ENVIRONMENT 1 #define SUPPORT_VERSIONED_PATHS 1 #define SUPPORT_CLASSIC_MACHO 1 #define SUPPORT_ZERO_COST_EXCEPTIONS 1 @@ -313,13 +313,15 @@ public: bool allowEnvVarsSharedCache; bool allowClassicFallbackPaths; bool allowInsertFailures; + bool allowInterposing; bool mainExecutableCodeSigned; bool prebinding; bool bindFlat; bool linkingMainExecutable; bool startedInitializingMainExecutable; -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX bool iOSonMac; + // dyld doesn't build for driverKit, so we use the macOS define to control whether driverKit is supported bool driverKit; #endif bool verboseOpts; @@ -529,7 +531,8 @@ public: virtual bool forceFlat() const = 0; // called at runtime when a lazily bound function is first called - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher) = 0; // called at runtime when a fast lazily bound function is first called virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, @@ -616,7 +619,7 @@ public: // 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 recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent); void recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload); void recursiveRebaseWithAccounting(const LinkContext& context); void weakBind(const LinkContext& context); @@ -646,12 +649,15 @@ public: void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } void setNeverUnloadRecursive(); + void forEachReExportDependent( void (^callback)(const ImageLoader*, bool& stop)) const; + bool isReferencedDownward() { return fIsReferencedDownward; } virtual void recursiveMakeDataReadOnly(const LinkContext& context); virtual uintptr_t resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, - const ImageLoader** foundIn) { return 0; } + const ImageLoader** foundIn, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { 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); @@ -695,10 +701,10 @@ public: bool objCMappedNotified() const { return fObjCMappedNotified; } struct InterposeTuple { - uintptr_t replacement; - ImageLoader* neverImage; // don't apply replacement to this image - ImageLoader* onlyImage; // only apply replacement to this image - uintptr_t replacee; + uintptr_t replacement; + dyld3::AuthenticatedValue neverImage; // don't apply replacement to this image + dyld3::AuthenticatedValue onlyImage; // only apply replacement to this image + uintptr_t replacee; }; static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end); @@ -751,7 +757,8 @@ protected: // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. // These methods do the above, exactly once, and it the right order virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); - virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual unsigned recursiveUpdateDepth(unsigned int maxDepth, dyld3::Array& danglingUpwards); + virtual unsigned updateDepth(unsigned int maxDepth); virtual void recursiveRebase(const LinkContext& context); virtual void recursiveApplyInterposing(const LinkContext& context); virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); @@ -768,10 +775,10 @@ protected: virtual void doRebase(const LinkContext& context) = 0; // do any symbolic fix ups in this image - virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; + virtual void doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) = 0; // called later via API to force all lazy pointer to be bound - virtual void doBindJustLazies(const LinkContext& context) = 0; + virtual void doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) = 0; // if image has any dtrace DOF sections, append them to list to be registered virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) = 0; @@ -843,6 +850,9 @@ public: protected: static std::vector fgInterposingTuples; +#if __x86_64__ + const char* fAotPath; +#endif const char* fPath; const char* fRealPath; dev_t fDevice; @@ -898,6 +908,7 @@ private: static_assert(sizeof(sizeOfData) == 8, "Bad data size"); static uint16_t fgLoadOrdinal; + }; diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index bc9724f..5ff8cf4 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -64,7 +64,7 @@ extern "C" long __stack_chk_guard; #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib" #define LIBDYLD_DYLIB_PATH "/usr/lib/system/libdyld.dylib" -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX #define DRIVERKIT_LIBSYSTEM_DYLIB_PATH "/System/DriverKit/usr/lib/libSystem.dylib" #define DRIVERKIT_LIBDYLD_DYLIB_PATH "/System/DriverKit/usr/lib/system/libdyld.dylib" #endif @@ -127,7 +127,7 @@ fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX static uintptr_t pageAlign(uintptr_t value) { return (value + 4095) & (-4096); @@ -148,8 +148,6 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat const uint32_t cmd_count = mh->ncmds; const uint32_t sizeofcmds = mh->sizeofcmds; - if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) ) - dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); if ( cmd_count > (sizeofcmds/sizeof(load_command)) ) dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds); const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); @@ -197,7 +195,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat break; case LC_SEGMENT_COMMAND: segCmd = (struct macho_segment_command*)cmd; -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) if ( ((segCmd->filesize) > pageAlign(segCmd->vmsize)) && (segCmd->vmsize != 0) ) #else @@ -349,7 +347,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat throw "malformed mach-o image: LC_DYSYMTAB size wrong"; dynSymbTabCmd = (dysymtab_command*)cmd; break; -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // error when loading iOS Simulator mach-o binary into macOS process case LC_VERSION_MIN_WATCHOS: case LC_VERSION_MIN_TVOS: @@ -369,7 +367,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat if ( !inCache && (startOfFileSegCmd == NULL) ) throw "malformed mach-o image: missing __TEXT segment that maps start of file"; // verify every segment does not overlap another segment - if ( context.strictMachORequired ) { + if ( context.strictMachORequired && !inCache ) { uintptr_t lastFileStart = 0; uintptr_t linkeditFileStart = 0; const struct load_command* cmd1 = startCmds; @@ -510,7 +508,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) ) throw "malformed mach-o image: symbol strings overrun __LINKEDIT"; } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX if ( (symTabCmd->symoff % sizeof(void*)) != 0 ) { // allow old malformed plugins in new app if ( sdkVersion((mach_header*)mh) >= DYLD_PACKED_VERSION(10,15,0) ) @@ -556,6 +554,10 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat if ( needsAddedLibSystemDepency(*libCount, mh) ) *libCount = 1; + + // dylibs that use LC_DYLD_CHAINED_FIXUPS have that load command removed when put in the dyld cache + if ( !*compressed && (mh->flags & MH_DYLIB_IN_CACHE) ) + *compressed = true; } @@ -668,7 +670,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) for(unsigned int i=0; i < fSegmentsCount; ++i) { // set up pointer to __LINKEDIT segment if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { - #if !__MAC_OS_X_VERSION_MIN_REQUIRED + #if !TARGET_OS_OSX // historically, macOS never did this check if ( segFileOffset(i) > fCoveredCodeLength ) dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); @@ -759,7 +761,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0); - #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + #if __i386__ && TARGET_OS_OSX const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0); if ( isObjCSeg ) fNotifyObjC = true; @@ -783,7 +785,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); - #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + #if __i386__ && TARGET_OS_OSX else if ( isObjCSeg ) { if ( strcmp(sect->sectname, "__image_info") == 0 ) { const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); @@ -797,7 +799,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) } #else else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) { - #if __MAC_OS_X_VERSION_MIN_REQUIRED + #if TARGET_OS_OSX const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); uint32_t flags = imageInfo[1]; if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) @@ -1135,7 +1137,7 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod disableCoverageCheck(); } else { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // ignore code signatures in binaries built with pre-10.9 tools if ( this->sdkVersion() < DYLD_PACKED_VERSION(10,9,0) ) { disableCoverageCheck(); @@ -1187,7 +1189,7 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure // We need to ignore older code signatures because they will be bad. if ( this->sdkVersion() < DYLD_PACKED_VERSION(10,9,0) ) { @@ -1342,8 +1344,13 @@ void* ImageLoaderMachO::getEntryFromLC_MAIN() const entry_point_command* mainCmd = (entry_point_command*)cmd; void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData); // verify entry point is in image - if ( this->containsAddress(entry) ) + if ( this->containsAddress(entry) ) { +#if __has_feature(ptrauth_calls) + // start() calls the result pointer as a function pointer so we need to sign it. + return __builtin_ptrauth_sign_unauthenticated(entry, 0, 0); +#endif return entry; + } else throw "LC_MAIN entryoff is out of range"; } @@ -1372,13 +1379,6 @@ void* ImageLoaderMachO::getEntryFromLC_UNIXTHREAD() const // verify entry point is in image if ( this->containsAddress(entry) ) return entry; - #elif __arm64__ && !__arm64e__ - // temp support until is fixed - const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16); - void* entry = (void*)(regs64[32] + fSlide); // arm_thread_state64_t.__pc - // verify entry point is in image - if ( this->containsAddress(entry) ) - return entry; #endif } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -1411,10 +1411,9 @@ bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const m { const dylib_command* dylibID = (dylib_command*)cmd; const char* installPath = (char*)cmd + dylibID->dylib.name.offset; - // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents + // It is OK for OS dylibs (libSystem or libmath) to have no dependents // but all other dylibs must depend on libSystem for initialization to initialize libSystem first - // rosetta circular dependency spew - isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/System/DriverKit/usr/lib/", 26) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) ); + isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/System/DriverKit/usr/lib/", 26) != 0) ); // if (isNonOSdylib) dyld::log("ImageLoaderMachO::needsAddedLibSystemDepency(%s)\n", installPath); } break; @@ -1540,7 +1539,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); for (size_t j=0; j < count; ++j) { uint32_t funcOffset = inits[j]; - // verify initializers are in TEXT segment - if ( funcOffset > seg->filesize ) { + // verify initializers are in image + if ( ! this->containsAddress((uint8_t*)this->machHeader() + funcOffset) ) { dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath()); } if ( ! dyld::gProcessInfo->libSystemInitialized ) { @@ -2356,6 +2355,9 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset); if ( context.verboseInit ) dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); +#if __has_feature(ptrauth_calls) + func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0); +#endif bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); { dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0); @@ -2487,13 +2489,13 @@ void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const Ini dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing); } - -intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) +intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context, size_t extraAllocationSize) { // preflight and calculate slide if needed const bool inPIE = (fgNextPIEDylibAddress != 0); intptr_t slide = 0; if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { + intptr_t segmentReAlignSlide = 0; bool needsToSlide = false; bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0); uintptr_t lowAddr = (unsigned long)(-1); @@ -2511,23 +2513,54 @@ intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) lowAddr = segLow; if ( segHigh > highAddr ) highAddr = segHigh; - + +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + // For Cambria on Aruba systems (16k page size), realign the image so the first segment ends on a 16k boundry. + // FIXME: this can be removed when Aruba dev systems are no longer supported. + if ( dyld::isTranslated() && vm_page_size == 0x4000 && i == 0 && segLow == 0 ) { + const uintptr_t segHighPageOffset = segHigh & vm_page_mask; + if ( segHighPageOffset > 0 ) { + // Adjust the slide to make the first segment end on a page boundry. + needsToSlide = true; + segmentReAlignSlide = vm_page_size - segHighPageOffset; + + if (context.verboseMapping) { + dyld::log("dyld: Image %s first segment(%s) does not end on a page boundry [0x%lx, 0x%lx) adding 0x%lx to slide to realign\n", getPath(), segName(i), segLow, segHigh, segmentReAlignSlide); + } + } + } +#endif if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) needsToSlide = true; } if ( needsToSlide ) { // find a chunk of address space to hold all segments - uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context); - slide = addr - lowAddr; + size_t size = highAddr-lowAddr+segmentReAlignSlide; + uintptr_t addr = reserveAnAddressRange(size+extraAllocationSize, context); + slide = addr - lowAddr + segmentReAlignSlide; + } else if ( extraAllocationSize ) { + if (!reserveAddressRange(highAddr, extraAllocationSize)) { + throw "failed to reserve space for aot"; + } } } else if ( ! this->segmentsCanSlide() ) { + uintptr_t highAddr = 0; for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uintptr_t segLow = segPreferredLoadAddress(i); + const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); + + if ( segHigh > highAddr ) + highAddr = segHigh; + if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) ) continue; if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i)); } + if (extraAllocationSize) { + dyld::throwf("binaries with non-slidable segments don't support aot: %s", this->getPath()); + } } else { throw "mach-o does not support independently sliding segments"; @@ -2540,9 +2573,6 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad { vm_address_t addr = 0; vm_size_t size = length; - // In Darling, we're not the only ones doing memory mapping. - // Therefore, we cannot dictate addresses, because we could (would!) conflict with the ELF loader. -#ifndef DARLING // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either if ( fgNextPIEDylibAddress != 0 ) { // add small (0-3 pages) random padding between dylibs @@ -2555,7 +2585,6 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad } fgNextPIEDylibAddress = 0; } -#endif kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r != KERN_SUCCESS ) throw "out of address space"; @@ -2573,12 +2602,23 @@ bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) return true; } - - void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) { + uint64_t extra_allocation_size = 0; + +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if (dyld::isTranslated()) { + fAotPath = new char[PATH_MAX]; + int ret = syscall(0x7000001, fd, this->getPath(), &extra_allocation_size, fAotPath, PATH_MAX); + if (ret != 0) { + delete fAotPath; + fAotPath = nullptr; + } + } +#endif + // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); + intptr_t slide = this->assignSegmentAddresses(context, extra_allocation_size); if ( context.verboseMapping ) { if ( offsetInFat != 0 ) dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); @@ -2596,10 +2636,24 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF dyld::log("dyld: Speculatively read offset=0x%08llX, len=0x%08llX, path=%s\n", offsetInFat, lenInFat, this->getPath()); // map in all segments + uintptr_t baseAddress = (unsigned long)(-1); + uintptr_t endAddress = 0; + uintptr_t mappedMachHeaderAddress = 0; for(unsigned int i=0, e=segmentCount(); i < e; ++i) { vm_offset_t fileOffset = (vm_offset_t)(segFileOffset(i) + offsetInFat); vm_size_t size = segFileSize(i); uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide; + const uintptr_t segmentEnd = dyld_page_round(requestedLoadAddress + segSize(i)); + + if ( requestedLoadAddress < baseAddress ) + baseAddress = requestedLoadAddress; + if ( segmentEnd > endAddress ) + endAddress = segmentEnd; + + if (segFileOffset(i) == 0 && segFileSize(i) != 0) { + mappedMachHeaderAddress = requestedLoadAddress; + } + int protection = 0; if ( !segUnaccessible(i) ) { if ( segExecutable(i) ) @@ -2646,6 +2700,24 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); } +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if (dyld::isTranslated() && extra_allocation_size != 0) { + const struct mach_header* aot_load_address; + dyld_aot_image_info aot_image_info = {}; + int ret = syscall(0x7000002, this->getPath(), mappedMachHeaderAddress, endAddress, &aot_load_address, &aot_image_info.aotImageSize, aot_image_info.aotImageKey); + if (ret == 0) { + extern void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[]); + + // fill in the aot load address, at this point the cambria trap has filled in + // the image size and image key fields + aot_image_info.aotLoadAddress = aot_load_address; + aot_image_info.x86LoadAddress = (struct mach_header*)baseAddress; + + addAotImagesToAllAotImages(1, &aot_image_info); + } + } +#endif + // update slide to reflect load location this->setSlide(slide); } @@ -2653,7 +2725,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) { // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); + intptr_t slide = this->assignSegmentAddresses(context, 0); if ( context.verboseMapping ) dyld::log("dyld: Mapping memory %p\n", memoryImage); // map in all segments @@ -2671,24 +2743,52 @@ void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, c this->setSlide(slide); // set R/W permissions on all segments at slide location for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - segProtect(i, context); + segProtect(i, context); } } +static vm_prot_t protectionForSegIndex(const ImageLoaderMachO* image, unsigned int segIndex) +{ + if ( image->segUnaccessible(segIndex) ) + return 0; + vm_prot_t protection = 0; + if ( image->segExecutable(segIndex) ) + protection |= PROT_EXEC; + if ( image->segReadable(segIndex) ) + protection |= PROT_READ; + if ( image->segWriteable(segIndex) ) + protection |= PROT_WRITE; + return protection; +} + void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context) { - vm_prot_t protection = 0; - if ( !segUnaccessible(segIndex) ) { - if ( segExecutable(segIndex) ) - protection |= PROT_EXEC; - if ( segReadable(segIndex) ) - protection |= PROT_READ; - if ( segWriteable(segIndex) ) - protection |= PROT_WRITE; - } + vm_prot_t protection = protectionForSegIndex(this, segIndex); vm_address_t addr = segActualLoadAddress(segIndex); vm_size_t size = segSize(segIndex); + +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if ( dyld::isTranslated() ) { + // can't vm_protect non-16KB segments + if ( (segIndex > 0) && ((addr & 0x3FFF) != 0) ) { + // overlaps previous segment + vm_prot_t prevProt = protectionForSegIndex(this, segIndex-1); + if ( (protection & prevProt) != prevProt ) { + // previous had more bits, so we need to not apply new permissions to the overlap + vm_size_t overlap = 0x4000 - (addr & 0x3FFF); + addr += overlap; + if ( size >= overlap ) + size -= overlap; + else if ( size < overlap ) + size = 0; + } + if ( size == 0 ) + return; + } + } +#endif + const bool setCurrentPermissions = false; kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); if ( r != KERN_SUCCESS ) { diff --git a/src/ImageLoaderMachO.cpp.orig b/src/ImageLoaderMachO.cpp.orig deleted file mode 100644 index 345f6bd..0000000 --- a/src/ImageLoaderMachO.cpp.orig +++ /dev/null @@ -1,2741 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -// work around until conformance work is complete rdar://problem/4508801 -#define __srr0 srr0 -#define __eip eip -#define __rip rip - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ImageLoaderMachO.h" -#include "ImageLoaderMachOCompressed.h" -#if SUPPORT_CLASSIC_MACHO -#include "ImageLoaderMachOClassic.h" -#endif -#include "mach-o/dyld_images.h" -#include "dyld.h" - -// use stack guard random value to add padding between dylibs -extern "C" long __stack_chk_guard; - -#ifndef LC_LOAD_UPWARD_DYLIB - #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ -#endif - -#ifndef LC_VERSION_MIN_TVOS - #define LC_VERSION_MIN_TVOS 0x2F -#endif - -#ifndef LC_VERSION_MIN_WATCHOS - #define LC_VERSION_MIN_WATCHOS 0x30 -#endif - - -#if TARGET_IPHONE_SIMULATOR - #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib" -#else - #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib" -#endif - -// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables -#if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 - #define LC_ROUTINES_COMMAND LC_ROUTINES_64 - #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT - struct macho_segment_command : public segment_command_64 {}; - struct macho_section : public section_64 {}; - struct macho_routines_command : public routines_command_64 {}; -#else - #define LC_SEGMENT_COMMAND LC_SEGMENT - #define LC_ROUTINES_COMMAND LC_ROUTINES - #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 - struct macho_segment_command : public segment_command {}; - struct macho_section : public section {}; - struct macho_routines_command : public routines_command {}; -#endif - -uint32_t ImageLoaderMachO::fgSymbolTableBinarySearchs = 0; - - -ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, - uint32_t segOffsets[], unsigned int libCount) - : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), - fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), -fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), -#if TEXT_RELOC_SUPPORT - fTextSegmentRebases(false), - fTextSegmentBinds(false), -#endif -#if __i386__ - fReadOnlyImportSegment(false), -#endif - fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), - fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false) -{ - fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); - - // construct SegmentMachO object for each LC_SEGMENT cmd using "placement new" to put - // each SegmentMachO object in array at end of ImageLoaderMachO object - const uint32_t cmd_count = mh->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, segIndex=0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; - // ignore zero-sized segments - if ( segCmd->vmsize != 0 ) { - // record offset of load command - segOffsets[segIndex++] = (uint32_t)((uint8_t*)segCmd - fMachOData); - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - -} - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -static uintptr_t pageAlign(uintptr_t value) -{ - return (value + 4095) & (-4096); -} -#endif - -// determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has -void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, - unsigned int* segCount, unsigned int* libCount, const LinkContext& context, - const linkedit_data_command** codeSigCmd, - const encryption_info_command** encryptCmd) -{ - *compressed = false; - *segCount = 0; - *libCount = 0; - *codeSigCmd = NULL; - *encryptCmd = NULL; - - const uint32_t cmd_count = mh->ncmds; - const uint32_t sizeofcmds = mh->sizeofcmds; - if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) ) - dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); - if ( cmd_count > (sizeofcmds/sizeof(load_command)) ) - dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds); - const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); - const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + sizeofcmds); - const struct load_command* cmd = startCmds; - bool foundLoadCommandSegment = false; - const macho_segment_command* linkeditSegCmd = NULL; - const macho_segment_command* startOfFileSegCmd = NULL; - const dyld_info_command* dyldInfoCmd = NULL; - const symtab_command* symTabCmd = NULL; - const dysymtab_command* dynSymbTabCmd = NULL; - for (uint32_t i = 0; i < cmd_count; ++i) { - uint32_t cmdLength = cmd->cmdsize; - const macho_segment_command* segCmd; - const dylib_command* dylibCmd; - if ( cmdLength < 8 ) { - dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", - i, cmdLength, path); - } - const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); - if ( (nextCmd > endCmds) || (nextCmd < cmd) ) { - dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", - i, cmdLength, mh->sizeofcmds, path); - } - switch (cmd->cmd) { - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - if ( cmd->cmdsize != sizeof(dyld_info_command) ) - throw "malformed mach-o image: LC_DYLD_INFO size wrong"; - dyldInfoCmd = (struct dyld_info_command*)cmd; - *compressed = true; - break; - case LC_SEGMENT_COMMAND: - segCmd = (struct macho_segment_command*)cmd; -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) - if ( ((segCmd->filesize) > pageAlign(segCmd->vmsize)) && (segCmd->vmsize != 0) ) -#else - // dyld should support non-allocatable __LLVM segment - if ( (segCmd->filesize > segCmd->vmsize) && ((segCmd->vmsize != 0) || ((segCmd->flags & SG_NORELOC) == 0)) ) -#endif - dyld::throwf("malformed mach-o image: segment load command %s filesize (0x%0lX) is larger than vmsize (0x%0lX)", segCmd->segname, (long)segCmd->filesize , (long)segCmd->vmsize ); - if ( cmd->cmdsize < sizeof(macho_segment_command) ) - throw "malformed mach-o image: LC_SEGMENT size too small"; - if ( cmd->cmdsize != (sizeof(macho_segment_command) + segCmd->nsects * sizeof(macho_section)) ) - throw "malformed mach-o image: LC_SEGMENT size wrong for number of sections"; - // ignore zero-sized segments - if ( segCmd->vmsize != 0 ) - *segCount += 1; - if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) { - #if TARGET_IPHONE_SIMULATOR - // Note: should check on all platforms that __LINKEDIT is read-only, but - if ( segCmd->initprot != VM_PROT_READ ) - throw "malformed mach-o image: __LINKEDIT segment does not have read-only permissions"; - #endif - if ( segCmd->fileoff == 0 ) - throw "malformed mach-o image: __LINKEDIT has fileoff==0 which overlaps mach_header"; - if ( linkeditSegCmd != NULL ) - throw "malformed mach-o image: multiple __LINKEDIT segments"; - linkeditSegCmd = segCmd; - } - else { - if ( segCmd->initprot & 0xFFFFFFF8 ) - dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in initprot", segCmd->segname, segCmd->initprot); - if ( segCmd->maxprot & 0xFFFFFFF8 ) - dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in maxprot", segCmd->segname, segCmd->maxprot); - if ( (segCmd->initprot != 0) && ((segCmd->initprot & VM_PROT_READ) == 0) ) - dyld::throwf("malformed mach-o image: %s segment is not mapped readable", segCmd->segname); - } - if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0) ) { - if ( (segCmd->initprot & VM_PROT_READ) == 0 ) - dyld::throwf("malformed mach-o image: %s segment maps start of file but is not readable", segCmd->segname); - if ( (segCmd->initprot & VM_PROT_WRITE) == VM_PROT_WRITE ) { - if ( context.strictMachORequired ) - dyld::throwf("malformed mach-o image: %s segment maps start of file but is writable", segCmd->segname); - } - if ( segCmd->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) - dyld::throwf("malformed mach-o image: %s segment does not map all of load commands", segCmd->segname); - if ( startOfFileSegCmd != NULL ) - dyld::throwf("malformed mach-o image: multiple segments map start of file: %s %s", startOfFileSegCmd->segname, segCmd->segname); - startOfFileSegCmd = segCmd; - } - if ( context.strictMachORequired ) { - uintptr_t vmStart = segCmd->vmaddr; - uintptr_t vmSize = segCmd->vmsize; - uintptr_t vmEnd = vmStart + vmSize; - uintptr_t fileStart = segCmd->fileoff; - uintptr_t fileSize = segCmd->filesize; - if ( (intptr_t)(vmSize) < 0 ) - dyld::throwf("malformed mach-o image: segment load command %s vmsize too large in %s", segCmd->segname, path); - if ( vmStart > vmEnd ) - dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname); - if ( vmSize != fileSize ) { - if ( segCmd->initprot == 0 ) { - // allow: fileSize == 0 && initprot == 0 e.g. __PAGEZERO - // allow: vmSize == 0 && initprot == 0 e.g. __LLVM - if ( (fileSize != 0) && (vmSize != 0) ) - dyld::throwf("malformed mach-o image: unaccessable segment %s has non-zero filesize and vmsize", segCmd->segname); - } - else { - // allow: vmSize > fileSize && initprot != X e.g. __DATA - if ( vmSize < fileSize ) { - dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname); - } - if ( segCmd->initprot & VM_PROT_EXECUTE ) { - dyld::throwf("malformed mach-o image: segment %s has vmsize != filesize and is executable", segCmd->segname); - } - } - } - if ( inCache ) { - if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) { - if ( foundLoadCommandSegment ) - throw "load commands in multiple segments"; - foundLoadCommandSegment = true; - } - } - else if ( (fileStart < mh->sizeofcmds) && (fileSize != 0) ) { - // all load commands must be in an executable segment - if ( (fileStart != 0) || (fileSize < (mh->sizeofcmds+sizeof(macho_header))) ) - dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); - if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) - dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); - if ( foundLoadCommandSegment ) - throw "load commands in multiple segments"; - foundLoadCommandSegment = true; - } - - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if (!inCache && sect->offset != 0 && ((sect->offset + sect->size) > (segCmd->fileoff + segCmd->filesize))) - dyld::throwf("malformed mach-o image: section %s,%s of '%s' exceeds segment %s booundary", sect->segname, sect->sectname, path, segCmd->segname); - } - } - break; - case LC_SEGMENT_COMMAND_WRONG: - dyld::throwf("malformed mach-o image: wrong LC_SEGMENT[_64] for architecture"); - break; - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - case LC_LOAD_UPWARD_DYLIB: - *libCount += 1; - // fall thru - case LC_ID_DYLIB: - dylibCmd = (dylib_command*)cmd; - if ( dylibCmd->dylib.name.offset > cmdLength ) - dyld::throwf("malformed mach-o image: dylib load command #%d has offset (%u) outside its size (%u)", i, dylibCmd->dylib.name.offset, cmdLength); - if ( (dylibCmd->dylib.name.offset + strlen((char*)dylibCmd + dylibCmd->dylib.name.offset) + 1) > cmdLength ) - dyld::throwf("malformed mach-o image: dylib load command #%d string extends beyond end of load command", i); - break; - case LC_CODE_SIGNATURE: - if ( cmd->cmdsize != sizeof(linkedit_data_command) ) - throw "malformed mach-o image: LC_CODE_SIGNATURE size wrong"; - // only support one LC_CODE_SIGNATURE per image - if ( *codeSigCmd != NULL ) - throw "malformed mach-o image: multiple LC_CODE_SIGNATURE load commands"; - *codeSigCmd = (struct linkedit_data_command*)cmd; - break; - case LC_ENCRYPTION_INFO: - if ( cmd->cmdsize != sizeof(encryption_info_command) ) - throw "malformed mach-o image: LC_ENCRYPTION_INFO size wrong"; - // only support one LC_ENCRYPTION_INFO per image - if ( *encryptCmd != NULL ) - throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO load commands"; - *encryptCmd = (encryption_info_command*)cmd; - break; - case LC_ENCRYPTION_INFO_64: - if ( cmd->cmdsize != sizeof(encryption_info_command_64) ) - throw "malformed mach-o image: LC_ENCRYPTION_INFO_64 size wrong"; - // only support one LC_ENCRYPTION_INFO_64 per image - if ( *encryptCmd != NULL ) - throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO_64 load commands"; - *encryptCmd = (encryption_info_command*)cmd; - break; - case LC_SYMTAB: - if ( cmd->cmdsize != sizeof(symtab_command) ) - throw "malformed mach-o image: LC_SYMTAB size wrong"; - symTabCmd = (symtab_command*)cmd; - break; - case LC_DYSYMTAB: - if ( cmd->cmdsize != sizeof(dysymtab_command) ) - throw "malformed mach-o image: LC_DYSYMTAB size wrong"; - dynSymbTabCmd = (dysymtab_command*)cmd; - break; -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // error when loading iOS Simulator mach-o binary into macOS process - case LC_VERSION_MIN_WATCHOS: - case LC_VERSION_MIN_TVOS: - case LC_VERSION_MIN_IPHONEOS: - throw "mach-o, but built for simulator (not macOS)"; - break; -#endif - } - cmd = nextCmd; - } - - if ( context.strictMachORequired && !foundLoadCommandSegment ) - throw "load commands not in a segment"; - if ( linkeditSegCmd == NULL ) - throw "malformed mach-o image: missing __LINKEDIT segment"; - if ( startOfFileSegCmd == NULL ) - throw "malformed mach-o image: missing __TEXT segment that maps start of file"; - // verify every segment does not overlap another segment - if ( context.strictMachORequired ) { - uintptr_t lastFileStart = 0; - uintptr_t linkeditFileStart = 0; - const struct load_command* cmd1 = startCmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd1->cmd == LC_SEGMENT_COMMAND ) { - struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1; - uintptr_t vmStart1 = segCmd1->vmaddr; - uintptr_t vmEnd1 = segCmd1->vmaddr + segCmd1->vmsize; - uintptr_t fileStart1 = segCmd1->fileoff; - uintptr_t fileEnd1 = segCmd1->fileoff + segCmd1->filesize; - - if (fileStart1 > lastFileStart) - lastFileStart = fileStart1; - - if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) { - linkeditFileStart = fileStart1; - } - - const struct load_command* cmd2 = startCmds; - for (uint32_t j = 0; j < cmd_count; ++j) { - if ( cmd2 == cmd1 ) - continue; - if ( cmd2->cmd == LC_SEGMENT_COMMAND ) { - struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2; - uintptr_t vmStart2 = segCmd2->vmaddr; - uintptr_t vmEnd2 = segCmd2->vmaddr + segCmd2->vmsize; - uintptr_t fileStart2 = segCmd2->fileoff; - uintptr_t fileEnd2 = segCmd2->fileoff + segCmd2->filesize; - if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) - || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) ) - dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname); - if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1)) - || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) ) - dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); - } - cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize); - } - } - cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize); - } - - if (lastFileStart != linkeditFileStart) - dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment"); - } - - // validate linkedit content - if ( (dyldInfoCmd == NULL) && (symTabCmd == NULL) ) - throw "malformed mach-o image: missing LC_SYMTAB or LC_DYLD_INFO"; - if ( dynSymbTabCmd == NULL ) - throw "malformed mach-o image: missing LC_DYSYMTAB"; - - uint32_t linkeditFileOffsetStart = (uint32_t)linkeditSegCmd->fileoff; - uint32_t linkeditFileOffsetEnd = (uint32_t)linkeditSegCmd->fileoff + (uint32_t)linkeditSegCmd->filesize; - - if ( !inCache && (dyldInfoCmd != NULL) && context.strictMachORequired ) { - // validate all LC_DYLD_INFO chunks fit in LINKEDIT and don't overlap - uint32_t offset = linkeditFileOffsetStart; - if ( dyldInfoCmd->rebase_size != 0 ) { - if ( dyldInfoCmd->rebase_size & 0x80000000 ) - throw "malformed mach-o image: dyld rebase info size overflow"; - if ( dyldInfoCmd->rebase_off < offset ) - throw "malformed mach-o image: dyld rebase info underruns __LINKEDIT"; - offset = dyldInfoCmd->rebase_off + dyldInfoCmd->rebase_size; - if ( offset > linkeditFileOffsetEnd ) - throw "malformed mach-o image: dyld rebase info overruns __LINKEDIT"; - } - if ( dyldInfoCmd->bind_size != 0 ) { - if ( dyldInfoCmd->bind_size & 0x80000000 ) - throw "malformed mach-o image: dyld bind info size overflow"; - if ( dyldInfoCmd->bind_off < offset ) - throw "malformed mach-o image: dyld bind info overlaps rebase info"; - offset = dyldInfoCmd->bind_off + dyldInfoCmd->bind_size; - if ( offset > linkeditFileOffsetEnd ) - throw "malformed mach-o image: dyld bind info overruns __LINKEDIT"; - } - if ( dyldInfoCmd->weak_bind_size != 0 ) { - if ( dyldInfoCmd->weak_bind_size & 0x80000000 ) - throw "malformed mach-o image: dyld weak bind info size overflow"; - if ( dyldInfoCmd->weak_bind_off < offset ) - throw "malformed mach-o image: dyld weak bind info overlaps bind info"; - offset = dyldInfoCmd->weak_bind_off + dyldInfoCmd->weak_bind_size; - if ( offset > linkeditFileOffsetEnd ) - throw "malformed mach-o image: dyld weak bind info overruns __LINKEDIT"; - } - if ( dyldInfoCmd->lazy_bind_size != 0 ) { - if ( dyldInfoCmd->lazy_bind_size & 0x80000000 ) - throw "malformed mach-o image: dyld lazy bind info size overflow"; - if ( dyldInfoCmd->lazy_bind_off < offset ) - throw "malformed mach-o image: dyld lazy bind info overlaps weak bind info"; - offset = dyldInfoCmd->lazy_bind_off + dyldInfoCmd->lazy_bind_size; - if ( offset > linkeditFileOffsetEnd ) - throw "malformed mach-o image: dyld lazy bind info overruns __LINKEDIT"; - } - if ( dyldInfoCmd->export_size != 0 ) { - if ( dyldInfoCmd->export_size & 0x80000000 ) - throw "malformed mach-o image: dyld export info size overflow"; - if ( dyldInfoCmd->export_off < offset ) - throw "malformed mach-o image: dyld export info overlaps lazy bind info"; - offset = dyldInfoCmd->export_off + dyldInfoCmd->export_size; - if ( offset > linkeditFileOffsetEnd ) - throw "malformed mach-o image: dyld export info overruns __LINKEDIT"; - } - } - - if ( symTabCmd != NULL ) { - // validate symbol table fits in LINKEDIT - if ( symTabCmd->symoff < linkeditFileOffsetStart ) - throw "malformed mach-o image: symbol table underruns __LINKEDIT"; - if ( symTabCmd->nsyms > 0x10000000 ) - throw "malformed mach-o image: symbol table too large"; - uint32_t symbolsSize = symTabCmd->nsyms * sizeof(macho_nlist); - if ( symbolsSize > linkeditSegCmd->filesize ) - throw "malformed mach-o image: symbol table overruns __LINKEDIT"; - if ( symTabCmd->symoff + symbolsSize < symTabCmd->symoff ) - throw "malformed mach-o image: symbol table size wraps"; - if ( symTabCmd->symoff + symbolsSize > symTabCmd->stroff ) - throw "malformed mach-o image: symbol table overlaps symbol strings"; - if ( symTabCmd->stroff + symTabCmd->strsize < symTabCmd->stroff ) - throw "malformed mach-o image: symbol string size wraps"; - if ( symTabCmd->stroff + symTabCmd->strsize > linkeditFileOffsetEnd ) { - // let old apps overflow as long as it stays within mapped page - if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) ) - throw "malformed mach-o image: symbol strings overrun __LINKEDIT"; - } - // validate indirect symbol table - if ( dynSymbTabCmd->nindirectsyms != 0 ) { - if ( dynSymbTabCmd->indirectsymoff < linkeditFileOffsetStart ) - throw "malformed mach-o image: indirect symbol table underruns __LINKEDIT"; - if ( dynSymbTabCmd->nindirectsyms > 0x10000000 ) - throw "malformed mach-o image: indirect symbol table too large"; - uint32_t indirectTableSize = dynSymbTabCmd->nindirectsyms * sizeof(uint32_t); - if ( indirectTableSize > linkeditSegCmd->filesize ) - throw "malformed mach-o image: indirect symbol table overruns __LINKEDIT"; - if ( dynSymbTabCmd->indirectsymoff + indirectTableSize < dynSymbTabCmd->indirectsymoff ) - throw "malformed mach-o image: indirect symbol table size wraps"; - if ( context.strictMachORequired && (dynSymbTabCmd->indirectsymoff + indirectTableSize > symTabCmd->stroff) ) - throw "malformed mach-o image: indirect symbol table overruns string pool"; - } - if ( (dynSymbTabCmd->nlocalsym > symTabCmd->nsyms) || (dynSymbTabCmd->ilocalsym > symTabCmd->nsyms) ) - throw "malformed mach-o image: indirect symbol table local symbol count exceeds total symbols"; - if ( dynSymbTabCmd->ilocalsym + dynSymbTabCmd->nlocalsym < dynSymbTabCmd->ilocalsym ) - throw "malformed mach-o image: indirect symbol table local symbol count wraps"; - if ( (dynSymbTabCmd->nextdefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iextdefsym > symTabCmd->nsyms) ) - throw "malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols"; - if ( dynSymbTabCmd->iextdefsym + dynSymbTabCmd->nextdefsym < dynSymbTabCmd->iextdefsym ) - throw "malformed mach-o image: indirect symbol table extern symbol count wraps"; - if ( (dynSymbTabCmd->nundefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iundefsym > symTabCmd->nsyms) ) - throw "malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols"; - if ( dynSymbTabCmd->iundefsym + dynSymbTabCmd->nundefsym < dynSymbTabCmd->iundefsym ) - throw "malformed mach-o image: indirect symbol table undefined symbol count wraps"; - } - - - // fSegmentsArrayCount is only 8-bits - if ( *segCount > 255 ) - dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); - - // fSegmentsArrayCount is only 8-bits - if ( *libCount > 4095 ) - dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); - - if ( needsAddedLibSystemDepency(*libCount, mh) ) - *libCount = 1; -} - - - -// create image for main executable -ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context) -{ - //dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n", - // sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed)); - bool compressed; - unsigned int segCount; - unsigned int libCount; - const linkedit_data_command* codeSigCmd; - const encryption_info_command* encryptCmd; - sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); - // instantiate concrete class based on content of load commands - if ( compressed ) - return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); - else -#if SUPPORT_CLASSIC_MACHO - return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); -#else - throw "missing LC_DYLD_INFO load command"; -#endif -} - - -// create image by mapping in a mach-o file -ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat, - uint64_t lenInFat, const struct stat& info, const LinkContext& context) -{ - bool compressed; - unsigned int segCount; - unsigned int libCount; - const linkedit_data_command* codeSigCmd; - const encryption_info_command* encryptCmd; - sniffLoadCommands((const macho_header*)firstPages, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); - // instantiate concrete class based on content of load commands - if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context); - else -#if SUPPORT_CLASSIC_MACHO - return ImageLoaderMachOClassic::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); -#else - throw "missing LC_DYLD_INFO load command"; -#endif -} - -// create image by using cached mach-o file -ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context) -{ - // instantiate right concrete class - bool compressed; - unsigned int segCount; - unsigned int libCount; - const linkedit_data_command* codeSigCmd; - const encryption_info_command* encryptCmd; - sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); - // instantiate concrete class based on content of load commands - if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); - else -#if SUPPORT_CLASSIC_MACHO - return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); -#else - throw "missing LC_DYLD_INFO load command"; -#endif -} - -// create image by copying an in-memory mach-o file -ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context) -{ - bool compressed; - unsigned int segCount; - unsigned int libCount; - const linkedit_data_command* sigcmd; - const encryption_info_command* encryptCmd; - sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd); - // instantiate concrete class based on content of load commands - if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); - else -#if SUPPORT_CLASSIC_MACHO - return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); -#else - throw "missing LC_DYLD_INFO load command"; -#endif -} - - -int ImageLoaderMachO::crashIfInvalidCodeSignature() -{ - // Now that segments are mapped in, try reading from first executable segment. - // If code signing is enabled the kernel will validate the code signature - // when paging in, and kill the process if invalid. - for(unsigned int i=0; i < fSegmentsCount; ++i) { - if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { - // return read value to ensure compiler does not optimize away load - int* p = (int*)segActualLoadAddress(i); - return *p; - } - } - return 0; -} - - -void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) -{ - // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide - 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)) - dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); - fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); - } -#if TEXT_RELOC_SUPPORT - // __TEXT segment always starts at beginning of file and contains mach_header and load commands - if ( segExecutable(i) ) { - if ( segHasRebaseFixUps(i) && (fSlide != 0) ) - fTextSegmentRebases = true; - if ( segHasBindFixUps(i) ) - fTextSegmentBinds = true; - } -#endif -#if __i386__ - if ( segIsReadOnlyImport(i) ) - fReadOnlyImportSegment = true; -#endif - // some segment always starts at beginning of file and contains mach_header and load commands - if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { - fMachOData = (uint8_t*)(segActualLoadAddress(i)); - } - } - - // keep count of prebound images with weak exports - if ( this->participatesInCoalescing() ) { - ++fgImagesRequiringCoalescing; - fRegisteredAsRequiresCoalescing = true; - if ( this->hasCoalescedExports() ) - ++fgImagesHasWeakDefinitions; - } - - // keep count of images used in shared cache - if ( fInSharedCache ) - ++fgImagesUsedFromSharedCache; - - // walk load commands (mapped in at start of __TEXT segment) - const dyld_info_command* dyldInfo = NULL; - const macho_nlist* symbolTable = NULL; - const char* symbolTableStrings = NULL; - const struct load_command* firstUnknownCmd = NULL; - const struct version_min_command* minOSVersionCmd = NULL; - const dysymtab_command* dynSymbolTable = NULL; - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SYMTAB: - { - const struct symtab_command* symtab = (struct symtab_command*)cmd; - symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; - symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); - } - break; - case LC_DYSYMTAB: - dynSymbolTable = (struct dysymtab_command*)cmd; - break; - case LC_SUB_UMBRELLA: - fHasSubUmbrella = true; - break; - case LC_SUB_FRAMEWORK: - fInUmbrella = true; - break; - case LC_SUB_LIBRARY: - fHasSubLibraries = true; - break; - case LC_ROUTINES_COMMAND: - fHasDashInit = true; - break; - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - dyldInfo = (struct dyld_info_command*)cmd; - break; - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0); - #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED - const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0); - if ( isObjCSeg ) - fNotifyObjC = true; - #else - const bool isDataSeg = (strncmp(seg->segname, "__DATA", 6) == 0); - #endif - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const uint8_t type = sect->flags & SECTION_TYPE; - if ( type == S_MOD_INIT_FUNC_POINTERS ) - fHasInitializers = true; - else if ( type == S_MOD_TERM_FUNC_POINTERS ) - fHasTerminators = true; - else if ( type == S_DTRACE_DOF ) - fHasDOFSections = true; - else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) - fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); - else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) - fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); - - #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED - else if ( isObjCSeg ) { - if ( strcmp(sect->sectname, "__image_info") == 0 ) { - const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); - uint32_t flags = imageInfo[1]; - if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) - dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); - } - else if ( ((macho_header*)fMachOData)->filetype == MH_DYLIB ) { - fRetainForObjC = true; - } - } - #else - else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) { - #if __MAC_OS_X_VERSION_MIN_REQUIRED - const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); - uint32_t flags = imageInfo[1]; - if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) - dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); - #endif - fNotifyObjC = true; - } - else if ( isDataSeg && (strncmp(sect->sectname, "__objc_", 7) == 0) && (((macho_header*)fMachOData)->filetype == MH_DYLIB) ) - fRetainForObjC = true; - #endif - } - } - break; - case LC_TWOLEVEL_HINTS: - // no longer supported - break; - case LC_ID_DYLIB: - { - fDylibIDOffset = (uint32_t)((uint8_t*)cmd - fMachOData); - } - break; - case LC_RPATH: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - case LC_LOAD_UPWARD_DYLIB: - case LC_MAIN: - // do nothing, just prevent LC_REQ_DYLD exception from occuring - break; - case LC_VERSION_MIN_MACOSX: - case LC_VERSION_MIN_IPHONEOS: - case LC_VERSION_MIN_TVOS: - case LC_VERSION_MIN_WATCHOS: - minOSVersionCmd = (version_min_command*)cmd; - break; - default: - if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) { - if ( firstUnknownCmd == NULL ) - firstUnknownCmd = cmd; - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - if ( firstUnknownCmd != NULL ) { - if ( minOSVersionCmd != NULL ) { - dyld::throwf("cannot load '%s' because it was built for OS version %u.%u (load command 0x%08X is unknown)", - this->getShortName(), - minOSVersionCmd->version >> 16, ((minOSVersionCmd->version >> 8) & 0xff), - firstUnknownCmd->cmd); - } - else { - dyld::throwf("cannot load '%s' (load command 0x%08X is unknown)", this->getShortName(), firstUnknownCmd->cmd); - } - } - - - if ( dyldInfo != NULL ) - this->setDyldInfo(dyldInfo); - if ( symbolTable != NULL) - this->setSymbolTableInfo(symbolTable, symbolTableStrings, dynSymbolTable); - -} - -// don't do this work in destructor because we need object to be full subclass -// for UnmapSegments() to work -void ImageLoaderMachO::destroy() -{ - // update count of images with weak exports - if ( fRegisteredAsRequiresCoalescing ) { - --fgImagesRequiringCoalescing; - if ( this->hasCoalescedExports() ) - --fgImagesHasWeakDefinitions; - } - - // keep count of images used in shared cache - if ( fInSharedCache ) - --fgImagesUsedFromSharedCache; - - // unmap image when done - UnmapSegments(); -} - - -unsigned int ImageLoaderMachO::segmentCount() const -{ - return fSegmentsCount; -} - - -const macho_segment_command* ImageLoaderMachO::segLoadCommand(unsigned int segIndex) const -{ - uint32_t* lcOffsets = this->segmentCommandOffsets(); - uint32_t lcOffset = lcOffsets[segIndex]; - return (macho_segment_command*)(&fMachOData[lcOffset]); -} - -const char* ImageLoaderMachO::segName(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->segname; -} - - -uintptr_t ImageLoaderMachO::segSize(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->vmsize; -} - - -uintptr_t ImageLoaderMachO::segFileSize(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->filesize; -} - - -bool ImageLoaderMachO::segHasTrailingZeroFill(unsigned int segIndex) -{ - return ( segWriteable(segIndex) && (segSize(segIndex) > segFileSize(segIndex)) ); -} - - -uintptr_t ImageLoaderMachO::segFileOffset(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->fileoff; -} - - -bool ImageLoaderMachO::segReadable(unsigned int segIndex) const -{ - return ( (segLoadCommand(segIndex)->initprot & VM_PROT_READ) != 0); -} - - -bool ImageLoaderMachO::segWriteable(unsigned int segIndex) const -{ - return ( (segLoadCommand(segIndex)->initprot & VM_PROT_WRITE) != 0); -} - - -bool ImageLoaderMachO::segExecutable(unsigned int segIndex) const -{ - return ( (segLoadCommand(segIndex)->initprot & VM_PROT_EXECUTE) != 0); -} - - -bool ImageLoaderMachO::segUnaccessible(unsigned int segIndex) const -{ - return (segLoadCommand(segIndex)->initprot == 0); -} - -bool ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int segIndex) const -{ - return (segLoadCommand(segIndex)->vmaddr != 0); -} - -uintptr_t ImageLoaderMachO::segPreferredLoadAddress(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->vmaddr; -} - -uintptr_t ImageLoaderMachO::segActualLoadAddress(unsigned int segIndex) const -{ - return segLoadCommand(segIndex)->vmaddr + fSlide; -} - - -uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const -{ - return segActualLoadAddress(segIndex) + segSize(segIndex); -} - -bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const -{ -#if TEXT_RELOC_SUPPORT - // scan sections for fix-up bit - const macho_segment_command* segCmd = segLoadCommand(segIndex); - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) - return true; - } -#endif - return false; -} - -bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const -{ -#if TEXT_RELOC_SUPPORT - // scan sections for fix-up bit - const macho_segment_command* segCmd = segLoadCommand(segIndex); - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) - return true; - } -#endif - return false; -} - -#if __i386__ -bool ImageLoaderMachO::segIsReadOnlyImport(unsigned int segIndex) const -{ - const macho_segment_command* segCmd = segLoadCommand(segIndex); - return ( (segCmd->initprot & VM_PROT_EXECUTE) - && ((segCmd->initprot & VM_PROT_WRITE) == 0) - && (strcmp(segCmd->segname, "__IMPORT") == 0) ); -} -#endif - - -void ImageLoaderMachO::UnmapSegments() -{ - // usually unmap image when done - if ( ! this->leaveMapped() && (this->getState() >= dyld_image_state_mapped) ) { - // unmap TEXT segment last because it contains load command being inspected - unsigned int textSegmentIndex = 0; - for(unsigned int i=0; i < fSegmentsCount; ++i) { - //dyld::log("unmap %s at 0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this)); - if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { - textSegmentIndex = i; - } - else { - // update stats - --ImageLoader::fgTotalSegmentsMapped; - ImageLoader::fgTotalBytesMapped -= segSize(i); - munmap((void*)segActualLoadAddress(i), segSize(i)); - } - } - // now unmap TEXT - --ImageLoader::fgTotalSegmentsMapped; - ImageLoader::fgTotalBytesMapped -= segSize(textSegmentIndex); - munmap((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); - } -} - - -// prefetch __DATA/__OBJC pages during launch, but not for dynamically loaded code -void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context) -{ - if ( context.linkingMainExecutable ) { - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - if ( segWriteable(i) && (segFileSize(i) > 0) ) { - // prefetch writable segment that have mmap'ed regions - radvisory advice; - advice.ra_offset = offsetInFat + segFileOffset(i); - advice.ra_count = (int)segFileSize(i); - // limit prefetch to 1MB (256 pages) - if ( advice.ra_count > 1024*1024 ) - advice.ra_count = 1024*1024; - // don't prefetch single pages, let them fault in - fgTotalBytesPreFetched += advice.ra_count; - fcntl(fd, F_RDADVISE, &advice); - if ( context.verboseMapping ) { - dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", - segName(i), segActualLoadAddress(i), segActualLoadAddress(i)+advice.ra_count-1); - } - } - } - } -} - - -bool ImageLoaderMachO::segmentsMustSlideTogether() const -{ - return true; -} - -bool ImageLoaderMachO::segmentsCanSlide() const -{ - return (this->isDylib() || this->isBundle() || this->isPositionIndependentExecutable()); -} - -bool ImageLoaderMachO::isBundle() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( mh->filetype == MH_BUNDLE ); -} - -bool ImageLoaderMachO::isDylib() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( mh->filetype == MH_DYLIB ); -} - -bool ImageLoaderMachO::isExecutable() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( mh->filetype == MH_EXECUTE ); -} - -bool ImageLoaderMachO::isPositionIndependentExecutable() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->filetype == MH_EXECUTE) && ((mh->flags & MH_PIE) != 0) ); -} - - -bool ImageLoaderMachO::forceFlat() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_FORCE_FLAT) != 0 ); -} - -bool ImageLoaderMachO::usesTwoLevelNameSpace() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_TWOLEVEL) != 0 ); -} - -bool ImageLoaderMachO::isPrebindable() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_PREBOUND) != 0 ); -} - -bool ImageLoaderMachO::hasCoalescedExports() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_WEAK_DEFINES) != 0 ); -} - -bool ImageLoaderMachO::hasReferencesToWeakSymbols() const -{ - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); -} - -bool ImageLoaderMachO::participatesInCoalescing() const -{ - const macho_header* mh = (macho_header*)fMachOData; - // if image is loaded with RTLD_LOCAL, then its symbols' visibility - // is reduced and it can't coalesce with other images - if ( this->hasHiddenExports() ) - return false; - return ( (mh->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) != 0 ); -} - - - -void ImageLoaderMachO::setSlide(intptr_t slide) -{ - fSlide = slide; -} - -void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context) -{ - // if dylib being loaded has no code signature load command - if ( codeSigCmd == NULL) { - if (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(); - } - } - else { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // ignore code signatures in binaries built with pre-10.9 tools - if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { - return; - } -#endif - fsignatures_t siginfo; - 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 TARGET_IPHONE_SIMULATOR - // rdar://problem/18759224> check range covered by the code directory after loading - // Attempt to fallback only if we are in the simulator - - if ( result == -1 ) { - result = fcntl(fd, F_ADDFILESIGS, &siginfo); - siginfo.fs_file_start = codeSigCmd->dataoff; - } -#endif - - if ( result == -1 ) { - if ( (errno == EPERM) || (errno == EBADEXEC) ) - dyld::throwf("code signature invalid for '%s'\n", this->getPath()); - 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); - } - } -#endif - } -} - -void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure - // We need to ignore older code signatures because they will be bad. - if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { - return; - } -#endif - if (codeSigCmd != NULL) { - void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, 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()); - } - 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()); - munmap(fdata, lenFileData); - } -} - - -const char* ImageLoaderMachO::getInstallPath() const -{ - if ( fDylibIDOffset != 0 ) { - const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); - return (char*)dylibID + dylibID->dylib.name.offset; - } - return NULL; -} - -void ImageLoaderMachO::registerInterposing() -{ - // 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)) ) { - // Ensure section is within segment - if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) - dyld::throwf("interpose section has malformed address range for %s\n", this->getPath()); - const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); - const size_t count = sect->size / sizeof(InterposeData); - for (size_t j=0; j < count; ++j) { - ImageLoader::InterposeTuple tuple; - tuple.replacement = interposeArray[j].replacement; - tuple.neverImage = this; - tuple.onlyImage = NULL; - tuple.replacee = interposeArray[j].replacee; - // ignore interposing on a weak function that does not exist - if ( tuple.replacee == 0 ) - continue; - // verify that replacement is in this image - if ( this->containsAddress((void*)tuple.replacement) ) { - // chain to any existing interpositions - for (std::vector::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); - } -} - -uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); - const struct load_command* cmd = cmds; - const struct version_min_command* versCmd; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch ( cmd->cmd ) { - case LC_VERSION_MIN_MACOSX: - case LC_VERSION_MIN_IPHONEOS: - case LC_VERSION_MIN_TVOS: - case LC_VERSION_MIN_WATCHOS: - versCmd = (version_min_command*)cmd; - return versCmd->sdk; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return 0; -} - -uint32_t ImageLoaderMachO::sdkVersion() const -{ - return ImageLoaderMachO::sdkVersion(machHeader()); -} - -uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); - const struct load_command* cmd = cmds; - const struct version_min_command* versCmd; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch ( cmd->cmd ) { - case LC_VERSION_MIN_MACOSX: - case LC_VERSION_MIN_IPHONEOS: - case LC_VERSION_MIN_TVOS: - case LC_VERSION_MIN_WATCHOS: - versCmd = (version_min_command*)cmd; - return versCmd->version; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return 0; -} - -uint32_t ImageLoaderMachO::minOSVersion() const -{ - return ImageLoaderMachO::minOSVersion(machHeader()); -} - - -void* ImageLoaderMachO::getThreadPC() 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) { - if ( cmd->cmd == LC_MAIN ) { - entry_point_command* mainCmd = (entry_point_command*)cmd; - void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData); - // verify entry point is in image - if ( this->containsAddress(entry) ) - return entry; - else - throw "LC_MAIN entryoff is out of range"; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return NULL; -} - - -void* ImageLoaderMachO::getMain() 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 - // verify entry point is in image - if ( this->containsAddress(entry) ) { - return entry; - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - throw "no valid entry point"; -} - -bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh) -{ - // ensure that every image depends on something which depends on libSystem - if ( libCount > 1 ) - return false; - - // dyld implicit-libSystem breaks valgrind - if ( mh->filetype == MH_EXECUTE ) - return false; - - bool isNonOSdylib = false; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - case LC_LOAD_UPWARD_DYLIB: - return false; - case LC_ID_DYLIB: - { - const dylib_command* dylibID = (dylib_command*)cmd; - const char* installPath = (char*)cmd + dylibID->dylib.name.offset; - // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents - // but all other dylibs must depend on libSystem for initialization to initialize libSystem first - // rosetta circular dependency spew - isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) ); - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return isNonOSdylib; -} - - -void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) -{ - if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { - DependentLibraryInfo* lib = &libs[0]; - lib->name = LIBSYSTEM_DYLIB_PATH; - lib->info.checksum = 0; - lib->info.minVersion = 0; - lib->info.maxVersion = 0; - lib->required = false; - lib->reExported = false; - lib->upward = false; - } - else { - uint32_t index = 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_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - case LC_LOAD_UPWARD_DYLIB: - { - const struct dylib_command* dylib = (struct dylib_command*)cmd; - DependentLibraryInfo* lib = &libs[index++]; - lib->name = (char*)cmd + dylib->dylib.name.offset; - //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); - lib->info.checksum = dylib->dylib.timestamp; - lib->info.minVersion = dylib->dylib.compatibility_version; - lib->info.maxVersion = dylib->dylib.current_version; - lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); - lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); - lib->upward = (cmd->cmd == LC_LOAD_UPWARD_DYLIB); - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - -ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo(const LibraryInfo&) -{ - LibraryInfo info; - if ( fDylibIDOffset != 0 ) { - const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); - info.minVersion = dylibID->dylib.compatibility_version; - info.maxVersion = dylibID->dylib.current_version; - info.checksum = dylibID->dylib.timestamp; - } - else { - info.minVersion = 0; - info.maxVersion = 0; - info.checksum = 0; - } - return info; -} - -void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector& paths) 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_RPATH: - 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) ) { - 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)]; - strcpy(newRealPath, resolvedPath); - char* addPoint = strrchr(newRealPath,'/'); - if ( addPoint != NULL ) { - strcpy(addPoint, &path[12]); - pathToAdd = strdup(newRealPath); - } - } - } - else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( context.processIsRestricted ) { - dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath()); - break; - } -#endif - char resolvedPath[PATH_MAX]; - if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) { - char newRealPath[strlen(resolvedPath) + strlen(path)]; - strcpy(newRealPath, resolvedPath); - char* addPoint = strrchr(newRealPath,'/'); - if ( addPoint != NULL ) { - strcpy(addPoint, &path[16]); - pathToAdd = strdup(newRealPath); - } - } - } -#if __MAC_OS_X_VERSION_MIN_REQUIRED - else if ( (path[0] != '/') && context.processIsRestricted ) { - dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath()); - break; - } -#endif -#if SUPPORT_ROOT_PATH - else if ( (path[0] == '/') && (context.rootPaths != NULL) ) { - // DYLD_ROOT_PATH should apply to LC_RPATH rpaths - // DYLD_ROOT_PATH can be a list of paths, but at this point we can only support one, so use first combination that exists - bool found = false; - for(const char** rp = context.rootPaths; *rp != NULL; ++rp) { - char newPath[PATH_MAX]; - strlcpy(newPath, *rp, PATH_MAX); - strlcat(newPath, path, PATH_MAX); - struct stat stat_buf; - if ( stat(newPath, &stat_buf) != -1 ) { - //dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath); - pathToAdd = strdup(newPath); - found = true; - break; - } - } - if ( ! found ) { - // make copy so that all elements of 'paths' can be freed - pathToAdd = strdup(path); - } - } -#endif - else { - // make copy so that all elements of 'paths' can be freed - pathToAdd = strdup(path); - } - if ( pathToAdd != NULL ) - paths.push_back(pathToAdd); - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } -} - - -bool ImageLoaderMachO::getUUID(uuid_t uuid) 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_UUID: - uuid_command* uc = (uuid_command*)cmd; - memcpy(uuid, uc->uuid, 16); - return true; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - bzero(uuid, 16); - return false; -} - -void ImageLoaderMachO::doRebase(const LinkContext& context) -{ - // Delay calling setNeverUnload() until we know this is not for dlopen_preflight() - if ( fRetainForObjC ) - this->setNeverUnload(); - - // if prebound and loaded at prebound address, then no need to rebase - if ( this->usablePrebinding(context) ) { - // skip rebasing because prebinding is valid - ++fgImagesWithUsedPrebinding; // bump totals for statistics - return; - } - - // print why prebinding was not used - if ( context.verbosePrebinding ) { - if ( !this->isPrebindable() ) { - dyld::log("dyld: image not prebound, so could not use prebinding in %s\n", this->getPath()); - } - else if ( fSlide != 0 ) { - dyld::log("dyld: image slid, so could not use prebinding in %s\n", this->getPath()); - } - else if ( !this->allDependentLibrariesAsWhenPreBound() ) { - dyld::log("dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath()); - } - else if ( !this->usesTwoLevelNameSpace() ){ - dyld::log("dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath()); - } - else { - dyld::log("dyld: environment variable disabled use of prebinding in %s\n", this->getPath()); - } - } - - //dyld::log("slide=0x%08lX for %s\n", slide, this->getPath()); - -#if PREBOUND_IMAGE_SUPPORT - // if prebound and we got here, then prebinding is not valid, so reset all lazy pointers - // if this image is in the shared cache, do not reset, they will be bound in doBind() - if ( this->isPrebindable() && !fInSharedCache ) - this->resetPreboundLazyPointers(context); -#endif - - // if loaded at preferred address, no rebasing necessary - if ( this->fSlide == 0 ) - return; - -#if TEXT_RELOC_SUPPORT - // if there are __TEXT fixups, temporarily make __TEXT writable - if ( fTextSegmentRebases ) - this->makeTextSegmentWritable(context, true); -#endif - - // do actual rebasing - this->rebase(context, fSlide); - -#if TEXT_RELOC_SUPPORT - // if there were __TEXT fixups, restore write protection - if ( fTextSegmentRebases ) - this->makeTextSegmentWritable(context, false); - -#endif -} - -#if TEXT_RELOC_SUPPORT -void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) -{ - for(unsigned int i=0; i < fSegmentsCount; ++i) { - if ( segExecutable(i) ) { - if ( writeable ) { - segMakeWritable(i, context); - } - else { - #if !__i386__ && !__x86_64__ - // some processors require range to be invalidated before it is made executable - sys_icache_invalidate((void*)segActualLoadAddress(i), segSize(textSegmentIndex)); - #endif - segProtect(i, context); - } - } - } - -} -#endif - -const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const -{ - // look in this image first - const ImageLoader::Symbol* result = this->findShallowExportedSymbol(name, foundIn); - if ( result != NULL ) - return result; - - if ( searchReExports ) { - for(unsigned int i=0; i < libraryCount(); ++i){ - if ( libReExported(i) ) { - ImageLoader* image = libImage(i); - if ( image != NULL ) { - const char* reExPath = libPath(i); - result = image->findExportedSymbol(name, searchReExports, reExPath, foundIn); - if ( result != NULL ) - return result; - } - } - } - } - - - return NULL; -} - - - -uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, - const ImageLoader* requestor, bool runResolver, const char* symbolName) const -{ - return this->getSymbolAddress(sym, requestor, context, runResolver); -} - -uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, - const LinkContext& context, bool runResolver) const -{ - uintptr_t result = exportedSymbolAddress(context, sym, requestor, runResolver); - // check for interposing overrides - result = interposedAddress(context, result, requestor); - return result; -} - -ImageLoader::DefinitionFlags ImageLoaderMachO::getExportedSymbolInfo(const Symbol* sym) const -{ - if ( exportedSymbolIsWeakDefintion(sym) ) - return kWeakDefinition; - else - return kNoDefinitionOptions; -} - -const char* ImageLoaderMachO::getExportedSymbolName(const Symbol* sym) const -{ - return exportedSymbolName(sym); -} - -uint32_t ImageLoaderMachO::getExportedSymbolCount() const -{ - return exportedSymbolCount(); -} - - -const ImageLoader::Symbol* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index) const -{ - return exportedSymbolIndexed(index); -} - - -uint32_t ImageLoaderMachO::getImportedSymbolCount() const -{ - return importedSymbolCount(); -} - - -const ImageLoader::Symbol* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index) const -{ - return importedSymbolIndexed(index); -} - - -ImageLoader::ReferenceFlags ImageLoaderMachO::getImportedSymbolInfo(const ImageLoader::Symbol* sym) const -{ - ImageLoader::ReferenceFlags flags = kNoReferenceOptions; - return flags; -} - - -const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol* sym) const -{ - return importedSymbolName(sym); -} - - -bool ImageLoaderMachO::getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) -{ - 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 ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { - *start = (uintptr_t*)(sect->addr + fSlide); - *length = sect->size; - return true; - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - *start = NULL; - *length = 0; - return false; -} - -void ImageLoaderMachO::getUnwindInfo(dyld_unwind_sections* info) -{ - info->mh = this->machHeader(); - info->dwarf_section = 0; - info->dwarf_section_length = 0; - info->compact_unwind_section = 0; - info->compact_unwind_section_length = 0; - if ( fEHFrameSectionOffset != 0 ) { - const macho_section* sect = (macho_section*)&fMachOData[fEHFrameSectionOffset]; - info->dwarf_section = (void*)(sect->addr + fSlide); - info->dwarf_section_length = sect->size; - } - if ( fUnwindInfoSectionOffset != 0 ) { - const macho_section* sect = (macho_section*)&fMachOData[fUnwindInfoSectionOffset]; - info->compact_unwind_section = (void*)(sect->addr + fSlide); - info->compact_unwind_section_length = sect->size; - } -} - -intptr_t ImageLoaderMachO::computeSlide(const mach_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); - const load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const macho_segment_command* seg = (macho_segment_command*)cmd; - if ( (seg->fileoff == 0) && (seg->filesize != 0) ) - return (char*)mh - (char*)(seg->vmaddr); - } - cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); - } - return 0; -} - -bool ImageLoaderMachO::findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, size_t* sectSize) -{ - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); - const load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - const macho_segment_command* seg = (macho_segment_command*)cmd; - const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command)); - const macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { - *sectAddress = (void*)(sect->addr + computeSlide(mh)); - *sectSize = sect->size; - return true; - } - } - } - break; - } - cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); - } - return false; -} - - -bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) -{ - 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; - const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide(); - 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 ( (unslidInteriorAddress >= seg->vmaddr) && (unslidInteriorAddress < (seg->vmaddr+seg->vmsize)) ) { - 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->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; - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return false; -} - -const char* ImageLoaderMachO::libPath(unsigned int index) const -{ - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - unsigned count = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch ( cmd->cmd ) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - case LC_LOAD_UPWARD_DYLIB: - if ( index == count ) { - const struct dylib_command* dylibCmd = (struct dylib_command*)cmd; - return (char*)cmd + dylibCmd->dylib.name.offset; - } - ++count; - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // if image linked with nothing and we implicitly added libSystem.dylib, return that - if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { - return LIBSYSTEM_DYLIB_PATH; - } - - return NULL; -} - - -void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, - const char* referencedFrom, const char* fromVersMismatch, - const char* expectedIn) -{ - // record values for possible use by CrashReporter or Finder - (*context.setErrorStrings)(DYLD_EXIT_REASON_SYMBOL_MISSING, referencedFrom, expectedIn, symbol); - dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n", - symbol, referencedFrom, fromVersMismatch, expectedIn); -} - -const mach_header* ImageLoaderMachO::machHeader() const -{ - return (mach_header*)fMachOData; -} - -uintptr_t ImageLoaderMachO::getSlide() const -{ - return fSlide; -} - -// hmm. maybe this should be up in ImageLoader?? -const void* ImageLoaderMachO::getEnd() const -{ - uintptr_t lastAddress = 0; - for(unsigned int i=0; i < fSegmentsCount; ++i) { - uintptr_t segEnd = segActualEndAddress(i); - if ( strcmp(segName(i), "__UNIXSTACK") != 0 ) { - if ( segEnd > lastAddress ) - lastAddress = segEnd; - } - } - return (const void*)lastAddress; -} - - -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) : ""), - 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) : ""), - symbolName, (uintptr_t)location, value); - } -#if LOG_BINDINGS -// dyld::logBindings("%s: %s\n", targetImage->getShortName(), symbolName); -#endif - - // do actual update - uintptr_t* locationToFix = (uintptr_t*)location; - uint32_t* loc32; - uintptr_t newValue = value+addend; - uint32_t value32; - switch (type) { - case BIND_TYPE_POINTER: - // test first so we don't needless dirty pages - if ( *locationToFix != newValue ) - *locationToFix = newValue; - break; - case BIND_TYPE_TEXT_ABSOLUTE32: - loc32 = (uint32_t*)locationToFix; - value32 = (uint32_t)newValue; - if ( *loc32 != value32 ) - *loc32 = value32; - break; - case BIND_TYPE_TEXT_PCREL32: - loc32 = (uint32_t*)locationToFix; - value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4)); - if ( *loc32 != value32 ) - *loc32 = value32; - break; - default: - dyld::throwf("bad bind type %d", type); - } - - // update statistics - ++fgTotalBindFixups; - - return newValue; -} - - - - - -#if SUPPORT_OLD_CRT_INITIALIZATION -// first 16 bytes of "start" in crt1.o -#if __i386__ - static uint8_t sStandardEntryPointInstructions[16] = { 0x6a, 0x00, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0x8b, 0x5d, 0x04, 0x89, 0x5c, 0x24 }; -#endif -#endif - -struct DATAdyld { - void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper - void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup - // the following only exist in main executables built for 10.5 or later - ProgramVars vars; -}; - -// These are defined in dyldStartup.s -extern "C" void stub_binding_helper(); - - -void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) -{ - const macho_header* mh = (macho_header*)fMachOData; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd; - // There used to be some optimizations to skip this section scan, but we need to handle the - // __dyld section in libdyld.dylib, so everything needs to be scanned for now. - // CrashTracer: 1,295 crashes in bash at bash: getenv - if ( true ) { - cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( strncmp(seg->segname, "__DATA", 6) == 0 ) { - 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 ( strcmp(sect->sectname, "__dyld" ) == 0 ) { - struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); - #if !__arm64__ && !__ARM_ARCH_7K__ - if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { - if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) - dd->dyldLazyBinder = (void*)&stub_binding_helper; - } - #endif // !__arm64__ - if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { - if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup ) - dd->dyldFuncLookup = (void*)&_dyld_func_lookup; - } - if ( mh->filetype == MH_EXECUTE ) { - // there are two ways to get the program variables - if ( (sect->size > offsetof(DATAdyld, vars)) && (dd->vars.mh == mh) ) { - // some really old binaries have space for vars, but it is zero filled - // main executable has 10.5 style __dyld section that has program variable pointers - context.setNewProgramVars(dd->vars); - } - else { - // main executable is pre-10.5 and requires the symbols names to be looked up - this->lookupProgramVars(context); - #if SUPPORT_OLD_CRT_INITIALIZATION - // If the first 16 bytes of the entry point's instructions do not - // 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 ( context.verboseInit ) - dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n"); - context.setRunInitialzersOldWay(); - } - #endif - } - } - else if ( mh->filetype == MH_DYLIB ) { - const char* installPath = this->getInstallPath(); - if ( (installPath != NULL) && (strncmp(installPath, "/usr/lib/", 9) == 0) ) { - if ( sect->size > offsetof(DATAdyld, vars) ) { - // use ProgramVars from libdyld.dylib but tweak mh field to correct value - dd->vars.mh = context.mainExecutable->machHeader(); - context.setNewProgramVars(dd->vars); - } - } - } - } - else if ( (strcmp(sect->sectname, "__program_vars" ) == 0) && (mh->filetype == MH_EXECUTE) ) { - // this is a Mac OS X 10.6 or later main executable - struct ProgramVars* pv = (struct ProgramVars*)(sect->addr + fSlide); - context.setNewProgramVars(*pv); - } - } - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - - -void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const -{ - ProgramVars vars = context.programVars; - const ImageLoader::Symbol* sym; - - // get mach header directly - vars.mh = (macho_header*)fMachOData; - - // lookup _NXArgc - sym = this->findShallowExportedSymbol("_NXArgc", NULL); - if ( sym != NULL ) - vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false, NULL); - - // lookup _NXArgv - sym = this->findShallowExportedSymbol("_NXArgv", NULL); - if ( sym != NULL ) - vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); - - // lookup _environ - sym = this->findShallowExportedSymbol("_environ", NULL); - if ( sym != NULL ) - vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); - - // lookup __progname - sym = this->findShallowExportedSymbol("___progname", NULL); - if ( sym != NULL ) - vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false, NULL); - - context.setNewProgramVars(vars); -} - - -bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const -{ - // 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() ) { - // allow environment variables to disable prebinding - if ( context.bindFlat ) - return false; - switch ( context.prebindUsage ) { - case kUseAllPrebinding: - return true; - case kUseSplitSegPrebinding: - return this->fIsSplitSeg; - case kUseAllButAppPredbinding: - return (this != context.mainExecutable); - case kUseNoPrebinding: - return false; - } - } - return false; -} - - -void ImageLoaderMachO::doImageInit(const LinkContext& context) -{ - if ( fHasDashInit ) { - 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_ROUTINES_COMMAND: - Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); - // verify initializers are in image - if ( ! this->containsAddress((void*)func) ) { - dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); - } - if ( ! dyld::gProcessInfo->libSystemInitialized ) { - // libSystem initializer must run first - dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath()); - } - if ( context.verboseInit ) - dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - -void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) -{ - if ( fHasInitializers ) { - 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) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const uint8_t type = sect->flags & SECTION_TYPE; - if ( type == S_MOD_INIT_FUNC_POINTERS ) { - Initializer* inits = (Initializer*)(sect->addr + fSlide); - const size_t count = sect->size / sizeof(uintptr_t); - // Ensure __mod_init_func section is within segment - if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) - dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath()); - for (size_t j=0; j < count; ++j) { - Initializer func = inits[j]; - // verify initializers are in image - if ( ! this->containsAddress((void*)func) ) { - dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); - } - if ( ! dyld::gProcessInfo->libSystemInitialized ) { - // libSystem initializer must run first - const char* installPath = getInstallPath(); - if ( (installPath == NULL) || (strcmp(installPath, LIBSYSTEM_DYLIB_PATH) != 0) ) - dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath()); - } - if ( context.verboseInit ) - dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); - bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); - bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); - if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { - // now safe to use malloc() and other calls in libSystem.dylib - dyld::gProcessInfo->libSystemInitialized = true; - } - } - } - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - - - -void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector& dofs) -{ - if ( fHasDOFSections ) { - // walk load commands (mapped in at start of __TEXT segment) - 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_DTRACE_DOF ) { - // Ensure section is within segment - if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) - dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); - ImageLoader::DOFInfo info; - info.dof = (void*)(sect->addr + fSlide); - info.imageHeader = this->machHeader(); - info.imageShortName = this->getShortName(); - dofs.push_back(info); - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - - -bool ImageLoaderMachO::doInitialization(const LinkContext& context) -{ - CRSetCrashLogMessage2(this->getPath()); - - // mach-o has -init and static initializers - doImageInit(context); - doModInitFunctions(context); - - CRSetCrashLogMessage2(NULL); - - return (fHasDashInit || fHasInitializers); -} - -bool ImageLoaderMachO::needsInitialization() -{ - return ( fHasDashInit || fHasInitializers ); -} - - -bool ImageLoaderMachO::needsTermination() -{ - return fHasTerminators; -} - - -void ImageLoaderMachO::doTermination(const LinkContext& context) -{ - if ( fHasTerminators ) { - 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) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const uint8_t type = sect->flags & SECTION_TYPE; - if ( type == S_MOD_TERM_FUNC_POINTERS ) { - // Ensure section is within segment - if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) - dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); - Terminator* terms = (Terminator*)(sect->addr + fSlide); - const size_t count = sect->size / sizeof(uintptr_t); - for (size_t j=count; j > 0; --j) { - Terminator func = terms[j-1]; - // verify terminators are in image - if ( ! this->containsAddress((void*)func) ) { - dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath()); - } - if ( context.verboseInit ) - dyld::log("dyld: calling termination function %p in %s\n", func, this->getPath()); - func(); - } - } - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} - - -void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo) -{ - ImageLoader::printStatisticsDetails(imageCount, timingInfo); - dyld::log("total symbol trie searches: %d\n", fgSymbolTrieSearchs); - dyld::log("total symbol table binary searches: %d\n", fgSymbolTableBinarySearchs); - dyld::log("total images defining weak symbols: %u\n", fgImagesHasWeakDefinitions); - dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing); -} - - -intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) -{ - // preflight and calculate slide if needed - const bool inPIE = (fgNextPIEDylibAddress != 0); - intptr_t slide = 0; - if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { - bool needsToSlide = false; - bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0); - uintptr_t lowAddr = (unsigned long)(-1); - uintptr_t highAddr = 0; - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - const uintptr_t segLow = segPreferredLoadAddress(i); - const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); - if ( segLow < highAddr ) { - if ( dyld_page_size > 4096 ) - dyld::throwf("can't map segments into 16KB pages"); - else - dyld::throwf("overlapping segments"); - } - if ( segLow < lowAddr ) - lowAddr = segLow; - if ( segHigh > highAddr ) - highAddr = segHigh; - - if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) - needsToSlide = true; - } - if ( needsToSlide ) { - // find a chunk of address space to hold all segments - uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context); - slide = addr - lowAddr; - } - } - else if ( ! this->segmentsCanSlide() ) { - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) ) - continue; - if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) - dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i)); - } - } - else { - throw "mach-o does not support independently sliding segments"; - } - return slide; -} - - -uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context) -{ - vm_address_t addr = 0; - vm_size_t size = length; - // In Darling, we're not the only ones doing memory mapping. - // Therefore, we cannot dictate addresses, because we could (would!) conflict with the ELF loader. -#ifndef DARLING - // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either - if ( fgNextPIEDylibAddress != 0 ) { - // add small (0-3 pages) random padding between dylibs - addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*dyld_page_size; - //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); - kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); - if ( r == KERN_SUCCESS ) { - fgNextPIEDylibAddress = addr + size; - return addr; - } - fgNextPIEDylibAddress = 0; - } -#endif - kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); - if ( r != KERN_SUCCESS ) - throw "out of address space"; - - return addr; -} - -bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) -{ - vm_address_t addr = start; - vm_size_t size = length; - kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); - if ( r != KERN_SUCCESS ) - return false; - return true; -} - - - -void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) -{ - // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); - if ( context.verboseMapping ) { - if ( offsetInFat != 0 ) - dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); - else - dyld::log("dyld: Mapping %s\n", this->getPath()); - } - // map in all segments - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - vm_offset_t fileOffset = segFileOffset(i) + offsetInFat; - vm_size_t size = segFileSize(i); - 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)) ) - protection |= PROT_EXEC; - if ( segReadable(i) ) - protection |= PROT_READ; - if ( segWriteable(i) ) { - protection |= PROT_WRITE; - // rdar://problem/22525618 force __LINKEDIT to always be mapped read-only - if ( strcmp(segName(i), "__LINKEDIT") == 0 ) - protection = PROT_READ; - } - } - #if __i386__ - // initially map __IMPORT segments R/W so dyld can update them - if ( segIsReadOnlyImport(i) ) - protection |= PROT_WRITE; - #endif - // wholly zero-fill segments have nothing to mmap() in - if ( size > 0 ) { - if ( (fileOffset+size) > fileLen ) { - dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", - segName(i), (uint64_t)(fileOffset+size), fileLen); - } - void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); - if ( loadAddress == ((void*)(-1)) ) { - int mmapErr = errno; - if ( mmapErr == EPERM ) { - if ( dyld::sandboxBlockedMmap(getPath()) ) - dyld::throwf("file system sandbox blocked mmap() of '%s'", this->getPath()); - else - dyld::throwf("code signing blocked mmap() of '%s'", this->getPath()); - } - else - dyld::throwf("mmap() errno=%d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", - mmapErr, requestedLoadAddress, (uintptr_t)size, segName(i), getPath()); - } - } - // update stats - ++ImageLoader::fgTotalSegmentsMapped; - ImageLoader::fgTotalBytesMapped += size; - if ( context.verboseMapping ) - dyld::log("%18s at 0x%08lX->0x%08lX with permissions %c%c%c\n", segName(i), requestedLoadAddress, requestedLoadAddress+size-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); - } - - // update slide to reflect load location - this->setSlide(slide); -} - -void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) -{ - // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); - if ( context.verboseMapping ) - dyld::log("dyld: Mapping memory %p\n", memoryImage); - // map in all segments - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - vm_address_t loadAddress = segPreferredLoadAddress(i) + slide; - vm_address_t srcAddr = (uintptr_t)memoryImage + segFileOffset(i); - vm_size_t size = segFileSize(i); - kern_return_t r = vm_copy(mach_task_self(), srcAddr, size, loadAddress); - if ( r != KERN_SUCCESS ) - throw "can't map segment"; - if ( context.verboseMapping ) - dyld::log("%18s at 0x%08lX->0x%08lX\n", segName(i), (uintptr_t)loadAddress, (uintptr_t)loadAddress+size-1); - } - // update slide to reflect load location - this->setSlide(slide); - // set R/W permissions on all segments at slide location - for(unsigned int i=0, e=segmentCount(); i < e; ++i) { - segProtect(i, context); - } -} - - -void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context) -{ - vm_prot_t protection = 0; - if ( !segUnaccessible(segIndex) ) { - if ( segExecutable(segIndex) ) - protection |= PROT_EXEC; - if ( segReadable(segIndex) ) - protection |= PROT_READ; - if ( segWriteable(segIndex) ) - protection |= PROT_WRITE; - } - vm_address_t addr = segActualLoadAddress(segIndex); - vm_size_t size = segSize(segIndex); - const bool setCurrentPermissions = false; - kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); - if ( r != KERN_SUCCESS ) { - dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s", - (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath()); - } - if ( context.verboseMapping ) { - dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); - } -} - -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; - if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) ) - protection |= VM_PROT_EXECUTE; - kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); - if ( r != KERN_SUCCESS ) { - dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s", - (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath()); - } - if ( context.verboseMapping ) { - dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); - } -} - - -const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr) -{ - // called by dladdr() - // only works with compressed LINKEDIT if classic symbol table is also present - const dysymtab_command* dynSymbolTable = NULL; - const symtab_command* symtab = NULL; - const macho_segment_command* seg; - const uint8_t* unslidLinkEditBase = NULL; - bool linkEditBaseFound = false; - intptr_t slide = 0; - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); - const load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - seg = (macho_segment_command*)cmd; - if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) { - unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff); - linkEditBaseFound = true; - } - else if ( (seg->fileoff == 0) && (seg->filesize != 0) ) - slide = (uintptr_t)mh - seg->vmaddr; - break; - case LC_SYMTAB: - symtab = (symtab_command*)cmd; - break; - case LC_DYSYMTAB: - dynSymbolTable = (dysymtab_command*)cmd; - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - // no symbol table => no lookup by address - if ( (symtab == NULL) || (dynSymbolTable == NULL) || !linkEditBaseFound ) - return NULL; - - const uint8_t* linkEditBase = unslidLinkEditBase + slide; - const char* symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; - const macho_nlist* symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); - - uintptr_t targetAddress = (uintptr_t)addr - slide; - const struct macho_nlist* bestSymbol = NULL; - // first walk all global symbols - const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; - const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; - for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { - if ( (s->n_type & N_TYPE) == N_SECT ) { - if ( bestSymbol == NULL ) { - if ( s->n_value <= targetAddress ) - bestSymbol = s; - } - else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { - bestSymbol = s; - } - } - } - // next walk all local symbols - const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; - const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; - for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { - if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { - if ( bestSymbol == NULL ) { - if ( s->n_value <= targetAddress ) - bestSymbol = s; - } - else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { - bestSymbol = s; - } - } - } - if ( bestSymbol != NULL ) { -#if __arm__ - if (bestSymbol->n_desc & N_ARM_THUMB_DEF) - *closestAddr = (void*)((bestSymbol->n_value | 1) + slide); - else - *closestAddr = (void*)(bestSymbol->n_value + slide); -#else - *closestAddr = (void*)(bestSymbol->n_value + slide); -#endif - return &symbolTableStrings[bestSymbol->n_un.n_strx]; - } - return NULL; -} - -bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, - uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind) -{ - if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) ) - return false; - uint8_t type = BIND_TYPE_POINTER; - uint8_t symboFlags = 0; - bool done = false; - const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset]; - while ( !done && (p < lazyInfoEnd) ) { - uint8_t immediate = *p & BIND_IMMEDIATE_MASK; - uint8_t opcode = *p & BIND_OPCODE_MASK; - ++p; - switch (opcode) { - case BIND_OPCODE_DONE: - *doneAfterBind = false; - return true; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: - *ordinal = immediate; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: - *ordinal = (int)read_uleb128(p, lazyInfoEnd); - break; - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: - // the special ordinals are negative numbers - if ( immediate == 0 ) - *ordinal = 0; - else { - int8_t signExtended = BIND_OPCODE_MASK | immediate; - *ordinal = signExtended; - } - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: - *symbolName = (char*)p; - symboFlags = immediate; - while (*p != '\0') - ++p; - ++p; - break; - case BIND_OPCODE_SET_TYPE_IMM: - type = immediate; - break; - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - *segIndex = immediate; - *segOffset = read_uleb128(p, lazyInfoEnd); - break; - case BIND_OPCODE_DO_BIND: - *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE); - lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset]; - return true; - break; - case BIND_OPCODE_SET_ADDEND_SLEB: - case BIND_OPCODE_ADD_ADDR_ULEB: - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: - default: - return false; - } - } - return false; -} - -const dyld_info_command* ImageLoaderMachO::findDyldInfoLoadCommand(const mach_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); - const load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - return (dyld_info_command*)cmd; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return NULL; -} - - -uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned segIndex) -{ - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); - const load_command* cmd = cmds; - unsigned curSegIndex = 0; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - if ( segIndex == curSegIndex ) { - const macho_segment_command* segCmd = (macho_segment_command*)cmd; - return segCmd->vmaddr; - } - ++curSegIndex; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return 0; -} - - - diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index 867f4b3..43fe9ba 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -89,7 +89,8 @@ public: virtual bool incrementCoalIterator(CoalIterator&) = 0; virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0; virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0; - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher) = 0; virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) = 0; virtual void doTermination(const LinkContext& context); virtual bool needsInitialization(); @@ -198,8 +199,8 @@ protected: virtual void getRPaths(const LinkContext& context, std::vector&) const; virtual bool getUUID(uuid_t) const; virtual void doRebase(const LinkContext& context); - virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; - virtual void doBindJustLazies(const LinkContext& context) = 0; + virtual void doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) = 0; + virtual void doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) = 0; virtual bool doInitialization(const LinkContext& context); virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs); virtual bool needsTermination(); @@ -230,7 +231,7 @@ protected: bool segIsReadOnlyImport(unsigned int) const; #endif bool segIsReadOnlyData(unsigned int) const; - intptr_t assignSegmentAddresses(const LinkContext& context); + intptr_t assignSegmentAddresses(const LinkContext& context, size_t extraAllocationSize); uintptr_t reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context); bool reserveAddressRange(uintptr_t start, size_t length); void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp index 03d2ffe..8822b73 100644 --- a/src/ImageLoaderMachOClassic.cpp +++ b/src/ImageLoaderMachOClassic.cpp @@ -1351,7 +1351,8 @@ uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfo throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; } -uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { // scan for all lazy-pointer sections const bool twoLevel = this->usesTwoLevelNameSpace(); @@ -1852,7 +1853,7 @@ void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) #endif // __i386__ -void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) +void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) { CRSetCrashLogMessage2(this->getPath()); #if __i386__ @@ -1894,7 +1895,7 @@ void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazys CRSetCrashLogMessage2(NULL); } -void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) +void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) { // some API called requested that all lazy pointers in this image be force bound this->bindIndirectSymbolPointers(context, false, true); diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h index 4196c44..ab6295d 100644 --- a/src/ImageLoaderMachOClassic.h +++ b/src/ImageLoaderMachOClassic.h @@ -54,9 +54,10 @@ public: virtual bool libReExported(unsigned int) const; virtual bool libIsUpward(unsigned int) const; virtual void setLibImage(unsigned int, ImageLoader*, bool, bool); - virtual void doBind(const LinkContext& context, bool forceLazysBound); - virtual void doBindJustLazies(const LinkContext& context); - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); + virtual void doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent); + virtual void doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher); + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher); virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 2b85fb8..54fb8dc 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -135,7 +135,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons const char* installName = image->getInstallPath(); if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) image->setPathUnowned(installName); -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // app crashes when libSystem cannot be found else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) image->setPathUnowned("/usr/lib/libSystem.B.dylib"); @@ -317,7 +317,7 @@ bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const { const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); - // re-export flag is second bit + // upward flag is second bit return ((images[libIndex] & 2) != 0); } @@ -650,8 +650,11 @@ uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, co static void patchCacheUsesOf(const ImageLoader::LinkContext& context, const dyld3::closure::Image* overriddenImage, - uint32_t cacheOffsetOfImpl, const char* symbolName, uintptr_t newImpl) + uint32_t cacheOffsetOfImpl, const char* symbolName, uintptr_t newImpl, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { + patcher.makeWriteable(); + uintptr_t cacheStart = (uintptr_t)context.dyldCache; uint32_t imageIndex = overriddenImage->imageNum() - (uint32_t)context.dyldCache->cachedDylibsImageArray()->startImageNum(); context.dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) { @@ -685,13 +688,15 @@ static void patchCacheUsesOf(const ImageLoader::LinkContext& context, const dyld uintptr_t ImageLoaderMachOCompressed::resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, - bool runResolver, const ImageLoader** foundIn) + bool runResolver, const ImageLoader** foundIn, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { const Symbol* sym; CoalesceNotifier notifier = nullptr; __block uintptr_t foundOutsideCache = 0; __block const char* foundOutsideCachePath = nullptr; __block uintptr_t lastFoundInCache = 0; + if ( this->usesChainedFixups() ) { notifier = ^(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh) { // This block is only called in dyld2 mode when a non-cached image is search for which weak-def implementation to use @@ -708,7 +713,7 @@ uintptr_t ImageLoaderMachOCompressed::resolveWeak(const LinkContext& context, co uint32_t cacheOffsetOfImpl = (uint32_t)((uintptr_t)implAddr - (uintptr_t)context.dyldCache); if ( context.verboseWeakBind ) dyld::log("dyld: weak bind, patching dyld cache uses of %s to use 0x%lX in %s\n", symbolName, foundOutsideCache, foundOutsideCachePath); - patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, symbolName, foundOutsideCache); + patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, symbolName, foundOutsideCache, patcher); } } } @@ -765,7 +770,7 @@ uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context extern const mach_header __dso_handle; uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); if ( imageMinOS > dyldMinOS ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); #else const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); @@ -779,6 +784,7 @@ uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + DyldSharedCache::DataConstLazyScopedWriter& patcher, LastLookup* last, bool runResolver) { *targetImage = NULL; @@ -799,7 +805,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const 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); + symbolAddress = this->resolveWeak(context, symbolName, weak_import, runResolver, targetImage, patcher); } else { if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { @@ -825,8 +831,14 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const symbolAddress = 0; } else { - dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", - symbolName, this->getPath(), libraryOrdinal); + // Try get the path from the load commands + if ( const char* depPath = libPath((unsigned int)libraryOrdinal-1) ) { + dyld::throwf("can't resolve symbol %s in %s because dependent dylib %s could not be loaded", + symbolName, this->getPath(), depPath); + } else { + dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", + symbolName, this->getPath(), libraryOrdinal); + } } } else { @@ -850,7 +862,8 @@ uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, ImageLo 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 char* msg, DyldSharedCache::DataConstLazyScopedWriter& patcher, + LastLookup* last, bool runResolver) { const ImageLoader* targetImage; uintptr_t symbolAddress; @@ -860,7 +873,7 @@ uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, ImageLo symbolAddress = 0; targetImage = nullptr; } else - symbolAddress = image->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver); + symbolAddress = image->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, patcher, last, runResolver); // do actual update return image->bindLocation(context, image->imageBaseAddress(), addr, symbolAddress, type, symbolName, addend, image->getPath(), targetImage ? targetImage->getPath() : NULL, msg, extraBindData, image->fSlide); @@ -875,8 +888,7 @@ void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintp segActualLoadAddress(segmentIndex), segmentEndAddress); } - -void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) +void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) { CRSetCrashLogMessage2(this->getPath()); @@ -885,8 +897,10 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa if ( this->usablePrebinding(context) ) { // don't need to bind // except weak which may now be inline with the regular binds - if (this->participatesInCoalescing()) { + if ( this->participatesInCoalescing() && (fDyldInfo != nullptr) ) { // run through all binding opcodes + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + auto* patcherPtr = &patcher; eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName, uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, @@ -896,7 +910,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa return (uintptr_t)0; return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, addend, libraryOrdinal, extraBindData, - msg, last, runResolver); + msg, *patcherPtr, last, runResolver); }); } } @@ -908,16 +922,23 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa vmAccountingSetSuspended(context, bindingBecauseOfRoot); if ( fChainedFixups != NULL ) { + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); const dyld_chained_fixups_header* fixupsHeader = (dyld_chained_fixups_header*)(fLinkEditBase + fChainedFixups->dataoff); - doApplyFixups(context, fixupsHeader); + doApplyFixups(context, fixupsHeader, patcher); } - else { + else if ( fDyldInfo != nullptr ) { #if TEXT_RELOC_SUPPORT // if there are __TEXT fixups, temporarily make __TEXT writable if ( fTextSegmentBinds ) this->makeTextSegmentWritable(context, true); #endif + // make the cache writable for this block + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + auto* patcherPtr = &patcher; + if ( this->inSharedCache() ) + patcher.makeWriteable(); + // run through all binding opcodes eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName, @@ -926,7 +947,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa const char* msg, LastLookup* last, bool runResolver) { return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, addend, libraryOrdinal, extraBindData, - msg, last, runResolver); + msg, *patcherPtr, last, runResolver); }); #if TEXT_RELOC_SUPPORT @@ -938,7 +959,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa // if this image is in the shared cache, but depends on something no longer in the shared cache, // there is no way to reset the lazy pointers, so force bind them now if ( forceLazysBound || fInSharedCache ) - this->doBindJustLazies(context); + this->doBindJustLazies(context, patcher); // this image is in cache, but something below it is not. If // this image has lazy pointer to a resolver function, then @@ -954,14 +975,36 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa // See if this dylib overrides something in the dyld cache uint32_t dyldCacheOverrideImageNum; if ( context.dyldCache && context.dyldCache->header.builtFromChainedFixups && overridesCachedDylib(dyldCacheOverrideImageNum) ) { + + // make the cache writable for this block + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + auto* patcherPtr = &patcher; + // 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); uint32_t imageIndex = dyldCacheOverrideImageNum - (uint32_t)context.dyldCache->cachedDylibsImageArray()->startImageNum(); + //dyld::log("doBind() found override of %s\n", this->getPath()); context.dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) { uintptr_t newImpl = 0; - const ImageLoader* foundIn; - this->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundIn, &newImpl); - patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl); + const ImageLoader* foundIn = nullptr; + if ( this->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundIn, &newImpl) ) { + //dyld::log(" patchCacheUsesOf(%s) found in %s\n", exportName, foundIn->getPath()); + patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl, *patcherPtr); + } + else { + // allow patched impls to move between re-export sibling dylibs + if ( reExportParent != nullptr ) { + reExportParent->forEachReExportDependent(^(const ImageLoader* reExportedDep, bool& stop) { + uintptr_t siblingImpl = 0; + const ImageLoader* foundInSibling = nullptr; + if ( reExportedDep->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundInSibling, &siblingImpl) ) { + stop = true; + //dyld::log(" patchCacheUsesOf(%s) found in sibling %s\n", exportName, foundInSibling->getPath()); + patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, siblingImpl, *patcherPtr); + } + }); + } + } }); } @@ -972,7 +1015,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa } -void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) +void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) { eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName, @@ -981,11 +1024,12 @@ void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) const char* msg, LastLookup* last, bool runResolver) { return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags, addend, libraryOrdinal, extraBindData, - msg, last, runResolver); + msg, patcher, last, runResolver); }); } -void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const dyld_chained_fixups_header* fixupsHeader) +void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const dyld_chained_fixups_header* fixupsHeader, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { const dyld3::MachOLoaded* ml = (dyld3::MachOLoaded*)machHeader(); const dyld_chained_starts_in_image* starts = (dyld_chained_starts_in_image*)((uint8_t*)fixupsHeader + fixupsHeader->starts_offset); @@ -993,14 +1037,22 @@ void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const // build table of resolved targets for each symbol ordinal STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128); targetAddrs.reserve(fixupsHeader->imports_count); - Diagnostics diag; + __block Diagnostics diag; const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)ml; ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { const ImageLoader* targetImage; uint8_t symbolFlags = weakImport ? BIND_SYMBOL_FLAGS_WEAK_IMPORT : 0; - uintptr_t symbolAddress = this->resolve(context, symbolName, symbolFlags, libOrdinal, &targetImage, NULL, true); - targetAddrs.push_back((void*)(symbolAddress + addend)); + try { + uintptr_t symbolAddress = this->resolve(context, symbolName, symbolFlags, libOrdinal, &targetImage, patcher, NULL, true); + targetAddrs.push_back((void*)(symbolAddress + addend)); + } + catch (const char* msg) { + stop = true; + diag.error("%s", msg); + } }); + if ( diag.hasError() ) + throw strdup(diag.errorMessage()); auto logFixups = ^(void* loc, void* newValue) { dyld::log("dyld: fixup: %s:%p = %p\n", this->getShortName(), loc, newValue); @@ -1009,6 +1061,8 @@ void ImageLoaderMachOCompressed::doApplyFixups(const LinkContext& context, const logFixups = nullptr; ml->fixupAllChainedFixups(diag, starts, fSlide, targetAddrs, logFixups); + if ( diag.hasError() ) + throw strdup(diag.errorMessage()); } void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) @@ -1016,6 +1070,11 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) // mach-o files advertise interposing by having a __DATA __interpose section struct InterposeData { uintptr_t replacement; uintptr_t replacee; }; + // FIDME: It seems wrong to need a patcher here, but resolve may call resolveWeak and patch the cache. + // That would require weak symbols in the interposing section though, which may not be supported. + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + auto* patcherPtr = &patcher; + __block Diagnostics diag; const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)fMachOData; ma->forEachInterposingSection(diag, ^(uint64_t vmOffset, uint64_t vmSize, bool& stopSections) { @@ -1030,11 +1089,12 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) return; uint64_t rebaseTargetRuntimeOffset; uint32_t bindOrdinal; + int64_t ptrAddend; if ( fixupLoc->isRebase(pointerFormat, 0, rebaseTargetRuntimeOffset) ) { //dyld::log("interpose rebase at fixup at %p to 0x%0llX\n", fixupLoc, rebaseTargetRuntimeOffset); lastRebaseTarget = (uintptr_t)(fMachOData+rebaseTargetRuntimeOffset); } - else if ( fixupLoc->isBind(pointerFormat, bindOrdinal) ) { + else if ( fixupLoc->isBind(pointerFormat, bindOrdinal, ptrAddend) ) { //dyld::log("interpose bind fixup at %p to bind ordinal %d\n", fixupLoc, bindOrdinal); __block uint32_t targetBindIndex = 0; ma->forEachChainedFixupTarget(diag, ^(int libraryOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) { @@ -1044,7 +1104,7 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) const ImageLoader* targetImage; uintptr_t targetBindAddress = 0; try { - targetBindAddress = this->resolve(context, symbolName, 0, libraryOrdinal, &targetImage, last, false); + targetBindAddress = this->resolve(context, symbolName, 0, libraryOrdinal, &targetImage, *patcherPtr, last, false); } catch (const char* msg) { if ( !weakImport ) @@ -1095,7 +1155,7 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) const ImageLoader* targetImage; uintptr_t targetBindAddress = 0; try { - targetBindAddress = this->resolve(context, symbolName, 0, libOrdinal, &targetImage, last, false); + targetBindAddress = this->resolve(context, symbolName, 0, libOrdinal, &targetImage, *patcherPtr, last, false); } catch (const char* msg) { if ( !weakImport ) @@ -1122,8 +1182,8 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context) } ImageLoader::fgInterposingTuples.push_back(tuple); } - }, ^(const char* symbolName){ }, - ^() { }); + }, ^(const char* symbolName){ + }); } } }); @@ -1157,6 +1217,13 @@ void ImageLoaderMachOCompressed::makeDataReadOnly() const if ( segIsReadOnlyData(i) ) { uintptr_t start = segActualLoadAddress(i); uintptr_t size = segSize(i); + #if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if ( dyld::isTranslated() ) { + // can't mprotect non-16KB segments + if ( ((size & 0x3FFF) != 0) || ((start & 0x3FFF) != 0) ) + continue; + } + #endif ::mprotect((void*)start, size, PROT_READ); //dyld::log("make read-only 0x%09lX -> 0x%09lX\n", (long)start, (long)(start+size)); } @@ -1481,7 +1548,8 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h // A program built targeting 10.5 will have hybrid stubs. When used with weak symbols // the classic lazy loader is used even when running on 10.6 -uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { // only works with compressed LINKEDIT if classic symbol table is also present const macho_nlist* symbolTable = NULL; @@ -1540,7 +1608,7 @@ uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, c libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; uintptr_t ptrToBind = (uintptr_t)lazyPointer; uintptr_t symbolAddr = bindAt(context, this, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, - NULL, "lazy ", NULL); + NULL, "lazy ", patcher, NULL); ++fgTotalLazyBindFixups; return symbolAddr; } @@ -1567,6 +1635,8 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI if ( lock != NULL ) lock(); } + + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; @@ -1587,7 +1657,7 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI 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 = bindAt(context, this, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, - NULL, "lazy ", NULL, true); + NULL, "lazy ", patcher, NULL, true); // Some old apps had multiple lazy symbols bound at once } while (!doneAfterBind && !context.strictMachORequired); @@ -1838,10 +1908,15 @@ void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)fMachOData; - if ( !ma->hasChainedFixups() ) { + if ( !ma->hasChainedFixups() && (fDyldInfo != nullptr) ) { // Note: all binds that happen as part of normal loading and fixups will have interposing applied. // There is only two cases where we need to parse bind opcodes and apply interposing: + // make the cache writable for this block + DyldSharedCache::DataConstLazyScopedWriter patcher(context.dyldCache, mach_task_self(), context.verboseMapping ? &dyld::log : nullptr); + if ( ma->inDyldCache() ) + patcher.makeWriteable(); + // 1) Lazy pointers are either not bound yet, or in dyld cache they are prebound (to uninterposed target) eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName, @@ -2050,7 +2125,7 @@ void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) { -#if __arm__ || __arm64__ +#if (__arm__ || __arm64__) && !TARGET_OS_SIMULATOR if ( encryptCmd == NULL ) return; // fMachOData not set up yet, need to manually find mach_header diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h index 6f8959f..080b47d 100644 --- a/src/ImageLoaderMachOCompressed.h +++ b/src/ImageLoaderMachOCompressed.h @@ -57,9 +57,10 @@ public: virtual bool libReExported(unsigned int) const; virtual bool libIsUpward(unsigned int) const; virtual void setLibImage(unsigned int, ImageLoader*, bool, bool); - virtual void doBind(const LinkContext& context, bool forceLazysBound); - virtual void doBindJustLazies(const LinkContext& context); - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); + virtual void doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent); + virtual void doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher); + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher); virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); @@ -95,7 +96,7 @@ protected: 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); + const ImageLoader** foundIn, DyldSharedCache::DataConstLazyScopedWriter& patcher); private: @@ -123,12 +124,14 @@ private: uint8_t symboFlags, intptr_t addend, long libraryOrdinal, ExtraBindData *extraBindData, const char* msg, + DyldSharedCache::DataConstLazyScopedWriter& patcher, LastLookup* last, bool runResolver=false); void bindCompressed(const LinkContext& context); void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); uintptr_t resolve(const LinkContext& context, const char* symbolName, - uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + DyldSharedCache::DataConstLazyScopedWriter& patcher, LastLookup* last = NULL, bool runResolver=false); uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, const ImageLoader** foundIn); @@ -147,7 +150,8 @@ private: 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); - void doApplyFixups(const LinkContext& context, const dyld_chained_fixups_header* fixupsHeader); + void doApplyFixups(const LinkContext& context, const dyld_chained_fixups_header* fixupsHeader, + DyldSharedCache::DataConstLazyScopedWriter& patcher); const struct dyld_info_command* fDyldInfo; const struct linkedit_data_command* fChainedFixups; diff --git a/src/ImageLoaderMegaDylib.cpp b/src/ImageLoaderMegaDylib.cpp index 71374b0..5978ac3 100644 --- a/src/ImageLoaderMegaDylib.cpp +++ b/src/ImageLoaderMegaDylib.cpp @@ -196,7 +196,7 @@ bool ImageLoaderMegaDylib::hasDylib(const char* path, unsigned* index) const { const uint8_t* imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, path); if ( imageNode == NULL ) { - #if __MAC_OS_X_VERSION_MIN_REQUIRED + #if TARGET_OS_OSX // not all symlinks are recorded as aliases in accelerator tables if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) { char resolvedPath[PATH_MAX]; @@ -573,7 +573,7 @@ void ImageLoaderMegaDylib::recursiveLoadLibraries(const LinkContext& context, bo recursiveMarkLoaded(context, index); } -unsigned int ImageLoaderMegaDylib::recursiveUpdateDepth(unsigned int maxDepth) +unsigned int ImageLoaderMegaDylib::updateDepth(unsigned int maxDepth) { setDepth(maxDepth); return maxDepth; @@ -946,7 +946,7 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m } } -void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent) { markAllbound(context); } diff --git a/src/ImageLoaderMegaDylib.h b/src/ImageLoaderMegaDylib.h index 081e95b..f001f14 100644 --- a/src/ImageLoaderMegaDylib.h +++ b/src/ImageLoaderMegaDylib.h @@ -83,7 +83,8 @@ public: virtual bool isExecutable() const { unreachable(); } virtual bool isPositionIndependentExecutable() const { unreachable(); } virtual bool forceFlat() const { unreachable(); } - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) { unreachable(); } + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context, + DyldSharedCache::DataConstLazyScopedWriter& patcher) { unreachable(); } virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) { unreachable(); } virtual void doTermination(const LinkContext& context) { unreachable(); } @@ -162,9 +163,9 @@ protected: #endif virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); - virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual unsigned updateDepth(unsigned int maxDepth); virtual void recursiveRebase(const LinkContext& context) { } - virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); + virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent); virtual void recursiveApplyInterposing(const LinkContext& context); virtual void recursiveMakeDataReadOnly(const LinkContext& context) {} virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) { } @@ -174,8 +175,8 @@ protected: virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) { unreachable(); } virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) { return requestorInfo; } virtual void doRebase(const LinkContext& context) { unreachable(); } - virtual void doBind(const LinkContext& context, bool forceLazysBound) { unreachable(); } - virtual void doBindJustLazies(const LinkContext& context) { unreachable(); } + virtual void doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) { unreachable(); } + virtual void doBindJustLazies(const LinkContext& context, DyldSharedCache::DataConstLazyScopedWriter& patcher) { unreachable(); } virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) { unreachable(); } virtual void doInterpose(const LinkContext& context) { unreachable(); } virtual bool doInitialization(const LinkContext& context) { unreachable(); } diff --git a/src/dyld.cpp.orig b/src/dyld.cpp.orig deleted file mode 100644 index 120eab6..0000000 --- a/src/dyld.cpp.orig +++ /dev/null @@ -1,6083 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-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@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // mach_absolute_time() -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include <_simple.h> -//#include -#include -#include -//#include -//#include -#include -#include -#include "../dyld_kernel.h" - -#include - -#ifndef CPU_SUBTYPE_ARM_V5TEJ - #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) -#endif -#ifndef CPU_SUBTYPE_ARM_XSCALE - #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) -#endif -#ifndef CPU_SUBTYPE_ARM_V7 - #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) -#endif -#ifndef CPU_SUBTYPE_ARM_V7F - #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) -#endif -#ifndef CPU_SUBTYPE_ARM_V7S - #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) -#endif -#ifndef CPU_SUBTYPE_ARM_V7K - #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) -#endif -#ifndef LC_DYLD_ENVIRONMENT - #define LC_DYLD_ENVIRONMENT 0x27 -#endif - -#ifndef CPU_SUBTYPE_X86_64_H - #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) -#endif - -#ifndef VM_PROT_SLIDE - #define VM_PROT_SLIDE 0x20 -#endif - -#include -#include - -#include "mach-o/dyld_gdb.h" - -#include "dyld.h" -#include "ImageLoader.h" -#include "ImageLoaderMachO.h" -#include "dyldLibSystemInterface.h" -#if DYLD_SHARED_CACHE_SUPPORT -#include "dyld_cache_format.h" -#endif -#include "dyld_process_info_internal.h" -//#include -#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" -#endif - -#if TARGET_IPHONE_SIMULATOR - extern "C" void* gSyscallHelpers; -#else - #include "dyldSyscallInterface.h" -#endif - - -// not libc header for send() syscall interface -extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); - - -// ARM and x86_64 are the only architecture that use cpu-sub-types -#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) - -#if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 - #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT - #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO - #define macho_segment_command segment_command_64 - #define macho_section section_64 -#else - #define LC_SEGMENT_COMMAND LC_SEGMENT - #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 - #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64 - #define macho_segment_command segment_command - #define macho_section section -#endif - - - -#define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ - - -/* implemented in dyld_gdb.cpp */ -extern void resetAllImages(); -extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); -extern void removeImageFromAllImages(const mach_header* mh); -extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info); -extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]); -extern size_t allImagesCount(); - -// magic so CrashReporter logs message -extern "C" { - char error_string[1024]; -} - -// magic linker symbol for start of dyld binary -extern "C" const macho_header __dso_handle; - - -// -// The file contains the core of dyld used to get a process to main(). -// The API's that dyld supports are implemented in dyldAPIs.cpp. -// -// -// -// -// -namespace dyld { - struct RegisteredDOF { const mach_header* mh; int registrationID; }; - struct DylibOverride { const char* installName; const char* override; }; -} - - -VECTOR_NEVER_DESTRUCTED(ImageLoader*); -VECTOR_NEVER_DESTRUCTED(dyld::RegisteredDOF); -VECTOR_NEVER_DESTRUCTED(dyld::ImageCallback); -VECTOR_NEVER_DESTRUCTED(dyld::DylibOverride); -VECTOR_NEVER_DESTRUCTED(ImageLoader::DynamicReference); - -VECTOR_NEVER_DESTRUCTED(dyld_image_state_change_handler); - -namespace dyld { - - -// -// state of all environment variables dyld uses -// -struct EnvironmentVariables { - const char* const * DYLD_FRAMEWORK_PATH; - const char* const * DYLD_FALLBACK_FRAMEWORK_PATH; - const char* const * DYLD_LIBRARY_PATH; - const char* const * DYLD_FALLBACK_LIBRARY_PATH; - const char* const * DYLD_INSERT_LIBRARIES; - const char* const * LD_LIBRARY_PATH; // for unix conformance - const char* const * DYLD_VERSIONED_LIBRARY_PATH; - const char* const * DYLD_VERSIONED_FRAMEWORK_PATH; - bool DYLD_PRINT_LIBRARIES_POST_LAUNCH; - bool DYLD_BIND_AT_LAUNCH; - bool DYLD_PRINT_STATISTICS; - bool DYLD_PRINT_STATISTICS_DETAILS; - bool DYLD_PRINT_OPTS; - bool DYLD_PRINT_ENV; - bool DYLD_DISABLE_DOFS; - bool DYLD_PRINT_CS_NOTIFICATIONS; - // DYLD_SHARED_CACHE_DONT_VALIDATE ==> sSharedCacheIgnoreInodeAndTimeStamp - // DYLD_SHARED_CACHE_DIR ==> sSharedCacheDir - // DYLD_ROOT_PATH ==> gLinkContext.rootPaths - // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix - // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts - // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv - // DYLD_FORCE_FLAT_NAMESPACE ==> gLinkContext.bindFlat - // DYLD_PRINT_INITIALIZERS ==> gLinkContext.verboseInit - // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping - // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind - // DYLD_PRINT_WEAK_BINDINGS ==> gLinkContext.verboseWeakBind - // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase - // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF - // DYLD_PRINT_APIS ==> gLogAPIs - // DYLD_IGNORE_PREBINDING ==> gLinkContext.prebindUsage - // DYLD_PREBIND_DEBUG ==> gLinkContext.verbosePrebinding - // DYLD_NEW_LOCAL_SHARED_REGIONS ==> gLinkContext.sharedRegionMode - // DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode - // DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings - // DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths - // DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing - // DYLD_PRINT_LIBRARIES ==> gLinkContext.verboseLoading -}; - - - -typedef std::vector StateHandlers; - - -enum EnvVarMode { envNone, envPrintOnly, envAll }; - -static const char* _simple_getenv(const char* envp[], const char* key) -{ - const size_t len = strlen(key); - for (int i = 0; envp[i] != NULL; i++) - { - if (strncmp(envp[i], key, len) == 0 && envp[i][len] == '=') - return envp[i] + len + 1; - } - return NULL; -} - -// all global state -static const char* sExecPath = NULL; -static const char* sExecShortName = NULL; -static const macho_header* sMainExecutableMachHeader = NULL; -#if CPU_SUBTYPES_SUPPORTED -static cpu_type_t sHostCPU; -static cpu_subtype_t sHostCPUsubtype; -#endif -static ImageLoaderMachO* sMainExecutable = NULL; -static EnvVarMode sEnvMode = envNone; -static size_t sInsertedDylibCount = 0; -static std::vector sAllImages; -static std::vector sImageRoots; -static std::vector sImageFilesNeedingTermination; -static std::vector sImageFilesNeedingDOFUnregistration; -static std::vector sAddImageCallbacks; -static std::vector sRemoveImageCallbacks; -static bool sRemoveImageCallbacksInUse = false; -static void* sSingleHandlers[7][3]; -static void* sBatchHandlers[7][3]; -static ImageLoader* sLastImageByAddressCache; -static EnvironmentVariables sEnv; -#if __MAC_OS_X_VERSION_MIN_REQUIRED -static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; -static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; -#else -static const char* sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; -static const char* sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL }; -#endif -static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; -static const char* sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL }; -static UndefinedHandler sUndefinedHandler = NULL; -static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked -#if DYLD_SHARED_CACHE_SUPPORT -static const dyld_cache_header* sSharedCache = NULL; -static long sSharedCacheSlide = 0; -static bool sSharedCacheIgnoreInodeAndTimeStamp = false; - bool gSharedCacheOverridden = false; -#if __IPHONE_OS_VERSION_MIN_REQUIRED - static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR; - #define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024 -#else - static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; -#endif -#endif -ImageLoader::LinkContext gLinkContext; -bool gLogAPIs = false; -#if SUPPORT_ACCELERATE_TABLES -bool gLogAppAPIs = false; -#endif -const struct LibSystemHelpers* gLibSystemHelpers = NULL; -#if SUPPORT_OLD_CRT_INITIALIZATION -bool gRunInitializersOldWay = false; -#endif -static std::vector sDylibOverrides; -#if !TARGET_IPHONE_SIMULATOR -static int sLogSocket = -1; -#endif -static bool sFrameworksFoundAsDylibs = false; -#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT -static bool sHaswell = false; -#endif -static std::vector sDynamicReferences; -static OSSpinLock sDynamicReferencesLock = 0; -#if !TARGET_IPHONE_SIMULATOR -static bool sLogToFile = false; -#endif -static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries"; - -static _dyld_objc_notify_mapped sNotifyObjCMapped; -static _dyld_objc_notify_init sNotifyObjCInit; -static _dyld_objc_notify_unmapped sNotifyObjCUnmapped; - -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR -static bool sForceStderr = false; -#endif - - - -#if SUPPORT_ACCELERATE_TABLES -static ImageLoaderMegaDylib* sAllCacheImagesProxy = NULL; -static bool sDisableAcceleratorTables = false; -#endif - -// -// The MappedRanges structure is used for fast address->image lookups. -// The table is only updated when the dyld lock is held, so we don't -// need to worry about multiple writers. But readers may look at this -// data without holding the lock. Therefore, all updates must be done -// in an order that will never cause readers to see inconsistent data. -// The general rule is that if the image field is non-NULL then -// the other fields are valid. -// -struct MappedRanges -{ - MappedRanges* next; - unsigned long count; - struct { - ImageLoader* image; - uintptr_t start; - uintptr_t end; - } array[1]; -}; - -static MappedRanges* sMappedRangesStart; - -void addMappedRange(ImageLoader* image, uintptr_t start, uintptr_t end) -{ - //dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName()); - for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { - for (unsigned long i=0; i < p->count; ++i) { - if ( p->array[i].image == NULL ) { - p->array[i].start = start; - p->array[i].end = end; - // add image field last with a barrier so that any reader will see consistent records - OSMemoryBarrier(); - p->array[i].image = image; - return; - } - } - } - // table must be full, chain another -#if SUPPORT_ACCELERATE_TABLES - unsigned count = (sAllCacheImagesProxy != NULL) ? 16 : 400; -#else - unsigned count = 400; -#endif - size_t allocationSize = sizeof(MappedRanges) + (count-1)*3*sizeof(void*); - MappedRanges* newRanges = (MappedRanges*)malloc(allocationSize); - bzero(newRanges, allocationSize); - newRanges->count = count; - newRanges->array[0].start = start; - newRanges->array[0].end = end; - newRanges->array[0].image = image; - OSMemoryBarrier(); - if ( sMappedRangesStart == NULL ) { - sMappedRangesStart = newRanges; - } - else { - for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { - if ( p->next == NULL ) { - OSMemoryBarrier(); - p->next = newRanges; - break; - } - } - } -} - -void removedMappedRanges(ImageLoader* image) -{ - for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { - for (unsigned long i=0; i < p->count; ++i) { - if ( p->array[i].image == image ) { - // clear with a barrier so that any reader will see consistent records - OSMemoryBarrier(); - p->array[i].image = NULL; - } - } - } -} - -ImageLoader* findMappedRange(uintptr_t target) -{ - for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { - for (unsigned long i=0; i < p->count; ++i) { - if ( p->array[i].image != NULL ) { - if ( (p->array[i].start <= target) && (target < p->array[i].end) ) - return p->array[i].image; - } - } - } - return NULL; -} - - - -const char* mkstringf(const char* format, ...) -{ - _SIMPLE_STRING buf = _simple_salloc(); - if ( buf != NULL ) { - va_list list; - va_start(list, format); - _simple_vsprintf(buf, format, list); - va_end(list); - const char* t = strdup(_simple_string(buf)); - _simple_sfree(buf); - if ( t != NULL ) - return t; - } - return "mkstringf, out of memory error"; -} - - -void throwf(const char* format, ...) -{ - _SIMPLE_STRING buf = _simple_salloc(); - if ( buf != NULL ) { - va_list list; - va_start(list, format); - _simple_vsprintf(buf, format, list); - va_end(list); - const char* t = strdup(_simple_string(buf)); - _simple_sfree(buf); - if ( t != NULL ) - throw t; - } - throw "throwf, out of memory error"; -} - - -#if !TARGET_IPHONE_SIMULATOR -static int sLogfile = STDERR_FILENO; -#endif - -#if !TARGET_IPHONE_SIMULATOR -// based on CFUtilities.c: also_do_stderr() -static bool useSyslog() -{ - // Use syslog() for processes managed by launchd - static bool launchdChecked = false; - static bool launchdOwned = false; - if ( !launchdChecked && gProcessInfo->libSystemInitialized ) { - if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) { - // only call isLaunchdOwned() after libSystem is initialized - launchdOwned = (*gLibSystemHelpers->isLaunchdOwned)(); - launchdChecked = true; - } - } - if ( launchdChecked && launchdOwned ) - return true; - - // If stderr is not available, use syslog() - struct stat sb; - int result = fstat(STDERR_FILENO, &sb); - if ( result < 0 ) - return true; // file descriptor 2 is closed - - return false; -} - - -static void socket_syslogv(int priority, const char* format, va_list list) -{ - // lazily create socket and connection to syslogd - if ( sLogSocket == -1 ) { - sLogSocket = ::socket(AF_UNIX, SOCK_DGRAM, 0); - if (sLogSocket == -1) - return; // cannot log - ::fcntl(sLogSocket, F_SETFD, 1); - - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path)); - if ( ::connect(sLogSocket, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) { - ::close(sLogSocket); - sLogSocket = -1; - return; - } - } - - // format message to syslogd like: "Process[pid]: message" - _SIMPLE_STRING buf = _simple_salloc(); - if ( buf == NULL ) - return; - if ( _simple_sprintf(buf, "<%d>%s[%d]: ", LOG_USER|LOG_NOTICE, sExecShortName, getpid()) == 0 ) { - if ( _simple_vsprintf(buf, format, list) == 0 ) { - const char* p = _simple_string(buf); - ::__sendto(sLogSocket, p, strlen(p), 0, NULL, 0); - } - } - _simple_sfree(buf); -} - - - -void vlog(const char* format, va_list list) -{ -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR - // log to console when running iOS app from Xcode - if ( !sLogToFile && !sForceStderr && useSyslog() ) -#else - if ( !sLogToFile && useSyslog() ) -#endif - socket_syslogv(LOG_ERR, format, list); - else { - _simple_vdprintf(sLogfile, format, list); - } -} - -void log(const char* format, ...) -{ - va_list list; - va_start(list, format); - vlog(format, list); - va_end(list); -} - - -void vwarn(const char* format, va_list list) -{ - _simple_dprintf(sLogfile, "dyld: warning, "); - _simple_vdprintf(sLogfile, format, list); -} - -void warn(const char* format, ...) -{ - va_list list; - va_start(list, format); - vwarn(format, list); - va_end(list); -} - - -#endif // !TARGET_IPHONE_SIMULATOR - - -// control access to sAllImages through a lock -// because global dyld lock is not held during initialization phase of dlopen() -// Use OSSpinLockLock to allow yielding -static OSSpinLock sAllImagesLock = 0; - -static void allImagesLock() -{ - OSSpinLockLock(&sAllImagesLock); -} - -static void allImagesUnlock() -{ - OSSpinLockUnlock(&sAllImagesLock); -} - - -// utility class to assure files are closed when an exception is thrown -class FileOpener { -public: - FileOpener(const char* path); - ~FileOpener(); - int getFileDescriptor() { return fd; } -private: - int fd; -}; - -FileOpener::FileOpener(const char* path) - : fd(-1) -{ - fd = my_open(path, O_RDONLY, 0); -} - -FileOpener::~FileOpener() -{ - if ( fd != -1 ) - close(fd); -} - - -static void registerDOFs(const std::vector& dofs) -{ - const size_t dofSectionCount = dofs.size(); - if ( !sEnv.DYLD_DISABLE_DOFS && (dofSectionCount != 0) ) { - int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); - if ( fd < 0 ) { - //dyld::warn("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. - for (unsigned int i=0; i < dofSectionCount; ++i) { - RegisteredDOF info; - info.mh = dofs[i].imageHeader; - info.registrationID = (int)(ioctlData->dofiod_helpers[i].dofhp_dof); - sImageFilesNeedingDOFUnregistration.push_back(info); - if ( gLinkContext.verboseDOF ) { - dyld::log("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n", - dofs[i].dof, dofs[i].imageShortName, info.registrationID); - } - } - } - else { - //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); - } - close(fd); - } - } -} - -static void unregisterDOF(int registrationID) -{ - int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); - if ( fd < 0 ) { - dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n"); - } - else { - ioctl(fd, DTRACEHIOC_REMOVE, registrationID); - close(fd); - if ( gLinkContext.verboseInit ) - dyld::warn("unregistering DOF section ID=0x%08X with dtrace\n", registrationID); - } -} - - -// -// _dyld_register_func_for_add_image() is implemented as part of the general image state change notification -// -static void notifyAddImageCallbacks(ImageLoader* image) -{ - // use guard so that we cannot notify about the same image twice - if ( ! image->addFuncNotified() ) { - for (std::vector::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) - (*it)(image->machHeader(), image->getSlide()); - image->setAddFuncNotified(); - } -} - - - -// notify gdb about these new images -static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) -{ - // don't add images without paths to all-image-info-list - if ( info[0].imageFilePath != NULL ) - addImagesToAllImages(infoCount, info); - return NULL; -} - - -static StateHandlers* stateToHandlers(dyld_image_states state, void* handlersArray[7][3]) -{ - switch ( state ) { - case dyld_image_state_mapped: - return reinterpret_cast(&handlersArray[0]); - - case dyld_image_state_dependents_mapped: - return reinterpret_cast(&handlersArray[1]); - - case dyld_image_state_rebased: - return reinterpret_cast(&handlersArray[2]); - - case dyld_image_state_bound: - return reinterpret_cast(&handlersArray[3]); - - case dyld_image_state_dependents_initialized: - return reinterpret_cast(&handlersArray[4]); - - case dyld_image_state_initialized: - return reinterpret_cast(&handlersArray[5]); - - case dyld_image_state_terminated: - return reinterpret_cast(&handlersArray[6]); - } - return NULL; -} - -#if SUPPORT_ACCELERATE_TABLES -static dyld_image_state_change_handler getPreInitNotifyHandler(unsigned index) -{ - std::vector* handlers = stateToHandlers(dyld_image_state_dependents_initialized, sSingleHandlers); - if ( index >= handlers->size() ) - return NULL; - return (*handlers)[index]; -} - -static dyld_image_state_change_handler getBoundBatchHandler(unsigned index) -{ - std::vector* handlers = stateToHandlers(dyld_image_state_bound, sBatchHandlers); - if ( index >= handlers->size() ) - return NULL; - return (*handlers)[index]; -} - -static void notifySingleFromCache(dyld_image_states state, const mach_header* mh, const char* path) -{ - //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); - std::vector* handlers = stateToHandlers(state, sSingleHandlers); - if ( handlers != NULL ) { - dyld_image_info info; - info.imageLoadAddress = mh; - info.imageFilePath = path; - info.imageFileModDate = 0; - for (dyld_image_state_change_handler handler : *handlers) { - const char* result = (*handler)(state, 1, &info); - if ( (result != NULL) && (state == dyld_image_state_mapped) ) { - //fprintf(stderr, " image rejected by handler=%p\n", *it); - // make copy of thrown string so that later catch clauses can free it - const char* str = strdup(result); - throw str; - } - } - } - if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) { - (*sNotifyObjCInit)(path, mh); - } -} -#endif - -static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; - - -static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[]) -{ - unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry); - unsigned pathsSize = 0; - for (unsigned j=0; j < imageCount; ++j) { - pathsSize += (strlen(infos[j].imageFilePath) + 1); - } - unsigned totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align - if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) { - // Putting all image paths into one message would make buffer too big. - // Instead split into two messages. Recurse as needed until paths fit in buffer. - unsigned imageHalfCount = imageCount/2; - notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos); - notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]); - return; - } - uint8_t buffer[totalSize]; - dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer; - header->version = 1; - header->imageCount = imageCount; - header->imagesOffset = sizeof(dyld_process_info_notify_header); - header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize; - header->timestamp = mach_absolute_time(); - dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset]; - char* const pathPoolStart = (char*)&buffer[header->stringsOffset]; - char* pathPool = pathPoolStart; - for (unsigned j=0; j < imageCount; ++j) { - strcpy(pathPool, infos[j].imageFilePath); - uint32_t len = (uint32_t)strlen(pathPool); - bzero(entries->uuid, 16); - const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress); - if ( image != NULL ) { - image->getUUID(entries->uuid); - } -#if SUPPORT_ACCELERATE_TABLES - else if ( sAllCacheImagesProxy != NULL ) { - const mach_header* mh; - const char* path; - unsigned index; - if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) { - sAllCacheImagesProxy->getDylibUUID(index, entries->uuid); - } - } -#endif - entries->loadAddress = (uint64_t)infos[j].imageLoadAddress; - entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart); - entries->pathLength = len; - pathPool += (len +1); - ++entries; - } - - if ( sNotifyReplyPorts[portSlot] == 0 ) { - if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) ) - mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND); - //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]); - } - //dyld::log("found port to send to\n"); - mach_msg_header_t* h = (mach_msg_header_t*)buffer; - h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE - h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID; - h->msgh_local_port = sNotifyReplyPorts[portSlot]; - h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot]; - h->msgh_reserved = 0; - h->msgh_size = (mach_msg_size_t)sizeof(buffer); - //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id); - kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 100, MACH_PORT_NULL); - //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); - if ( sendResult == MACH_SEND_INVALID_DEST ) { - // sender is not responding, detatch - //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]); - mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]); - mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); - dyld::gProcessInfo->notifyPorts[portSlot] = 0; - sNotifyReplyPorts[portSlot] = 0; - } -} - -#define MAX_KERNEL_IMAGES_PER_CALL (100*3) - -static void flushKernelNotifications(bool loading, bool force, std::array& kernelInfos, uint32_t &kernelInfoCount) { -#if 0 - if ((force && kernelInfoCount != 0) || kernelInfoCount == MAX_KERNEL_IMAGES_PER_CALL) { - if (loading) { - task_register_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); - } else { - task_unregister_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); - } - kernelInfoCount = 0; - } -#endif -} - -static -void queueKernelNotification(const ImageLoader& image, bool loading, std::array& kernelInfos, uint32_t &kernelInfoCount) { - if ( !image.inSharedCache() ) { - ino_t inode = image.getInode(); - image.getUUID(kernelInfos[kernelInfoCount].uuid); - memcpy(&kernelInfos[kernelInfoCount].fsobjid, &inode, 8); - kernelInfos[kernelInfoCount].load_addr = (uint64_t)image.machHeader(); - // FIXME we should also be grabbing the device ID, but that is not necessary yet, - // and requires threading it through the ImageLoader - kernelInfos[kernelInfoCount].fsid.val[0] = 0; - kernelInfos[kernelInfoCount].fsid.val[1] = 0; - kernelInfoCount++; - } - flushKernelNotifications(loading, false, kernelInfos, kernelInfoCount); -} - -void notifyKernel(const ImageLoader& image, bool loading) { - std::array kernelInfos; - uint32_t kernelInfoCount = 0; - queueKernelNotification(image, loading, kernelInfos, kernelInfoCount); - flushKernelNotifications(loading, true, kernelInfos, kernelInfoCount); -} - -static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) -{ - //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); - std::vector* handlers = stateToHandlers(state, sSingleHandlers); - if ( handlers != NULL ) { - dyld_image_info info; - info.imageLoadAddress = image->machHeader(); - info.imageFilePath = image->getRealPath(); - info.imageFileModDate = image->lastModified(); - for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { - const char* result = (*it)(state, 1, &info); - if ( (result != NULL) && (state == dyld_image_state_mapped) ) { - //fprintf(stderr, " image rejected by handler=%p\n", *it); - // make copy of thrown string so that later catch clauses can free it - const char* str = strdup(result); - throw str; - } - } - } - if ( state == dyld_image_state_mapped ) { - // Save load addr + UUID for images from outside the shared cache - if ( !image->inSharedCache() ) { - dyld_uuid_info info; - if ( image->getUUID(info.imageUUID) ) { - info.imageLoadAddress = image->machHeader(); - addNonSharedCacheImageUUID(info); - } - } - } - if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) { - uint64_t t0 = mach_absolute_time(); - (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); - uint64_t t1 = mach_absolute_time(); - uint64_t t2 = mach_absolute_time(); - uint64_t timeInObjC = t1-t0; - uint64_t emptyTime = (t2-t1)*100; - if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) { - timingInfo->addTime(image->getShortName(), timeInObjC); - } - } -#if 0 - // 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; - } - } - } -#endif -} - - -// -// Normally, dyld_all_image_infos is only updated in batches after an entire -// graph is loaded. But if there is an error loading the initial set of -// dylibs needed by the main executable, dyld_all_image_infos is not yet set -// up, leading to usually brief crash logs. -// -// This function manually adds the images loaded so far to dyld::gProcessInfo. -// It should only be called before terminating. -// -void syncAllImages() -{ - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { - dyld_image_info info; - ImageLoader* image = *it; - info.imageLoadAddress = image->machHeader(); - info.imageFilePath = image->getRealPath(); - info.imageFileModDate = image->lastModified(); - // add to all_image_infos if not already there - bool found = false; - int existingCount = dyld::gProcessInfo->infoArrayCount; - const dyld_image_info* existing = dyld::gProcessInfo->infoArray; - if ( existing != NULL ) { - for (int i=0; i < existingCount; ++i) { - if ( existing[i].imageLoadAddress == info.imageLoadAddress ) { - //dyld::log("not adding %s\n", info.imageFilePath); - found = true; - break; - } - } - } - if ( ! found ) { - //dyld::log("adding %s\n", info.imageFilePath); - addImagesToAllImages(1, &info); - } - } -} - - -static int imageSorter(const void* l, const void* r) -{ - const ImageLoader* left = *((ImageLoader**)l); - const ImageLoader* right= *((ImageLoader**)r); - return left->compare(right); -} - -static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification) -{ - std::vector* handlers = stateToHandlers(state, sBatchHandlers); - std::array kernelInfos; - uint32_t kernelInfoCount = 0; - - if ( (handlers != NULL) || ((state == dyld_image_state_bound) && (sNotifyObjCMapped != NULL)) ) { - // don't use a vector because it will use malloc/free and we want notifcation to be low cost - allImagesLock(); - dyld_image_info infos[allImagesCount()+1]; - ImageLoader* images[allImagesCount()+1]; - ImageLoader** end = images; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - dyld_image_states imageState = (*it)->getState(); - if ( (imageState == state) || (orLater && (imageState > state)) ) - *end++ = *it; - } - if ( sBundleBeingLoaded != NULL ) { - dyld_image_states imageState = sBundleBeingLoaded->getState(); - if ( (imageState == state) || (orLater && (imageState > state)) ) - *end++ = sBundleBeingLoaded; - } - const char* dontLoadReason = NULL; - uint32_t imageCount = (uint32_t)(end-images); - if ( imageCount != 0 ) { - // sort bottom up - qsort(images, imageCount, sizeof(ImageLoader*), &imageSorter); - // build info array - for (unsigned int i=0; i < imageCount; ++i) { - dyld_image_info* p = &infos[i]; - ImageLoader* image = images[i]; - //dyld::log(" state=%d, name=%s\n", state, image->getPath()); - p->imageLoadAddress = image->machHeader(); - p->imageFilePath = image->getRealPath(); - p->imageFileModDate = image->lastModified(); - // get these registered with the kernel as early as possible - if ( state == dyld_image_state_dependents_mapped) - queueKernelNotification(*image, true, kernelInfos, kernelInfoCount); - // special case for add_image hook - if ( state == dyld_image_state_bound ) - notifyAddImageCallbacks(image); - } - flushKernelNotifications(true, true, kernelInfos, kernelInfoCount); - } -#if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(state, orLater, &infos[imageCount]); - // support _dyld_register_func_for_add_image() - if ( state == dyld_image_state_bound ) { - for (ImageCallback callback : sAddImageCallbacks) { - for (unsigned i=0; i < cacheCount; ++i) - (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheSlide); - } - } - imageCount += cacheCount; - } -#endif - if ( imageCount != 0 ) { - if ( !onlyObjCMappedNotification ) { - if ( onlyHandler != NULL ) { - const char* result = NULL; - if ( result == NULL ) { - result = (*onlyHandler)(state, imageCount, infos); - } - if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { - //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler); - // make copy of thrown string so that later catch clauses can free it - dontLoadReason = strdup(result); - } - } - else { - // call each handler with whole array - if ( handlers != NULL ) { - for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { - const char* result = (*it)(state, imageCount, infos); - if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { - //fprintf(stderr, " images rejected by handler=%p\n", *it); - // make copy of thrown string so that later catch clauses can free it - dontLoadReason = strdup(result); - break; - } - } - } - } - } - // tell objc about new images - if ( (onlyHandler == NULL) && ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) && (sNotifyObjCMapped != NULL) ) { - const char* paths[imageCount]; - const mach_header* mhs[imageCount]; - unsigned objcImageCount = 0; - for (int i=0; i < imageCount; ++i) { - const ImageLoader* image = findImageByMachHeader(infos[i].imageLoadAddress); - bool hasObjC = false; - if ( image != NULL ) { - hasObjC = image->notifyObjC(); - } -#if SUPPORT_ACCELERATE_TABLES - else if ( sAllCacheImagesProxy != NULL ) { - const mach_header* mh; - const char* path; - unsigned index; - if ( sAllCacheImagesProxy->addressInCache(infos[i].imageLoadAddress, &mh, &path, &index) ) { - hasObjC = (mh->flags & MH_HAS_OBJC); - } - } -#endif - if ( hasObjC ) { - paths[objcImageCount] = infos[i].imageFilePath; - mhs[objcImageCount] = infos[i].imageLoadAddress; - ++objcImageCount; - } - } - if ( objcImageCount != 0 ) { - uint64_t t0 = mach_absolute_time(); - (*sNotifyObjCMapped)(objcImageCount, paths, mhs); - uint64_t t1 = mach_absolute_time(); - ImageLoader::fgTotalObjCSetupTime += (t1-t0); - } - } - } - allImagesUnlock(); - if ( dontLoadReason != NULL ) - throw dontLoadReason; - if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) { - if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { - // mach message csdlc about loaded images - uint64_t loadTimestamp = mach_absolute_time(); - for (unsigned j=0; j < 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); - } -#if 0 - coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress); -#endif - } - } - for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { - if ( dyld::gProcessInfo->notifyPorts[slot] ) - notifyMonitoringDyld(false, slot, imageCount, infos); - } - } - } -} - - - -static void notifyBatch(dyld_image_states state, bool preflightOnly) -{ - notifyBatchPartial(state, false, NULL, preflightOnly, false); -} - -// 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 DYLD_INSERT_LIBRARIES is used, those libraries are first. -static void addRootImage(ImageLoader* image) -{ - //dyld::log("addRootImage(%p, %s)\n", image, image->getPath()); - // add to list of roots - sImageRoots.push_back(image); -} - - -static void clearAllDepths() -{ - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) - (*it)->clearDepth(); -} - -static void printAllDepths() -{ - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) - dyld::log("%03d %s\n", (*it)->getDepth(), (*it)->getShortName()); -} - - -static unsigned int imageCount() -{ - allImagesLock(); - unsigned int result = (unsigned int)sAllImages.size(); - allImagesUnlock(); - return (result); -} - - -static void setNewProgramVars(const ProgramVars& newVars) -{ - // make a copy of the pointers to program variables - gLinkContext.programVars = newVars; - - // now set each program global to their initial value - *gLinkContext.programVars.NXArgcPtr = gLinkContext.argc; - *gLinkContext.programVars.NXArgvPtr = gLinkContext.argv; - *gLinkContext.programVars.environPtr = gLinkContext.envp; - *gLinkContext.programVars.__prognamePtr = gLinkContext.progname; -} - -#if SUPPORT_OLD_CRT_INITIALIZATION -static void setRunInitialzersOldWay() -{ - gRunInitializersOldWay = true; -} -#endif - -static bool sandboxBlocked(const char* path, const char* kind) -{ -#if TARGET_IPHONE_SIMULATOR || DARLING - // sandbox calls not yet supported in simulator runtime - return false; -#else - sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT); - return ( sandbox_check(getpid(), kind, filter, path) > 0 ); -#endif -} - -bool sandboxBlockedMmap(const char* path) -{ - return sandboxBlocked(path, "file-map-executable"); -} - -bool sandboxBlockedOpen(const char* path) -{ - return sandboxBlocked(path, "file-read-data"); -} - -bool sandboxBlockedStat(const char* path) -{ - return sandboxBlocked(path, "file-read-metadata"); -} - - -static void addDynamicReference(ImageLoader* from, ImageLoader* to) { - // don't add dynamic reference if target is in the shared cache (since it can't be unloaded) - if ( to->inSharedCache() ) - return; - - // don't add dynamic reference if there already is a static one - if ( from->dependsOn(to) ) - return; - - // don't add if this combination already exists - OSSpinLockLock(&sDynamicReferencesLock); - for (std::vector::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) { - if ( (it->from == from) && (it->to == to) ) { - OSSpinLockUnlock(&sDynamicReferencesLock); - return; - } - } - - //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName()); - ImageLoader::DynamicReference t; - t.from = from; - t.to = to; - sDynamicReferences.push_back(t); - OSSpinLockUnlock(&sDynamicReferencesLock); -} - -static void addImage(ImageLoader* image) -{ - // add to master list - allImagesLock(); - sAllImages.push_back(image); - allImagesUnlock(); - - // update mapped ranges - uintptr_t lastSegStart = 0; - uintptr_t lastSegEnd = 0; - for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { - if ( image->segUnaccessible(i) ) - continue; - uintptr_t start = image->segActualLoadAddress(i); - uintptr_t end = image->segActualEndAddress(i); - if ( start == lastSegEnd ) { - // two segments are contiguous, just record combined segments - lastSegEnd = end; - } - else { - // non-contiguous segments, record last (if any) - if ( lastSegEnd != 0 ) - addMappedRange(image, lastSegStart, lastSegEnd); - lastSegStart = start; - lastSegEnd = end; - } - } - if ( lastSegEnd != 0 ) - addMappedRange(image, lastSegStart, lastSegEnd); - - - if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { - dyld::log("dyld: loaded: %s\n", image->getPath()); - } - -} - -// -// Helper for std::remove_if -// -class RefUsesImage { -public: - RefUsesImage(ImageLoader* image) : _image(image) {} - bool operator()(const ImageLoader::DynamicReference& ref) const { - return ( (ref.from == _image) || (ref.to == _image) ); - } -private: - ImageLoader* _image; -}; - - - -void removeImage(ImageLoader* image) -{ - // if has dtrace DOF section, tell dtrace it is going away, then remove from sImageFilesNeedingDOFUnregistration - for (std::vector::iterator it=sImageFilesNeedingDOFUnregistration.begin(); it != sImageFilesNeedingDOFUnregistration.end(); ) { - if ( it->mh == image->machHeader() ) { - unregisterDOF(it->registrationID); - sImageFilesNeedingDOFUnregistration.erase(it); - // don't increment iterator, the erase caused next element to be copied to where this iterator points - } - else { - ++it; - } - } - - // tell all registered remove image handlers about this - // do this before removing image from internal data structures so that the callback can query dyld about the image - 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::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { - (*it)(image->machHeader(), image->getSlide()); - } - sRemoveImageCallbacksInUse = false; - - if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() ) - (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader()); - } - - // notify - notifySingle(dyld_image_state_terminated, image, NULL); - - // remove from mapped images table - removedMappedRanges(image); - - // remove from master list - allImagesLock(); - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - if ( *it == image ) { - sAllImages.erase(it); - break; - } - } - allImagesUnlock(); - - // remove from sDynamicReferences - OSSpinLockLock(&sDynamicReferencesLock); - sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); - OSSpinLockUnlock(&sDynamicReferencesLock); - - // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back) - if ( sLastImageByAddressCache == image ) - sLastImageByAddressCache = NULL; - - // if in root list, pull it out - for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { - if ( *it == image ) { - sImageRoots.erase(it); - break; - } - } - - // log if requested - if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { - dyld::log("dyld: unloaded: %s\n", image->getPath()); - } - - // tell gdb, new way - removeImageFromAllImages(image->machHeader()); -} - - -void runImageStaticTerminators(ImageLoader* image) -{ - // if in termination list, pull it out and run terminator - bool mightBeMore; - do { - mightBeMore = false; - for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { - if ( *it == image ) { - sImageFilesNeedingTermination.erase(it); - if (gLogAPIs) dyld::log("dlclose(), running static terminators for %p %s\n", image, image->getShortName()); - image->doTermination(gLinkContext); - mightBeMore = true; - break; - } - } - } while ( mightBeMore ); -} - -static void terminationRecorder(ImageLoader* image) -{ - sImageFilesNeedingTermination.push_back(image); -} - -const char* getExecutablePath() -{ - return sExecPath; -} - -static void runAllStaticTerminators(void* extra) -{ - try { - const size_t imageCount = sImageFilesNeedingTermination.size(); - for(size_t i=imageCount; i > 0; --i){ - ImageLoader* image = sImageFilesNeedingTermination[i-1]; - image->doTermination(gLinkContext); - } - sImageFilesNeedingTermination.clear(); - notifyBatch(dyld_image_state_terminated, false); - } - catch (const char* msg) { - halt(msg); - } -} - -void initializeMainExecutable() -{ - // record that we've reached this step - gLinkContext.startedInitializingMainExecutable = true; - - // run initialzers for any inserted dylibs - ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; - initializerTimes[0].count = 0; - const size_t rootCount = sImageRoots.size(); - if ( rootCount > 1 ) { - for(size_t i=1; i < rootCount; ++i) { - sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); - } - } - - // run initializers for main executable and everything it brings up - sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); - - // register cxa_atexit() handler to run static terminators in all loaded images when this process exits - if ( gLibSystemHelpers != NULL ) - (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL); - - // dump info if requested - if ( sEnv.DYLD_PRINT_STATISTICS ) - ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]); - if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS ) - ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]); -} - -bool mainExecutablePrebound() -{ - return sMainExecutable->usablePrebinding(gLinkContext); -} - -ImageLoader* mainExecutable() -{ - return sMainExecutable; -} - - - - -#if SUPPORT_VERSIONED_PATHS - -// forward reference -static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName); - - -// -// Examines a dylib file and if its current_version is newer than the installed -// dylib at its install_name, then add the dylib file to sDylibOverrides. -// -static void checkDylibOverride(const char* dylibFile) -{ - //dyld::log("checkDylibOverride('%s')\n", dylibFile); - uint32_t altVersion; - char sysInstallName[PATH_MAX]; - if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) && (sysInstallName[0] =='/') ) { - //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName); - uint32_t sysVersion; - if ( getDylibVersionAndInstallname(sysInstallName, &sysVersion, NULL) ) { - //dyld::log("%s has version 0x%08X\n", sysInstallName, sysVersion); - if ( altVersion > sysVersion ) { - //dyld::log("override found: %s -> %s\n", sysInstallName, dylibFile); - // see if there already is an override for this dylib - bool entryExists = false; - for (std::vector::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { - if ( strcmp(it->installName, sysInstallName) == 0 ) { - entryExists = true; - uint32_t prevVersion; - if ( getDylibVersionAndInstallname(it->override, &prevVersion, NULL) ) { - if ( altVersion > prevVersion ) { - // found an even newer override - free((void*)(it->override)); - char resolvedPath[PATH_MAX]; - if ( realpath(dylibFile, resolvedPath) != NULL ) - it->override = strdup(resolvedPath); - else - it->override = strdup(dylibFile); - break; - } - } - } - } - if ( ! entryExists ) { - DylibOverride entry; - entry.installName = strdup(sysInstallName); - char resolvedPath[PATH_MAX]; - if ( realpath(dylibFile, resolvedPath) != NULL ) - entry.override = strdup(resolvedPath); - else - entry.override = strdup(dylibFile); - sDylibOverrides.push_back(entry); - //dyld::log("added override: %s -> %s\n", entry.installName, entry.override); - } - } - } - } - -} - -static void checkDylibOverridesInDir(const char* dirPath) -{ - //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath); - char dylibPath[PATH_MAX]; - long dirPathLen = strlcpy(dylibPath, dirPath, PATH_MAX-1); - if ( dirPathLen >= PATH_MAX ) - return; - DIR* dirp = opendir(dirPath); - if ( dirp != NULL) { - dirent entry; - dirent* entp = NULL; - while ( readdir_r(dirp, &entry, &entp) == 0 ) { - if ( entp == NULL ) - break; - if ( entp->d_type != DT_REG ) - continue; - dylibPath[dirPathLen] = '/'; - dylibPath[dirPathLen+1] = '\0'; - if ( strlcat(dylibPath, entp->d_name, PATH_MAX) >= PATH_MAX ) - continue; - checkDylibOverride(dylibPath); - } - closedir(dirp); - } -} - - -static void checkFrameworkOverridesInDir(const char* dirPath) -{ - //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath); - char frameworkPath[PATH_MAX]; - long dirPathLen = strlcpy(frameworkPath, dirPath, PATH_MAX-1); - if ( dirPathLen >= PATH_MAX ) - return; - DIR* dirp = opendir(dirPath); - if ( dirp != NULL) { - dirent entry; - dirent* entp = NULL; - while ( readdir_r(dirp, &entry, &entp) == 0 ) { - if ( entp == NULL ) - break; - if ( entp->d_type != DT_DIR ) - continue; - frameworkPath[dirPathLen] = '/'; - frameworkPath[dirPathLen+1] = '\0'; - int dirNameLen = (int)strlen(entp->d_name); - if ( dirNameLen < 11 ) - continue; - if ( strcmp(&entp->d_name[dirNameLen-10], ".framework") != 0 ) - continue; - if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) - continue; - if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX ) - continue; - if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) - continue; - frameworkPath[strlen(frameworkPath)-10] = '\0'; - checkDylibOverride(frameworkPath); - } - closedir(dirp); - } -} -#endif // SUPPORT_VERSIONED_PATHS - - -// -// Turns a colon separated list of strings into a NULL terminated array -// of string pointers. If mainExecutableDir param is not NULL, -// substitutes @loader_path with main executable's dir. -// -static const char** parseColonList(const char* list, const char* mainExecutableDir) -{ - static const char* sEmptyList[] = { NULL }; - - if ( list[0] == '\0' ) - return sEmptyList; - - int colonCount = 0; - for(const char* s=list; *s != '\0'; ++s) { - if (*s == ':') - ++colonCount; - } - - int index = 0; - const char* start = list; - char** result = new char*[colonCount+2]; - for(const char* s=list; *s != '\0'; ++s) { - if (*s == ':') { - size_t len = s-start; - if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.processIsRestricted ) { - dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n"); - continue; - } -#endif - size_t mainExecDirLen = strlen(mainExecutableDir); - char* str = new char[mainExecDirLen+len+1]; - strcpy(str, mainExecutableDir); - strlcat(str, &start[13], mainExecDirLen+len+1); - str[mainExecDirLen+len-13] = '\0'; - start = &s[1]; - result[index++] = str; - } - else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.processIsRestricted ) { - dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); - continue; - } -#endif - size_t mainExecDirLen = strlen(mainExecutableDir); - char* str = new char[mainExecDirLen+len+1]; - strcpy(str, mainExecutableDir); - strlcat(str, &start[17], mainExecDirLen+len+1); - str[mainExecDirLen+len-17] = '\0'; - start = &s[1]; - result[index++] = str; - } - else { - char* str = new char[len+1]; - strncpy(str, start, len); - str[len] = '\0'; - start = &s[1]; - result[index++] = str; - } - } - } - 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"); - } - else -#endif - { - size_t mainExecDirLen = strlen(mainExecutableDir); - char* str = new char[mainExecDirLen+len+1]; - strcpy(str, mainExecutableDir); - strlcat(str, &start[13], mainExecDirLen+len+1); - str[mainExecDirLen+len-13] = '\0'; - result[index++] = str; - } - } - else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.processIsRestricted ) { - dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); - } - else -#endif - { - size_t mainExecDirLen = strlen(mainExecutableDir); - char* str = new char[mainExecDirLen+len+1]; - strcpy(str, mainExecutableDir); - strlcat(str, &start[17], mainExecDirLen+len+1); - str[mainExecDirLen+len-17] = '\0'; - result[index++] = str; - } - } - else { - char* str = new char[len+1]; - strcpy(str, start); - result[index++] = str; - } - result[index] = NULL; - - //dyld::log("parseColonList(%s)\n", list); - //for(int i=0; result[i] != NULL; ++i) - // dyld::log(" %s\n", result[i]); - return (const char**)result; -} - -static void appendParsedColonList(const char* list, const char* mainExecutableDir, const char* const ** storage) -{ - const char** newlist = parseColonList(list, mainExecutableDir); - if ( *storage == NULL ) { - // first time, just set - *storage = newlist; - } - else { - // need to append to existing list - const char* const* existing = *storage; - int count = 0; - for(int i=0; existing[i] != NULL; ++i) - ++count; - for(int i=0; newlist[i] != NULL; ++i) - ++count; - const char** combinedList = new const char*[count+2]; - int index = 0; - for(int i=0; existing[i] != NULL; ++i) - combinedList[index++] = existing[i]; - for(int i=0; newlist[i] != NULL; ++i) - combinedList[index++] = newlist[i]; - combinedList[index] = NULL; - // leak old arrays - *storage = combinedList; - } -} - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -static void paths_expand_roots(const char **paths, const char *key, const char *val) -{ -// assert(val != NULL); -// assert(paths != NULL); - if(NULL != key) { - size_t keyLen = strlen(key); - for(int i=0; paths[i] != NULL; ++i) { - if ( strncmp(paths[i], key, keyLen) == 0 ) { - char* newPath = new char[strlen(val) + (strlen(paths[i]) - keyLen) + 1]; - strcpy(newPath, val); - strcat(newPath, &paths[i][keyLen]); - paths[i] = newPath; - } - } - } - return; -} - -static void removePathWithPrefix(const char* paths[], const char* prefix) -{ - size_t prefixLen = strlen(prefix); - int skip = 0; - int i; - for(i = 0; paths[i] != NULL; ++i) { - if ( strncmp(paths[i], prefix, prefixLen) == 0 ) - ++skip; - else - paths[i-skip] = paths[i]; - } - paths[i-skip] = NULL; -} -#endif - - -#if 0 -static void paths_dump(const char **paths) -{ -// assert(paths != NULL); - const char **strs = paths; - while(*strs != NULL) - { - dyld::log("\"%s\"\n", *strs); - strs++; - } - return; -} -#endif - -static void printOptions(const char* argv[]) -{ - uint32_t i = 0; - while ( NULL != argv[i] ) { - dyld::log("opt[%i] = \"%s\"\n", i, argv[i]); - i++; - } -} - -static void printEnvironmentVariables(const char* envp[]) -{ - while ( NULL != *envp ) { - dyld::log("%s\n", *envp); - envp++; - } -} - -void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir) -{ - if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FRAMEWORK_PATH); - } - else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_FRAMEWORK_PATH); - } - else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_LIBRARY_PATH); - } - else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH); - } -#if SUPPORT_ROOT_PATH - else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) { - if ( strcmp(value, "/") != 0 ) { - gLinkContext.rootPaths = parseColonList(value, mainExecutableDir); - for (int i=0; gLinkContext.rootPaths[i] != NULL; ++i) { - if ( gLinkContext.rootPaths[i][0] != '/' ) { - dyld::warn("DYLD_ROOT_PATH not used because it contains a non-absolute path\n"); - gLinkContext.rootPaths = NULL; - break; - } - } - } - } -#endif - else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) { - gLinkContext.imageSuffix = value; - } - else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { - sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL); -#if SUPPORT_ACCELERATE_TABLES - sDisableAcceleratorTables = true; -#endif - } - else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { - sEnv.DYLD_PRINT_OPTS = true; - } - else if ( strcmp(key, "DYLD_PRINT_ENV") == 0 ) { - sEnv.DYLD_PRINT_ENV = true; - } - else if ( strcmp(key, "DYLD_DISABLE_DOFS") == 0 ) { - sEnv.DYLD_DISABLE_DOFS = true; - } - else if ( strcmp(key, "DYLD_DISABLE_PREFETCH") == 0 ) { - gLinkContext.preFetchDisabled = true; - } - else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) { - gLinkContext.verboseLoading = true; - } - else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) { - sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true; - } - else if ( strcmp(key, "DYLD_BIND_AT_LAUNCH") == 0 ) { - sEnv.DYLD_BIND_AT_LAUNCH = true; - } - else if ( strcmp(key, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) { - gLinkContext.bindFlat = true; - } - else if ( strcmp(key, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) { - // ignore, no longer relevant but some scripts still set it - } - else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) { - } - else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) { - gLinkContext.verbosePrebinding = true; - } - else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) { - gLinkContext.verboseInit = true; - } - else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) { - gLinkContext.verboseDOF = true; - } - else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) { - sEnv.DYLD_PRINT_STATISTICS = true; -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR - // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps - sForceStderr = true; -#endif - } - else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR - // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps - sForceStderr = true; -#endif - } - else if ( strcmp(key, "DYLD_PRINT_STATISTICS_DETAILS") == 0 ) { - sEnv.DYLD_PRINT_STATISTICS_DETAILS = true; - } - else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) { - gLinkContext.verboseMapping = true; - } - else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) { - gLinkContext.verboseBind = true; - } - else if ( strcmp(key, "DYLD_PRINT_WEAK_BINDINGS") == 0 ) { - gLinkContext.verboseWeakBind = true; - } - else if ( strcmp(key, "DYLD_PRINT_REBASINGS") == 0 ) { - gLinkContext.verboseRebase = true; - } - else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) { - gLogAPIs = true; - } -#if SUPPORT_ACCELERATE_TABLES - else if ( strcmp(key, "DYLD_PRINT_APIS_APP") == 0 ) { - gLogAppAPIs = true; - } -#endif - else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) { - gLinkContext.verboseWarnings = true; - } - 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 ) { - if ( strcmp(value, "private") == 0 ) { - gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; - } - else if ( strcmp(value, "avoid") == 0 ) { - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - } - else if ( strcmp(value, "use") == 0 ) { - gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; - } - else if ( value[0] == '\0' ) { - gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; - } - else { - dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); - } - } -#if DYLD_SHARED_CACHE_SUPPORT - else if ( strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0 ) { - sSharedCacheDir = value; - } - else if ( strcmp(key, "DYLD_SHARED_CACHE_DONT_VALIDATE") == 0 ) { - sSharedCacheIgnoreInodeAndTimeStamp = true; - } -#endif - else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) { - if ( strcmp(value, "all") == 0 ) { - gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; - } - else if ( strcmp(value, "app") == 0 ) { - gLinkContext.prebindUsage = ImageLoader::kUseAllButAppPredbinding; - } - else if ( strcmp(value, "nonsplit") == 0 ) { - gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; - } - else if ( value[0] == '\0' ) { - gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; - } - else { - dyld::warn("unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n"); - } - } -#if SUPPORT_VERSIONED_PATHS - else if ( strcmp(key, "DYLD_VERSIONED_LIBRARY_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_LIBRARY_PATH); - #if SUPPORT_ACCELERATE_TABLES - sDisableAcceleratorTables = true; - #endif - } - else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) { - appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH); - #if SUPPORT_ACCELERATE_TABLES - sDisableAcceleratorTables = true; - #endif - } -#endif -#if !TARGET_IPHONE_SIMULATOR - else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) ) { - int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644); - if ( fd != -1 ) { - sLogfile = fd; - sLogToFile = true; - } - else { - dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno); - } - } -#endif - else { - dyld::warn("unknown environment variable: %s\n", key); - } -} - - -#if SUPPORT_LC_DYLD_ENVIRONMENT -static void checkLoadCommandEnvironmentVariables() -{ - // Support augmenting dyld environment variables in load commands - const uint32_t cmd_count = sMainExecutableMachHeader->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)sMainExecutableMachHeader)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_DYLD_ENVIRONMENT: - { - const struct dylinker_command* envcmd = (struct dylinker_command*)cmd; - const char* keyEqualsValue = (char*)envcmd + envcmd->name.offset; - char mainExecutableDir[strlen(sExecPath)+2]; - strcpy(mainExecutableDir, sExecPath); - char* lastSlash = strrchr(mainExecutableDir, '/'); - if ( lastSlash != NULL) - lastSlash[1] = '\0'; - // 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 ) { - const char* value = &equals[1]; - const size_t keyLen = equals-keyEqualsValue; - // don't let malformed load command overflow stack - if ( keyLen < 40 ) { - char key[keyLen+1]; - strncpy(key, keyEqualsValue, keyLen); - key[keyLen] = '\0'; - //dyld::log("processing: %s\n", keyEqualsValue); - //dyld::log("mainExecutableDir: %s\n", mainExecutableDir); - processDyldEnvironmentVariable(key, value, mainExecutableDir); - } - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } -} -#endif // SUPPORT_LC_DYLD_ENVIRONMENT - - -static bool hasCodeSignatureLoadCommand(const macho_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if (cmd->cmd == LC_CODE_SIGNATURE) - return true; - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return false; -} - - -#if SUPPORT_VERSIONED_PATHS -static void checkVersionedPaths() -{ - // search DYLD_VERSIONED_LIBRARY_PATH directories for dylibs and check if they are newer - if ( sEnv.DYLD_VERSIONED_LIBRARY_PATH != NULL ) { - for(const char* const* lp = sEnv.DYLD_VERSIONED_LIBRARY_PATH; *lp != NULL; ++lp) { - checkDylibOverridesInDir(*lp); - } - } - - // search DYLD_VERSIONED_FRAMEWORK_PATH directories for dylibs and check if they are newer - if ( sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != NULL ) { - for(const char* const* fp = sEnv.DYLD_VERSIONED_FRAMEWORK_PATH; *fp != NULL; ++fp) { - checkFrameworkOverridesInDir(*fp); - } - } -} -#endif - - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -// -// For security, setuid programs ignore DYLD_* environment variables. -// Additionally, the DYLD_* enviroment variables are removed -// from the environment, so that any child processes don't see them. -// -static void pruneEnvironmentVariables(const char* envp[], const char*** applep) -{ -#if SUPPORT_LC_DYLD_ENVIRONMENT - checkLoadCommandEnvironmentVariables(); -#endif - - // delete all DYLD_* and LD_LIBRARY_PATH environment variables - int removedCount = 0; - const char** d = envp; - for(const char** s = envp; *s != NULL; s++) { - if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) { - *d++ = *s; - } - else { - ++removedCount; - } - } - *d++ = NULL; - // slide apple parameters - if ( removedCount > 0 ) { - *applep = d; - do { - *d = d[removedCount]; - } while ( *d++ != NULL ); - for(int i=0; i < removedCount; ++i) - *d++ = NULL; - } - - // disable framework and library fallback paths for setuid binaries rdar://problem/4589305 - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = NULL; - sEnv.DYLD_FALLBACK_LIBRARY_PATH = NULL; - - if ( removedCount > 0 ) - strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage)); -} -#endif - -static void defaultUninitializedFallbackPaths(const char* envp[]) -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.processIsRestricted ) { - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths; - sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths; - return; - } - - // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment - const char* home = _simple_getenv(envp, "HOME");; - if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { - const char** fpaths = sFrameworkFallbackPaths; - if ( home == NULL ) - removePathWithPrefix(fpaths, "$HOME"); - else - paths_expand_roots(fpaths, "$HOME", home); - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = fpaths; - } - - // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment - if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { - const char** lpaths = sLibraryFallbackPaths; - if ( home == NULL ) - removePathWithPrefix(lpaths, "$HOME"); - else - paths_expand_roots(lpaths, "$HOME", home); - sEnv.DYLD_FALLBACK_LIBRARY_PATH = lpaths; - } -#else - if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sFrameworkFallbackPaths; - - if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) - sEnv.DYLD_FALLBACK_LIBRARY_PATH = sLibraryFallbackPaths; -#endif -} - - -static void checkEnvironmentVariables(const char* envp[]) -{ - if ( sEnvMode == envNone ) - return; - const char** p; - for(p = envp; *p != NULL; p++) { - const char* keyEqualsValue = *p; - if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) { - const char* equals = strchr(keyEqualsValue, '='); - if ( equals != NULL ) { - strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage)); - strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage)); - const char* value = &equals[1]; - const size_t keyLen = equals-keyEqualsValue; - char key[keyLen+1]; - strncpy(key, keyEqualsValue, keyLen); - key[keyLen] = '\0'; - if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) ) - continue; - processDyldEnvironmentVariable(key, value, NULL); - } - } - else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) { - const char* path = &keyEqualsValue[16]; - sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL); - } - } - -#if SUPPORT_LC_DYLD_ENVIRONMENT - checkLoadCommandEnvironmentVariables(); -#endif // SUPPORT_LC_DYLD_ENVIRONMENT - -#if SUPPORT_ROOT_PATH - // DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together - if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) { - dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n"); - gLinkContext.imageSuffix = NULL; - } -#endif -} - -#if __x86_64__ && DYLD_SHARED_CACHE_SUPPORT -static bool isGCProgram(const macho_header* mh, uintptr_t slide) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if (strcmp(seg->segname, "__DATA") == 0) { - 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 (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) { - const uint32_t* objcInfo = (uint32_t*)(sect->addr + slide); - return (objcInfo[1] & 6); // 6 = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC) - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return false; -} -#endif - -static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide) -{ -#if CPU_SUBTYPES_SUPPORTED -#if __ARM_ARCH_7K__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V7K; -#elif __ARM_ARCH_7A__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V7; -#elif __ARM_ARCH_6K__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V6; -#elif __ARM_ARCH_7F__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V7F; -#elif __ARM_ARCH_7S__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S; -#else - 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); - if ( result != KERN_SUCCESS ) - throw "host_info() failed"; - sHostCPU = info.cpu_type; - sHostCPUsubtype = info.cpu_subtype; - mach_port_deallocate(mach_task_self(), hostPort); - #if __x86_64__ - #if DYLD_SHARED_CACHE_SUPPORT - sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H); - // x86_64h: Fall back to the x86_64 slice if an app requires GC. - if ( sHaswell ) { - if ( isGCProgram(mainExecutableMH, mainExecutableSlide) ) { - // When running a GC program on a haswell machine, don't use and 'h slices - sHostCPUsubtype = CPU_SUBTYPE_X86_64_ALL; - sHaswell = false; - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - } - } - #endif - #endif -#endif -#endif -} - -static void checkSharedRegionDisable() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // if main executable has segments that overlap the shared region, - // then disable using the shared region - if ( sMainExecutable->overlapsWithAddressRange((void*)(uintptr_t)SHARED_REGION_BASE, (void*)(uintptr_t)(SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) { - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - if ( gLinkContext.verboseMapping ) - dyld::warn("disabling shared region because main executable overlaps\n"); - } -#if __i386__ - if ( gLinkContext.processIsRestricted ) { - // use private or no shared region for suid processes - gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; - } -#endif -#endif - // iPhoneOS cannot run without shared region -} - -bool validImage(const ImageLoader* possibleImage) -{ - const size_t imageCount = sAllImages.size(); - for(size_t i=0; i < imageCount; ++i) { - if ( possibleImage == sAllImages[i] ) { - return true; - } - } - return false; -} - -uint32_t getImageCount() -{ - return (uint32_t)sAllImages.size(); -} - -ImageLoader* getIndexedImage(unsigned int index) -{ - if ( index < sAllImages.size() ) - return sAllImages[index]; - return NULL; -} - -ImageLoader* findImageByMachHeader(const struct mach_header* target) -{ - return findMappedRange((uintptr_t)target); -} - - -ImageLoader* findImageContainingAddress(const void* addr) -{ - #if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - const mach_header* mh; - const char* path; - unsigned index; - if ( sAllCacheImagesProxy->addressInCache(addr, &mh, &path, &index) ) - return sAllCacheImagesProxy; - } - #endif - return findMappedRange((uintptr_t)addr); -} - - -ImageLoader* findImageContainingSymbol(const void* symbol) -{ - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* anImage = *it; - if ( anImage->containsSymbol(symbol) ) - return anImage; - } - return NULL; -} - - - -void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData) -{ - const size_t imageCount = sAllImages.size(); - for(size_t i=0; i < imageCount; ++i) { - ImageLoader* anImage = sAllImages[i]; - (*callback)(anImage, userData); - } -} - -ImageLoader* findLoadedImage(const struct stat& stat_buf) -{ - const size_t imageCount = sAllImages.size(); - for(size_t i=0; i < imageCount; ++i){ - ImageLoader* anImage = sAllImages[i]; - if ( anImage->statMatch(stat_buf) ) - return anImage; - } - return NULL; -} - -// based on ANSI-C strstr() -static const char* strrstr(const char* str, const char* sub) -{ - const size_t sublen = strlen(sub); - for(const char* p = &str[strlen(str)]; p != str; --p) { - if ( strncmp(p, sub, sublen) == 0 ) - return p; - } - return NULL; -} - - -// -// Find framework path -// -// /path/foo.framework/foo => foo.framework/foo -// /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo -// /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar -// /path/foo.framework/Libraries/bar.dylb => NULL -// /path/foo.framework/bar => NULL -// -// Returns NULL if not a framework path -// -static const char* getFrameworkPartialPath(const char* path) -{ - const char* dirDot = strrstr(path, ".framework/"); - if ( dirDot != NULL ) { - const char* dirStart = dirDot; - for ( ; dirStart >= path; --dirStart) { - if ( (*dirStart == '/') || (dirStart == path) ) { - const char* frameworkStart = &dirStart[1]; - if ( dirStart == path ) - --frameworkStart; - size_t len = dirDot - frameworkStart; - char framework[len+1]; - strncpy(framework, frameworkStart, len); - framework[len] = '\0'; - const char* leaf = strrchr(path, '/'); - if ( leaf != NULL ) { - if ( strcmp(framework, &leaf[1]) == 0 ) { - return frameworkStart; - } - 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; - } - } - } - } - } - } - return NULL; -} - - -static const char* getLibraryLeafName(const char* path) -{ - const char* start = strrchr(path, '/'); - if ( start != NULL ) - return &start[1]; - else - return path; -} - - -// only for architectures that use cpu-sub-types -#if CPU_SUBTYPES_SUPPORTED - -const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1; - - -// -// A fat file may contain multiple sub-images for the same CPU type. -// In that case, dyld picks which sub-image to use by scanning a table -// of preferred cpu-sub-types for the running cpu. -// -// There is one row in the table for each cpu-sub-type on which dyld might run. -// The first entry in a row is that cpu-sub-type. It is followed by all -// cpu-sub-types that can run on that cpu, if preferred order. Each row ends with -// a "SUBTYPE_ALL" (to denote that images written to run on any cpu-sub-type are usable), -// followed by one or more CPU_SUBTYPE_END_OF_LIST to pad out this row. -// - - -#if __arm__ -// -// ARM sub-type lists -// -const int kARM_RowCount = 8; -static const cpu_subtype_t kARM[kARM_RowCount][9] = { - - // armv7f can run: v7f, v7, v6, v5, and v4 - { CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, - - // armv7k can run: v7k - { CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_END_OF_LIST }, - - // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4 - { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, - - // armv7 can run: v7, v6, v5, and v4 - { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, - - // armv6 can run: v6, v5, and v4 - { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, - - // xscale can run: xscale, v5, and v4 - { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, - - // armv5 can run: v5 and v4 - { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, - - // armv4 can run: v4 - { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, -}; -#endif - -#if __x86_64__ -// -// x86_64 sub-type lists -// -const int kX86_64_RowCount = 2; -static const cpu_subtype_t kX86_64[kX86_64_RowCount][5] = { - - // x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64 - { CPU_SUBTYPE_X86_64_H, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, - - // x86_64 can run: x86_64(lib) and x86_64 - { CPU_SUBTYPE_X86_64_ALL, (cpu_subtype_t) CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, - -}; -#endif - - -// scan the tables above to find the cpu-sub-type-list for this machine -static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype) -{ - switch (cpu) { -#if __arm__ - case CPU_TYPE_ARM: - for (int i=0; i < kARM_RowCount ; ++i) { - if ( kARM[i][0] == subtype ) - return kARM[i]; - } - break; -#endif -#if __x86_64__ - case CPU_TYPE_X86_64: - for (int i=0; i < kX86_64_RowCount ; ++i) { - if ( kX86_64[i][0] == subtype ) - return kX86_64[i]; - } - break; -#endif - } - return NULL; -} - - - - -// scan fat table-of-contents for best most preferred subtype -static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len) -{ - const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); - for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) { - for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) { - if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) - && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) { - *offset = OSSwapBigToHostInt32(archs[fatIndex].offset); - *len = OSSwapBigToHostInt32(archs[fatIndex].size); - return true; - } - } - } - return false; -} - -// scan fat table-of-contents for exact match of cpu and cpu-sub-type -static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len) -{ - const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); - for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) - && ((cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == subtype) ) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - } - return false; -} - -// scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types -static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len) -{ - const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); - for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) { - switch (cpu) { -#if __arm__ - case CPU_TYPE_ARM: - if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM_ALL ) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - break; -#endif -#if __x86_64__ - case CPU_TYPE_X86_64: - if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - break; -#endif - } - } - } - return false; -} - -#endif // CPU_SUBTYPES_SUPPORTED - - -// -// Validate the fat_header and fat_arch array: -// -// 1) arch count would not cause array to extend past 4096 byte read buffer -// 2) no slice overlaps the fat_header and arch array -// 3) arch list does not contain duplicate cputype/cpusubtype tuples -// 4) arch list does not have two overlapping slices. -// -static bool fatValidate(const fat_header* fh) -{ - if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) - return false; - - // since only first 4096 bytes of file read, we can only handle up to 204 slices. - const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch); - if ( sliceCount > 204 ) - return false; - - // compare all slices looking for conflicts - const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); - for (uint32_t i=0; i < sliceCount; ++i) { - uint32_t i_offset = OSSwapBigToHostInt32(archs[i].offset); - uint32_t i_size = OSSwapBigToHostInt32(archs[i].size); - uint32_t i_cputype = OSSwapBigToHostInt32(archs[i].cputype); - uint32_t i_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); - uint32_t i_end = i_offset + i_size; - // slice cannot overlap with header - if ( i_offset < 4096 ) - return false; - // slice size cannot overflow - if ( i_end < i_offset ) - return false; - for (uint32_t j=i+1; j < sliceCount; ++j) { - uint32_t j_offset = OSSwapBigToHostInt32(archs[j].offset); - uint32_t j_size = OSSwapBigToHostInt32(archs[j].size); - uint32_t j_cputype = OSSwapBigToHostInt32(archs[j].cputype); - uint32_t j_cpusubtype = OSSwapBigToHostInt32(archs[j].cpusubtype); - uint32_t j_end = j_offset + j_size; - // duplicate slices types not allowed - if ( (i_cputype == j_cputype) && (i_cpusubtype == j_cpusubtype) ) - return false; - // slice size cannot overflow - if ( j_end < j_offset ) - return false; - // check for overlap of slices - if ( i_offset <= j_offset ) { - if ( j_offset < i_end ) - return false; // j overlaps end of i - } - else { - // j overlaps end of i - if ( i_offset < j_end ) - return false; // i overlaps end of j - } - } - } - return true; -} - -// -// A fat file may contain multiple sub-images for the same cpu-type, -// each optimized for a different cpu-sub-type (e.g G3 or G5). -// This routine picks the optimal sub-image. -// -static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) -{ - if ( !fatValidate(fh) ) - return false; - -#if CPU_SUBTYPES_SUPPORTED - // assume all dylibs loaded must have same cpu type as main executable - const cpu_type_t cpu = sMainExecutableMachHeader->cputype; - - // We only know the subtype to use if the main executable cpu type matches the host - if ( (cpu & CPU_TYPE_MASK) == sHostCPU ) { - // get preference ordered list of subtypes - const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype); - - // use ordered list to find best sub-image in fat file - if ( subTypePreferenceList != NULL ) { - if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) ) - return true; - } - - // if running cpu is not in list, try for an exact match - if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) ) - return true; - } - - // running on an uknown cpu, can only load generic code - return fatFindRunsOnAllCPUs(cpu, fh, offset, len); -#else - // just find first slice with matching architecture - const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); - for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == sMainExecutableMachHeader->cputype) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - } - return false; -#endif -} - - - -// -// This is used to validate if a non-fat (aka thin or raw) mach-o file can be used -// on the current processor. // -bool isCompatibleMachO(const uint8_t* firstPage, const char* path) -{ -#if CPU_SUBTYPES_SUPPORTED - // It is deemed compatible if any of the following are true: - // 1) mach_header subtype is in list of compatible subtypes for running processor - // 2) mach_header subtype is same as running processor subtype - // 3) mach_header subtype runs on all processor variants - const mach_header* mh = (mach_header*)firstPage; - if ( mh->magic == sMainExecutableMachHeader->magic ) { - if ( mh->cputype == sMainExecutableMachHeader->cputype ) { - if ( (mh->cputype & CPU_TYPE_MASK) == sHostCPU ) { - // get preference ordered list of subtypes that this machine can use - const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype); - if ( subTypePreferenceList != NULL ) { - // if image's subtype is in the list, it is compatible - for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) { - if ( *p == mh->cpusubtype ) - return true; - } - // have list and not in list, so not compatible - throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path); - } - // unknown cpu sub-type, but if exact match for current subtype then ok to use - if ( mh->cpusubtype == sHostCPUsubtype ) - return true; - } - - // cpu type has no ordered list of subtypes - switch (mh->cputype) { - case CPU_TYPE_I386: - case CPU_TYPE_X86_64: - // subtypes are not used or these architectures - return true; - } - } - } -#else - // For architectures that don't support cpu-sub-types - // this just check the cpu type. - const mach_header* mh = (mach_header*)firstPage; - if ( mh->magic == sMainExecutableMachHeader->magic ) { - if ( mh->cputype == sMainExecutableMachHeader->cputype ) { - return true; - } - } -#endif - return false; -} - - - - -// The kernel maps in main executable before dyld gets control. We need to -// make an ImageLoader* for the already mapped in main executable. -static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) -{ - // try mach-o loader - if ( isCompatibleMachO((const uint8_t*)mh, path) ) { - ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); - addImage(image); - return (ImageLoaderMachO*)image; - } - - throw "main executable not a known format"; -} - -#if DYLD_SHARED_CACHE_SUPPORT - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -static bool dylibsCanOverrideCache() -{ - uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM); - if ( (devFlags & 1) == 0 ) - return false; - return ( (sSharedCache != NULL) && (sSharedCache->cacheType == kDyldSharedCacheTypeDevelopment) ); -} -#endif - -static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) -{ - if ( sSharedCache != NULL ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // Mac OS X always requires inode/mtime to valid cache - // if stat() not done yet, do it now - struct stat statb; - if ( stat_buf == NULL ) { - if ( my_stat(path, &statb) == -1 ) - return false; - stat_buf = &statb; - } -#endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED - uint64_t hash = 0; - for (const char* s=path; *s != '\0'; ++s) - hash += hash*4 + *s; -#endif - - // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired - const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset); - const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount]; -#if __IPHONE_OS_VERSION_MIN_REQUIRED - const bool cacheHasHashInfo = (start->modTime == 0); -#endif - for( const dyld_cache_image_info* p = start; p != end; ++p) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // just check path - const char* aPath = (char*)sSharedCache + p->pathFileOffset; - if ( cacheHasHashInfo && (p->inode != hash) ) - continue; - if ( strcmp(path, aPath) == 0 ) { - // found image in cache - *mh = (macho_header*)(p->address+sSharedCacheSlide); - *pathInCache = aPath; - *slide = sSharedCacheSlide; - if ( aPath < (char*)(*mh) ) { - // found alias, rescan list to get canonical name - for (const dyld_cache_image_info* p2 = start; p2 != end; ++p2) { - if ( p2->address == p->address ) { - *pathInCache = (char*)sSharedCache + p2->pathFileOffset; - break; - } - } - } - return true; - } -#elif __MAC_OS_X_VERSION_MIN_REQUIRED - // check mtime and inode first because it is fast - bool inodeMatch = ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ); - if ( searchByPath || sSharedCacheIgnoreInodeAndTimeStamp || inodeMatch ) { - // mod-time and inode match an image in the shared cache, now check path - const char* aPath = (char*)sSharedCache + p->pathFileOffset; - bool cacheHit = (strcmp(path, aPath) == 0); - if ( inodeMatch && !cacheHit ) { - // path does not match install name of dylib in cache, but inode and mtime does match - // perhaps path is a symlink to the cached dylib - struct stat pathInCacheStatBuf; - if ( my_stat(aPath, &pathInCacheStatBuf) != -1 ) - cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf->st_dev) && (pathInCacheStatBuf.st_ino == stat_buf->st_ino) ); - } - if ( cacheHit ) { - // found image in cache, return info - *mh = (macho_header*)(p->address+sSharedCacheSlide); - //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%s\n", - // *mh, p->address, sSharedCacheSlide, aPath); - *pathInCache = aPath; - *slide = sSharedCacheSlide; - return true; - } - } -#endif - } - } - return false; -} - -bool inSharedCache(const char* path) -{ - const macho_header* mhInCache; - const char* pathInCache; - long slide; - return findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slide); -} - -#endif - -static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) -{ - // now sanity check that this loaded image does not have the same install path as any existing image - const char* loadedImageInstallPath = image->getInstallPath(); - if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* anImage = *it; - const char* installPath = anImage->getInstallPath(); - if ( installPath != NULL) { - if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { - //dyld::log("duplicate(%s) => %p\n", installPath, anImage); - removeImage(image); - ImageLoader::deleteImage(image); - return anImage; - } - } - } - } - - // some API's restrict what they can load - if ( context.mustBeBundle && !image->isBundle() ) - throw "not a bundle"; - if ( context.mustBeDylib && !image->isDylib() ) - throw "not a dylib"; - - // regular main executables cannot be loaded - if ( image->isExecutable() ) { - if ( !context.canBePIE || !image->isPositionIndependentExecutable() ) - throw "can't load a main executable"; - } - - // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list - if ( ! image->isBundle() ) - addImage(image); - - return image; -} - -#if TARGET_IPHONE_SIMULATOR -static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) -{ - const macho_header* mh = (macho_header*)firstPages; - const uint32_t cmd_count = mh->ncmds; - const load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const load_command* const cmdsEnd = (load_command*)((char*)cmds + mh->sizeofcmds); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - #if TARGET_OS_WATCH - case LC_VERSION_MIN_WATCHOS: - return true; - #elif TARGET_OS_TV - case LC_VERSION_MIN_TVOS: - return true; - #elif TARGET_OS_IOS - case LC_VERSION_MIN_IPHONEOS: - return true; - #endif - case LC_VERSION_MIN_MACOSX: - // grandfather in a few libSystem dylibs - if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) || - (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) || - (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0)) - return true; - return false; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - if ( cmd > cmdsEnd ) - return false; - } - return false; -} -#endif - -// map in file and instantiate an ImageLoader -static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context) -{ - //dyld::log("%s(%s)\n", __func__ , path); - uint64_t fileOffset = 0; - uint64_t fileLength = stat_buf.st_size; - - // validate it is a file (not directory) - if ( (stat_buf.st_mode & S_IFMT) != S_IFREG ) - throw "not a file"; - - uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE]; - bool shortPage = false; - - // min mach-o file is 4K - if ( fileLength < 4096 ) { - if ( pread(fd, firstPages, fileLength, 0) != (ssize_t)fileLength ) - throwf("pread of short file failed: %d", errno); - shortPage = true; - } - else { - // optimistically read only first 4KB - if ( pread(fd, firstPages, 4096, 0) != 4096 ) - throwf("pread of first 4K failed: %d", errno); - } - - // if fat wrapper, find usable sub-file - const fat_header* fileStartAsFat = (fat_header*)firstPages; - if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) - throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch)); - if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { - if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) - throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); - if (pread(fd, firstPages, 4096, fileOffset) != 4096) - throwf("pread of fat file failed: %d", errno); - } - else { - throw "no matching architecture in universal wrapper"; - } - } - - // 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 - const mach_header* mh = (mach_header*)firstPages; - switch ( mh->filetype ) { - case MH_EXECUTE: - case MH_DYLIB: - case MH_BUNDLE: - break; - default: - throw "mach-o, but wrong filetype"; - } - - uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds; - if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) - throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); - - if ( headerAndLoadCommandsSize > fileLength ) - dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength); - - if ( headerAndLoadCommandsSize > 4096 ) { - // read more pages - unsigned readAmount = headerAndLoadCommandsSize - 4096; - if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount ) - throwf("pread of extra load commands past 4KB failed: %d", errno); - } - -#if TARGET_IPHONE_SIMULATOR - // dyld_sim should restrict loading osx binaries - if ( !isSimulatorBinary(firstPages, path) ) { - #if TARGET_OS_WATCH - throw "mach-o, but not built for watchOS simulator"; - #elif TARGET_OS_TV - throw "mach-o, but not built for tvOS simulator"; - #else - throw "mach-o, but not built for iOS simulator"; - #endif - } -#endif - - // instantiate an image - ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); - - // validate - return checkandAddImage(image, context); - } - - // try other file formats here... - - - // throw error about what was found - switch (*(uint32_t*)firstPages) { - case MH_MAGIC: - case MH_CIGAM: - case MH_MAGIC_64: - case MH_CIGAM_64: - throw "mach-o, but wrong architecture"; - default: - throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", - firstPages[0], firstPages[1], firstPages[2], firstPages[3], firstPages[4], firstPages[5], firstPages[6],firstPages[7]); - } -} - - -static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, const struct stat& stat_buf, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - - // open file (automagically closed when this function exits) - FileOpener file(path); - - // just return NULL if file not found, but record any other errors - if ( file.getFileDescriptor() == -1 ) { - int err = errno; - if ( err != ENOENT ) { - const char* newMsg; - if ( (err == EPERM) && sandboxBlockedOpen(path) ) - newMsg = dyld::mkstringf("file system sandbox blocked open() of '%s'", path); - else - newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err); - exceptions->push_back(newMsg); - } - return NULL; - } - - try { - return loadPhase6(file.getFileDescriptor(), stat_buf, path, context); - } - catch (const char* msg) { - const char* newMsg = dyld::mkstringf("%s: %s", path, msg); - exceptions->push_back(newMsg); - free((void*)msg); - return NULL; - } -} - - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - ImageLoader* image = NULL; - - #if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - unsigned index; - if ( sAllCacheImagesProxy->hasDylib(path, &index) ) - return sAllCacheImagesProxy; - } - #endif - - // just return NULL if file not found, but record any other errors - struct stat stat_buf; - if ( my_stat(path, &stat_buf) == -1 ) { - int err = errno; - if ( err != ENOENT ) { - if ( (err == EPERM) && sandboxBlockedStat(path) ) - exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path)); - else - exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err)); - } - return NULL; - } - - // in case image was renamed or found via symlinks, check for inode match - image = findLoadedImage(stat_buf); - if ( image != NULL ) - return image; - - // do nothing if not already loaded and if RTLD_NOLOAD or NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED - if ( context.dontLoad ) - return NULL; - -#if DYLD_SHARED_CACHE_SUPPORT - // see if this image is in shared cache - const macho_header* mhInCache; - const char* pathInCache; - long slideInCache; - if ( findInSharedCacheImage(path, false, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) { - image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); - return checkandAddImage(image, context); - } -#endif - // file exists and is not in dyld shared cache, so open it - return loadPhase5open(path, context, stat_buf, exceptions); -} -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED - - - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -static ImageLoader* loadPhase5stat(const char* path, const LoadContext& context, struct stat* stat_buf, - int* statErrNo, bool* imageFound, std::vector* exceptions) -{ - ImageLoader* image = NULL; - *imageFound = false; - *statErrNo = 0; - if ( my_stat(path, stat_buf) == 0 ) { - // in case image was renamed or found via symlinks, check for inode match - image = findLoadedImage(*stat_buf); - if ( image != NULL ) { - *imageFound = true; - return image; - } - // do nothing if not already loaded and if RTLD_NOLOAD - if ( context.dontLoad ) { - *imageFound = true; - return NULL; - } - image = loadPhase5open(path, context, *stat_buf, exceptions); - if ( image != NULL ) { - *imageFound = true; - return image; - } - } - else { - *statErrNo = errno; - } - return NULL; -} - -// try to open file -static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - struct stat stat_buf; - bool imageFound; - int statErrNo; - ImageLoader* image; -#if DYLD_SHARED_CACHE_SUPPORT - #if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) ) - return sAllCacheImagesProxy; - } - #endif - if ( dylibsCanOverrideCache() ) { - // flag is set that allows installed framework roots to override dyld shared cache - image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); - if ( imageFound ) - return image; - } - // see if this image is in shared cache - const macho_header* mhInCache; - const char* pathInCache; - long slideInCache; - if ( findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) { - // see if this image in the cache was already loaded via a different path - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { - ImageLoader* anImage = *it; - if ( (const macho_header*)anImage->machHeader() == mhInCache ) - return anImage; - } - // do nothing if not already loaded and if RTLD_NOLOAD - if ( context.dontLoad ) - return NULL; - // nope, so instantiate a new image from dyld shared cache - // zero out stat buffer so mtime, etc are zero for items from the shared cache - bzero(&stat_buf, sizeof(stat_buf)); - image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); - return checkandAddImage(image, context); - } - - if ( !dylibsCanOverrideCache() ) { - // flag is not set, and not in cache to try opening it - image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); - if ( imageFound ) - return image; - } -#else - image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions); - if ( imageFound ) - return image; -#endif - // just return NULL if file not found, but record any other errors - if ( (statErrNo != ENOENT) && (statErrNo != 0) ) { - if ( (statErrNo == EPERM) && sandboxBlockedStat(path) ) - exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path)); - else - exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo)); - } - return NULL; -} -#endif // __IPHONE_OS_VERSION_MIN_REQUIRED - - -// look for path match with existing loaded images -static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context) -{ - //dyld::log("%s(%s, %s)\n", __func__ , path, orgPath); - // search path against load-path and install-path of all already loaded images - uint32_t hash = ImageLoader::hash(path); - //dyld::log("check() hash=%d, path=%s\n", hash, path); - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* anImage = *it; - // check hash first to cut down on strcmp calls - //dyld::log(" check() hash=%d, path=%s\n", anImage->getPathHash(), anImage->getPath()); - if ( anImage->getPathHash() == hash ) { - if ( strcmp(path, anImage->getPath()) == 0 ) { - // if we are looking for a dylib don't return something else - if ( !context.mustBeDylib || anImage->isDylib() ) - return anImage; - } - } - if ( context.matchByInstallName || anImage->matchInstallPath() ) { - const char* installPath = anImage->getInstallPath(); - if ( installPath != NULL) { - if ( strcmp(path, installPath) == 0 ) { - // if we are looking for a dylib don't return something else - if ( !context.mustBeDylib || anImage->isDylib() ) - return anImage; - } - } - } - // an install name starting with @rpath should match by install name, not just real path - if ( (orgPath[0] == '@') && (strncmp(orgPath, "@rpath/", 7) == 0) ) { - const char* installPath = anImage->getInstallPath(); - if ( installPath != NULL) { - if ( !context.mustBeDylib || anImage->isDylib() ) { - if ( strcmp(orgPath, installPath) == 0 ) - return anImage; - } - } - } - } - - //dyld::log("%s(%s) => NULL\n", __func__, path); - return NULL; -} - - -// open or check existing -static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - - // check for specific dylib overrides - for (std::vector::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { - if ( strcmp(it->installName, path) == 0 ) { - path = it->override; - break; - } - } - - if ( exceptions != NULL ) - return loadPhase5load(path, orgPath, context, cacheIndex, exceptions); - else - return loadPhase5check(path, orgPath, context); -} - -// try with and without image suffix -static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - ImageLoader* image = NULL; - if ( gLinkContext.imageSuffix != NULL ) { - char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2]; - ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix); - image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions); - } - if ( image == NULL ) - image = loadPhase5(path, orgPath, context, cacheIndex, exceptions); - return image; -} - -static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, - const char* const frameworkPaths[], const char* const libraryPaths[], - unsigned& cacheIndex, std::vector* exceptions); // forward reference - - -// expand @ variables -static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* 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 ) - throwf("unsafe use of @executable_path in %s with restricted binary", context.origin); -#endif - // handle @executable_path path prefix - const char* executablePath = sExecPath; - char newPath[strlen(executablePath) + strlen(path)]; - strcpy(newPath, executablePath); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newPath, &path[17]); - image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - - // perhaps main executable path is a sym link, find realpath and retry - char resolvedPath[PATH_MAX]; - if ( realpath(sExecPath, resolvedPath) != NULL ) { - char newRealPath[strlen(resolvedPath) + strlen(path)]; - strcpy(newRealPath, resolvedPath); - addPoint = strrchr(newRealPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[17]); - else - strcpy(newRealPath, &path[17]); - image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - } - else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305 - if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) ) - throwf("unsafe use of @loader_path in %s with restricted binary", context.origin); -#endif - // handle @loader_path path prefix - char newPath[strlen(context.origin) + strlen(path)]; - strcpy(newPath, context.origin); - char* addPoint = strrchr(newPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[13]); - else - strcpy(newPath, &path[13]); - image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - - // perhaps loader path is a sym link, find realpath and retry - char resolvedPath[PATH_MAX]; - if ( realpath(context.origin, resolvedPath) != NULL ) { - char newRealPath[strlen(resolvedPath) + strlen(path)]; - strcpy(newRealPath, resolvedPath); - addPoint = strrchr(newRealPath,'/'); - if ( addPoint != NULL ) - strcpy(&addPoint[1], &path[13]); - else - strcpy(newRealPath, &path[13]); - image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - } - else if ( context.implicitRPath || (strncmp(path, "@rpath/", 7) == 0) ) { - const char* trailingPath = (strncmp(path, "@rpath/", 7) == 0) ? &path[7] : path; - // substitute @rpath with all -rpath paths up the load chain - for(const ImageLoader::RPathChain* rp=context.rpath; rp != NULL; rp=rp->next) { - if (rp->paths != NULL ) { - for(std::vector::iterator it=rp->paths->begin(); it != rp->paths->end(); ++it) { - const char* anRPath = *it; - char newPath[strlen(anRPath) + strlen(trailingPath)+2]; - strcpy(newPath, anRPath); - if ( newPath[strlen(newPath)-1] != '/' ) - strcat(newPath, "/"); - strcat(newPath, trailingPath); - image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); - if ( gLinkContext.verboseRPaths && (exceptions != NULL) ) { - if ( image != NULL ) - dyld::log("RPATH successful expansion of %s to: %s\n", orgPath, newPath); - else - dyld::log("RPATH failed to expanding %s to: %s\n", orgPath, newPath); - } - if ( image != NULL ) - return image; - } - } - } - - // substitute @rpath with LD_LIBRARY_PATH - if ( sEnv.LD_LIBRARY_PATH != NULL ) { - image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - - // if this is the "open" pass, don't try to open @rpath/... as a relative path - if ( (exceptions != NULL) && (trailingPath != path) ) - return NULL; - } -#if __MAC_OS_X_VERSION_MIN_REQUIRED - else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) { - throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); - } -#endif - - return loadPhase4(path, orgPath, context, cacheIndex, exceptions); -} - - -// try search paths -static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, - const char* const frameworkPaths[], const char* const libraryPaths[], - unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - ImageLoader* image = NULL; - const char* frameworkPartialPath = getFrameworkPartialPath(path); - if ( frameworkPaths != NULL ) { - if ( frameworkPartialPath != NULL ) { - const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); - for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) { - char npath[strlen(*fp)+frameworkPartialPathLen+8]; - strcpy(npath, *fp); - strcat(npath, "/"); - strcat(npath, frameworkPartialPath); - //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); - image = loadPhase4(npath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - } - } - // An executable with the same name as a framework & DYLD_LIBRARY_PATH pointing to it gets loaded twice - // Some apps depend on frameworks being found via library paths - if ( (libraryPaths != NULL) && ((frameworkPartialPath == NULL) || sFrameworksFoundAsDylibs) ) { - const char* libraryLeafName = getLibraryLeafName(path); - const size_t libraryLeafNameLen = strlen(libraryLeafName); - for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) { - char libpath[strlen(*lp)+libraryLeafNameLen+8]; - strcpy(libpath, *lp); - strcat(libpath, "/"); - strcat(libpath, libraryLeafName); - //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); - image = loadPhase4(libpath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - } - return NULL; -} - -// try search overrides and fallbacks -static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - ImageLoader* image = NULL; - - // handle LD_LIBRARY_PATH environment variables that force searching - if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) { - image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex,exceptions); - if ( image != NULL ) - return image; - } - - // handle DYLD_ environment variables that force searching - if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) { - image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - - // try raw path - image = loadPhase3(path, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - - // try fallback paths during second time (will open file) - const char* const* fallbackLibraryPaths = sEnv.DYLD_FALLBACK_LIBRARY_PATH; - if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths ) - fallbackLibraryPaths = NULL; - if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) { - image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - - return NULL; -} - -// try root substitutions -static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) -{ - //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); - -#if SUPPORT_ROOT_PATH - // handle DYLD_ROOT_PATH which forces absolute paths to use a new root - if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) { - for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) { - char newPath[strlen(*rootPath) + strlen(path)+2]; - strcpy(newPath, *rootPath); - strcat(newPath, path); - ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions); - if ( image != NULL ) - return image; - } - } -#endif - - // try raw path - return loadPhase1(path, orgPath, context, cacheIndex, exceptions); -} - -#if DYLD_SHARED_CACHE_SUPPORT - static bool cacheablePath(const char* path) { - if (strncmp(path, "/usr/lib/", 9) == 0) - return true; - if (strncmp(path, "/System/Library/", 16) == 0) - return true; - return false; - } -#endif - -// -// Given all the DYLD_ environment variables, the general case for loading libraries -// is that any given path expands into a list of possible locations to load. We -// also must take care to ensure two copies of the "same" library are never loaded. -// -// The algorithm used here is that there is a separate function for each "phase" of the -// path expansion. Each phase function calls the next phase with each possible expansion -// of that phase. The result is the last phase is called with all possible paths. -// -// To catch duplicates the algorithm is run twice. The first time, the last phase checks -// the path against all loaded images. The second time, the last phase calls open() on -// the path. Either time, if an image is found, the phases all unwind without checking -// for other paths. -// -ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex) -{ - CRSetCrashLogMessage2(path); - const char* orgPath = path; - cacheIndex = UINT32_MAX; - - //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 ( realpath(path, realPath) != NULL ) - path = realPath; - } - - // try all path permutations and check against existing loaded images - - ImageLoader* image = loadPhase0(path, orgPath, context, cacheIndex, NULL); - if ( image != NULL ) { - CRSetCrashLogMessage2(NULL); - return image; - } - - // try all path permutations and try open() until first success - std::vector exceptions; - image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions); -#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT && !TARGET_IPHONE_SIMULATOR - // support symlinks on disk to a path in dyld shared cache - if ( (image == NULL) && cacheablePath(path) && !context.dontLoad ) { - char resolvedPath[PATH_MAX]; - realpath(path, resolvedPath); - int myerr = errno; - // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT - if ( (myerr == ENOENT) || (myerr == 0) ) - { - // see if this image is in shared cache - const macho_header* mhInCache; - const char* pathInCache; - long slideInCache; - if ( findInSharedCacheImage(resolvedPath, false, NULL, &mhInCache, &pathInCache, &slideInCache) ) { - struct stat stat_buf; - bzero(&stat_buf, sizeof(stat_buf)); - try { - image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); - image = checkandAddImage(image, context); - } - catch (...) { - image = NULL; - } - } - } - } -#endif - CRSetCrashLogMessage2(NULL); - if ( image != NULL ) { - // leak in dyld during dlopen when using DYLD_ variables - for (std::vector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { - free((void*)(*it)); - } -#if DYLD_SHARED_CACHE_SUPPORT - // if loaded image is not from cache, but original path is in cache - // set gSharedCacheOverridden flag to disable some ObjC optimizations - if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) { - gSharedCacheOverridden = true; - } -#endif - return image; - } - else if ( exceptions.size() == 0 ) { - if ( context.dontLoad ) { - return NULL; - } - else - throw "image not found"; - } - else { - const char* msgStart = "no suitable image found. Did find:"; - const char* delim = "\n\t"; - size_t allsizes = strlen(msgStart)+8; - for (size_t i=0; i < exceptions.size(); ++i) - allsizes += (strlen(exceptions[i]) + strlen(delim)); - char* fullMsg = new char[allsizes]; - strcpy(fullMsg, msgStart); - for (size_t i=0; i < exceptions.size(); ++i) { - strcat(fullMsg, delim); - strcat(fullMsg, exceptions[i]); - free((void*)exceptions[i]); - } - throw (const char*)fullMsg; - } -} - - - -#if DYLD_SHARED_CACHE_SUPPORT - - - -#if __i386__ - #define ARCH_NAME "i386" - #define ARCH_CACHE_MAGIC "dyld_v1 i386" -#elif __x86_64__ - #define ARCH_NAME "x86_64" - #define ARCH_CACHE_MAGIC "dyld_v1 x86_64" - #define ARCH_NAME_H "x86_64h" - #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h" -#elif __ARM_ARCH_5TEJ__ - #define ARCH_NAME "armv5" - #define ARCH_CACHE_MAGIC "dyld_v1 armv5" -#elif __ARM_ARCH_6K__ - #define ARCH_NAME "armv6" - #define ARCH_CACHE_MAGIC "dyld_v1 armv6" -#elif __ARM_ARCH_7F__ - #define ARCH_NAME "armv7f" - #define ARCH_CACHE_MAGIC "dyld_v1 armv7f" -#elif __ARM_ARCH_7K__ - #define ARCH_NAME "armv7k" - #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" -#elif __ARM_ARCH_7A__ - #define ARCH_NAME "armv7" - #define ARCH_CACHE_MAGIC "dyld_v1 armv7" -#elif __ARM_ARCH_7S__ - #define ARCH_NAME "armv7s" - #define ARCH_CACHE_MAGIC "dyld_v1 armv7s" -#elif __arm64__ - #define ARCH_NAME "arm64" - #define ARCH_CACHE_MAGIC "dyld_v1 arm64" -#endif - - -static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address) -{ - if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) - return syscall(294, start_address); - return -1; -} - - -static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo) -{ - const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask); - const uintptr_t valueMask = ~deltaMask; - const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add); - const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; - - uint32_t pageOffset = startOffset; - uint32_t delta = 1; - while ( delta != 0 ) { - uint8_t* loc = pageContent + pageOffset; - uintptr_t rawValue = *((uintptr_t*)loc); - delta = (uint32_t)((rawValue & deltaMask) >> deltaShift); - uintptr_t value = (rawValue & valueMask); - if ( value != 0 ) { - value += valueAdd; - value += slideAmount; - } - *((uintptr_t*)loc) = value; - //dyld::log(" pageOffset=0x%03X, loc=%p, org value=0x%08llX, new value=0x%08llX, delta=0x%X\n", pageOffset, loc, (uint64_t)rawValue, (uint64_t)value, delta); - pageOffset += delta; - } -} - - -static void loadAndCheckCodeSignature(int fd, uint32_t count, const shared_file_mapping_np mappings[], - off_t codeSignatureOffset, size_t codeSignatureSize, - const void *firstPages, size_t firstPagesSize) -{ - // register code signature blob for whole dyld cache - fsignatures_t siginfo; - siginfo.fs_file_start = 0; // cache always starts at beginning of file - siginfo.fs_blob_start = (void*)codeSignatureOffset; - siginfo.fs_blob_size = codeSignatureSize; - - int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); - // don't warn in chrooted case because mapping syscall is about to fail too - if ( result == -1 ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - throwf("code signature registration for shared cache failed with errno=%d\n", errno); -#else - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: code signature registration for shared cache failed with errno=%d\n", errno); -#endif - } - uint64_t codeSignedLength = siginfo.fs_file_start; - for (uint32_t i = 0; i < count; ++i) { - if ( (mappings[i].sfm_size > codeSignedLength) || (mappings[i].sfm_file_offset > (codeSignedLength - mappings[i].sfm_size)) ) - throw "dyld shared cache mapping not covered by code signature"; - } - - void *fdata = xmmap(NULL, firstPagesSize, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); - if ( fdata == MAP_FAILED ) - throwf("mmap() errno=%d validating first page of shared cache", errno); - if ( memcmp(fdata, firstPages, firstPagesSize) != 0 ) - throwf("mmap() page compare failed for shared cache"); - munmap(fdata, firstPagesSize); -} - -static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], - long slide, void* slideInfo, unsigned long slideInfoSize) -{ - if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) { - return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize); - } - - // remove the shared region sub-map - vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); - - // notify gdb or other lurkers that this process is no longer using the shared region - dyld::gProcessInfo->processDetachedFromSharedRegion = true; - - // map cache just for this process with mmap() - const shared_file_mapping_np* const start = mappings; - const shared_file_mapping_np* const end = &mappings[count]; - for (const shared_file_mapping_np* p = start; p < end; ++p ) { - void* mmapAddress = (void*)(uintptr_t)(p->sfm_address); - size_t size = p->sfm_size; - //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size); - int protection = 0; - if ( p->sfm_init_prot & VM_PROT_EXECUTE ) - protection |= PROT_EXEC; - if ( p->sfm_init_prot & VM_PROT_READ ) - protection |= PROT_READ; - if ( p->sfm_init_prot & VM_PROT_WRITE ) - protection |= PROT_WRITE; - off_t offset = p->sfm_file_offset; - if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) { - // failed to map some chunk of this shared cache file - // clear shared region - vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); - // go back to not using shared region at all - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - if ( gLinkContext.verboseMapping ) { - dyld::log("dyld: shared cached region cannot be mapped at address %p with size 0x%08lX\n", - mmapAddress, size); - } - // return failure - return -1; - } - } - - // update all __DATA pages with slide info - const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo; - if ( slideInfoHeader->version == 2 ) { - const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo; - const uint32_t page_size = slideHeader->page_size; - const uint16_t* page_starts = (uint16_t*)((long)(slideInfo) + slideHeader->page_starts_offset); - const uint16_t* page_extras = (uint16_t*)((long)(slideInfo) + slideHeader->page_extras_offset); - const uintptr_t dataPagesStart = mappings[1].sfm_address; - for (int i=0; i < slideHeader->page_starts_count; ++i) { - uint8_t* page = (uint8_t*)(long)(dataPagesStart + (page_size*i)); - uint16_t pageEntry = page_starts[i]; - //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, pageEntry); - if ( pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) - continue; - if ( pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { - uint16_t chainIndex = (pageEntry & 0x3FFF); - bool done = false; - while ( !done ) { - uint16_t info = page_extras[chainIndex]; - uint16_t pageStartOffset = (info & 0x3FFF)*4; - //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset); - rebaseChain(page, pageStartOffset, slide, slideHeader); - done = (info & DYLD_CACHE_SLIDE_PAGE_ATTR_END); - ++chainIndex; - } - } - else { - uint32_t pageOffset = pageEntry * 4; - //dyld::log(" start pageOffset=0x%03X\n", pageOffset); - rebaseChain(page, pageOffset, slide, slideHeader); - } - } - } - else if ( slide != 0 ) { - const uintptr_t dataPagesStart = mappings[1].sfm_address; - const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset); - const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset); - for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) { - const uint8_t* entry = &entries[toc[i]*slideInfoHeader->entries_size]; - const uint8_t* page = (uint8_t*)(long)(dataPagesStart + (4096*i)); - //dyld::log("page=%p toc[%d]=%d entries=%p\n", page, i, toc[i], entry); - for(int j=0; j < 128; ++j) { - uint8_t b = entry[j]; - //dyld::log(" entry[%d] = 0x%02X\n", j, b); - if ( b != 0 ) { - for(int k=0; k < 8; ++k) { - if ( b & (1< highAddress ) - highAddress = mappings[i].sfm_address + mappings[i].sfm_size; - } - } -} - -static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[]) -{ -#if __x86_64__ - // x86_64 has a two memory regions: - // 256MB at 0x00007FFF70000000 - // 1024MB at 0x00007FFF80000000 - // Some old shared caches have r/w region after rx region, so all regions slide within 1GB range - // Newer shared caches have r/w region based at 0x7FFF70000000 and r/o regions at 0x7FFF80000000, so each part has max slide - if ( (mappingsCount >= 3) && (mappings[1].sfm_init_prot == (VM_PROT_READ|VM_PROT_WRITE)) && (mappings[1].sfm_address == 0x00007FFF70000000) ) { - const uint64_t rwSize = mappings[1].sfm_size; - const uint64_t rwSlop = 0x10000000ULL - rwSize; - const uint64_t roSize = (mappings[2].sfm_address + mappings[2].sfm_size) - mappings[0].sfm_address; - const uint64_t roSlop = 0x40000000ULL - roSize; - const uint64_t space = (rwSlop < roSlop) ? rwSlop : roSlop; - - // choose new random slide - long slide = (arc4random() % space) & (-4096); - //dyld::log("rwSlop=0x%0llX, roSlop=0x%0llX\n", rwSlop, roSlop); - //dyld::log("space=0x%0llX, slide=0x%0lX\n", space, slide); - - // update mappings - for(uint32_t i=0; i < mappingsCount; ++i) { - mappings[i].sfm_address += slide; - } - - return slide; - } - // else fall through to handle old style cache -#endif - // get bounds of cache - uint64_t lowAddress; - uint64_t highAddress; - getCacheBounds(mappingsCount, mappings, lowAddress, highAddress); - - // find slop space - const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress; - - // choose new random slide -#if __arm__ - // change shared cache slide for 32-bit arm to always be 16k aligned - long slide = ((arc4random() % space) & (-16384)); -#else - long slide = dyld_page_trunc(arc4random() % space); -#endif - //dyld::log("slideSpace=0x%0llX\n", space); - //dyld::log("slide=0x%0lX\n", slide); - - // update mappings - for(uint32_t i=0; i < mappingsCount; ++i) { - mappings[i].sfm_address += slide; - } - - return slide; -} - -static void mapSharedCache() -{ - uint64_t cacheBaseAddress = 0; - // quick check if a cache is already mapped into shared region - if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { - sSharedCache = (dyld_cache_header*)cacheBaseAddress; - // if we don't understand the currently mapped shared cache, then ignore -#if __x86_64__ - const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); -#else - const char* magic = ARCH_CACHE_MAGIC; -#endif - if ( strcmp(sSharedCache->magic, magic) != 0 ) { - sSharedCache = NULL; - if ( gLinkContext.verboseMapping ) { - dyld::log("dyld: existing shared cached in memory is not compatible\n"); - return; - } - } - // check if cache file is slidable - const dyld_cache_header* header = sSharedCache; - if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) { - // solve for slide by comparing loaded address to address of first region - const uint8_t* loadedAddress = (uint8_t*)sSharedCache; - const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset); - const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address); - sSharedCacheSlide = loadedAddress - preferedLoadAddress; - dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide; - dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress; - //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress); - } - // if cache has a uuid, copy it - if ( header->mappingOffset >= 0x68 ) { - memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); - } - // verbose logging - if ( gLinkContext.verboseMapping ) { - dyld::log("dyld: re-using existing %s shared cache mapping\n", (header->cacheType == kDyldSharedCacheTypeDevelopment ? "development" : "production")); - } - if (header->mappingOffset >= 0x68) { - dyld_kernel_image_info_t kernelCacheInfo; - memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); - kernelCacheInfo.load_addr = (uint64_t)sSharedCache; - kernelCacheInfo.fsobjid.fid_objno = 0; - kernelCacheInfo.fsobjid.fid_generation = 0; - kernelCacheInfo.fsid.val[0] = 0; - kernelCacheInfo.fsid.val[0] = 0; -#if 0 - task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, false); -#endif - } - } - else { -#if __i386__ || __x86_64__ - // Safe Boot should disable dyld shared cache - // if we are in safe-boot mode and the cache was not made during this boot cycle, - // delete the cache file - uint32_t safeBootValue = 0; - size_t safeBootValueSize = sizeof(safeBootValue); - if ( (sysctlbyname("kern.safeboot", &safeBootValue, &safeBootValueSize, NULL, 0) == 0) && (safeBootValue != 0) ) { - // user booted machine in safe-boot mode - struct stat dyldCacheStatInfo; - // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path - if ( my_stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { - struct timeval bootTimeValue; - size_t bootTimeValueSize = sizeof(bootTimeValue); - if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) { - // if the cache file was created before this boot, then throw it away and let it rebuild itself - if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) { - ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - return; - } - } - } - } -#endif - // map in shared cache to shared region - int fd = openSharedCacheFile(); - if ( fd != -1 ) { - uint8_t firstPages[8192]; - if ( ::read(fd, firstPages, 8192) == 8192 ) { - dyld_cache_header* header = (dyld_cache_header*)firstPages; - #if __x86_64__ - const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); - #else - const char* magic = ARCH_CACHE_MAGIC; - #endif - if ( strcmp(header->magic, magic) == 0 ) { - const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset]; - const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount]; - #if __IPHONE_OS_VERSION_MIN_REQUIRED - if ( (header->mappingCount != 3) - || (header->mappingOffset > 256) - || (fileMappingsStart[0].fileOffset != 0) - || (fileMappingsStart[0].address != SHARED_REGION_BASE) - || ((fileMappingsStart[0].address + fileMappingsStart[0].size) > fileMappingsStart[1].address) - || ((fileMappingsStart[1].address + fileMappingsStart[1].size) > fileMappingsStart[2].address) - || ((fileMappingsStart[0].fileOffset + fileMappingsStart[0].size) != fileMappingsStart[1].fileOffset) - || ((fileMappingsStart[1].fileOffset + fileMappingsStart[1].size) != fileMappingsStart[2].fileOffset) ) - throw "dyld shared cache file is invalid"; - #endif - shared_file_mapping_np mappings[header->mappingCount]; - unsigned int mappingCount = header->mappingCount; - int readWriteMappingIndex = -1; - int readOnlyMappingIndex = -1; - // validate that the cache file has not been truncated - bool goodCache = false; - struct stat stat_buf; - if ( fstat(fd, &stat_buf) == 0 ) { - goodCache = true; - int i=0; - for (const dyld_cache_mapping_info* p = fileMappingsStart; p < fileMappingsEnd; ++p, ++i) { - mappings[i].sfm_address = p->address; - mappings[i].sfm_size = p->size; - mappings[i].sfm_file_offset = p->fileOffset; - mappings[i].sfm_max_prot = p->maxProt; - mappings[i].sfm_init_prot = p->initProt; - // rdar://problem/5694507 old update_dyld_shared_cache tool could make a cache file - // that is not page aligned, but otherwise ok. - if ( p->fileOffset+p->size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) { - dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); - goodCache = false; - } - if ( (mappings[i].sfm_init_prot & (VM_PROT_READ|VM_PROT_WRITE)) == (VM_PROT_READ|VM_PROT_WRITE) ) { - readWriteMappingIndex = i; - } - if ( mappings[i].sfm_init_prot == VM_PROT_READ ) { - readOnlyMappingIndex = i; - } - } - // if shared cache is code signed, add a mapping for the code signature - uint64_t signatureSize = header->codeSignatureSize; - // zero size in header means signature runs to end-of-file - if ( signatureSize == 0 ) - signatureSize = stat_buf.st_size - header->codeSignatureOffset; - if ( signatureSize != 0 ) { -#if __arm__ || __arm64__ - size_t alignedSignatureSize = (signatureSize+16383) & (-16384); -#else - size_t alignedSignatureSize = (signatureSize+4095) & (-4096); -#endif - // validate code signature covers entire shared cache - loadAndCheckCodeSignature(fd, mappingCount, mappings, header->codeSignatureOffset, alignedSignatureSize, firstPages, sizeof(firstPages)); - } -#if __IPHONE_OS_VERSION_MIN_REQUIRED - else { - throw "dyld shared cache file not code signed"; - } -#endif - } -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache - if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) { - bool foundLibSystem = false; - if ( my_stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { - const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset]; - const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount]; - for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) { - if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { - foundLibSystem = true; - break; - } - } - } - if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) { - dyld::log("dyld: shared cached file was built against a different libSystem.dylib, ignoring cache.\n" - "to update dyld shared cache run: 'sudo update_dyld_shared_cache' then reboot.\n"); - goodCache = false; - } - } -#endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED - { - uint64_t lowAddress; - uint64_t highAddress; - getCacheBounds(mappingCount, mappings, lowAddress, highAddress); - if ( (highAddress-lowAddress) > SHARED_REGION_SIZE ) - throw "dyld shared cache is too big to fit in shared region"; - } -#endif - - if ( goodCache && (readWriteMappingIndex == -1) ) { - dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); - goodCache = false; - } - if ( goodCache && (readOnlyMappingIndex == -1) ) { - dyld::log("dyld: shared cached file is missing read-only mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); - goodCache = false; - } - if ( goodCache ) { - long cacheSlide = 0; - void* slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset));; - uint64_t slideInfoSize = header->slideInfoSize; - // check if shared cache contains slid info - if ( slideInfoSize != 0 ) { - // don't slide shared cache if ASLR disabled (main executable didn't slide) - if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) ) { - cacheSlide = 0; - } - else { - // generate random slide amount - cacheSlide = pickCacheSlide(mappingCount, mappings); - } - - slideInfo = (void*)((uint8_t*)slideInfo + cacheSlide); - // add VM_PROT_SLIDE bit to __DATA area of cache - mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE; - mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE; - } - if ( gLinkContext.verboseMapping ) { - dyld::log("dyld: calling _shared_region_map_and_slide_np() with regions:\n"); - for (int i=0; i < mappingCount; ++i) { - dyld::log(" address=0x%08llX, size=0x%08llX, fileOffset=0x%08llX\n", mappings[i].sfm_address, mappings[i].sfm_size, mappings[i].sfm_file_offset); - } - } - - if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, cacheSlide, slideInfo, slideInfoSize) == 0) { - // successfully mapped cache into shared region - sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; - sSharedCacheSlide = cacheSlide; - dyld::gProcessInfo->sharedCacheSlide = cacheSlide; - dyld::gProcessInfo->sharedCacheBaseAddress = mappings[0].sfm_address; - //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide); - // if cache has a uuid, copy it - if ( header->mappingOffset >= 0x68 ) { - const bool privateSharedCache = gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion; - memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); - dyld_kernel_image_info_t kernelCacheInfo; - memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); - kernelCacheInfo.load_addr = (uint64_t)sSharedCache; - kernelCacheInfo.fsobjid.fid_objno = 0; - kernelCacheInfo.fsobjid.fid_generation = 0; - kernelCacheInfo.fsid.val[0] = 0; - kernelCacheInfo.fsid.val[0] = 0; - if (privateSharedCache) { - kernelCacheInfo.fsobjid = *(fsobj_id_t*)(&stat_buf.st_ino); - struct statfs statfs_buf; - if ( fstatfs(fd, &statfs_buf) == 0 ) { - kernelCacheInfo.fsid = statfs_buf.f_fsid; - } - } -#if 0 - task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, privateSharedCache); -#endif - } - } - else { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - throwf("dyld shared cache could not be mapped. errno=%d, slide=0x%08lX, slideInfo=%p, slideInfoSize=0x%08llX, mappingCount=%u, " - "address/size/off/init/max [0]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [1]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [2]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X", - errno, cacheSlide, slideInfo, slideInfoSize, mappingCount, - mappings[0].sfm_address, mappings[0].sfm_size, mappings[0].sfm_file_offset, mappings[0].sfm_init_prot, mappings[0].sfm_max_prot, - mappings[1].sfm_address, mappings[1].sfm_size, mappings[1].sfm_file_offset, mappings[1].sfm_init_prot, mappings[1].sfm_max_prot, - mappings[2].sfm_address, mappings[2].sfm_size, mappings[2].sfm_file_offset, mappings[2].sfm_init_prot, mappings[2].sfm_max_prot); -#endif - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: shared cached file could not be mapped\n"); - } - } - } - else { - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: shared cached file is invalid\n"); - } - } - else { - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: shared cached file cannot be read\n"); - } - close(fd); - } - else { - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: shared cached file cannot be opened\n"); - } - } - - // remember if dyld loaded at same address as when cache built - if ( sSharedCache != NULL ) { - gLinkContext.dyldLoadedAtSameAddressNeededBySharedCache = ((uintptr_t)(sSharedCache->dyldBaseAddress) == (uintptr_t)&_mh_dylinker_header); - } - - // tell gdb where the shared cache is - if ( sSharedCache != NULL ) { - const dyld_cache_mapping_info* const start = (dyld_cache_mapping_info*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset); - dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount; - // only room to tell gdb about first four regions - if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 ) - dyld_shared_cache_ranges.sharedRegionsCount = 4; - const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; - int index = 0; - for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) { - dyld_shared_cache_ranges.ranges[index].start = p->address+sSharedCacheSlide; - dyld_shared_cache_ranges.ranges[index].length = p->size; - if ( gLinkContext.verboseMapping ) { - dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n", - p->address+sSharedCacheSlide, p->address+sSharedCacheSlide+p->size-1, - ((p->initProt & VM_PROT_READ) ? "read " : ""), - ((p->initProt & VM_PROT_WRITE) ? "write " : ""), - ((p->initProt & VM_PROT_EXECUTE) ? "execute " : ""), p->initProt, p->maxProt); - } - #if __i386__ - // If a non-writable and executable region is found in the R/W shared region, then this is __IMPORT segments - // This is an old cache. Make writable. dyld no longer supports turn W on and off as it binds - if ( (p->initProt == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->address & 0xF0000000) == 0xA0000000) ) { - if ( p->size != 0 ) { - vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ | VM_PROT_WRITE; - vm_protect(mach_task_self(), p->address, p->size, false, prot); - if ( gLinkContext.verboseMapping ) { - dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->address, - p->address+p->size-1, - (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' ); - } - } - } - #endif - } - if ( gLinkContext.verboseMapping ) { - // list the code blob - dyld_cache_header* header = (dyld_cache_header*)sSharedCache; - uint64_t signatureSize = header->codeSignatureSize; - // zero size in header means signature runs to end-of-file - if ( signatureSize == 0 ) { - struct stat stat_buf; - // FIXME: need size of cache file actually used - if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) - signatureSize = stat_buf.st_size - header->codeSignatureOffset; - } - if ( signatureSize != 0 ) { - const dyld_cache_mapping_info* const last = &start[dyld_shared_cache_ranges.sharedRegionsCount-1]; - uint64_t codeBlobStart = last->address + last->size; - dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize); - } - } - #if SUPPORT_ACCELERATE_TABLES - if ( !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCache->mappingOffset > 0x80) && (sSharedCache->accelerateInfoAddr != 0) ) { - sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(sSharedCache, sSharedCacheSlide, gLinkContext); - } - #endif - } -} -#endif // #if DYLD_SHARED_CACHE_SUPPORT - - - -// create when NSLinkModule is called for a second time on a bundle -ImageLoader* cloneImage(ImageLoader* image) -{ - // open file (automagically closed when this function exits) - FileOpener file(image->getPath()); - - struct stat stat_buf; - if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) - throw "stat error"; - - dyld::LoadContext context; - context.useSearchPaths = false; - context.useFallbackPaths = false; - context.useLdLibraryPath = false; - context.implicitRPath = false; - context.matchByInstallName = false; - context.dontLoad = false; - context.mustBeBundle = true; - context.mustBeDylib = false; - context.canBePIE = false; - context.origin = NULL; - context.rpath = NULL; - return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); -} - - -ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName) -{ - // if fat wrapper, find usable sub-file - const fat_header* memStartAsFat = (fat_header*)mem; - uint64_t fileOffset = 0; - uint64_t fileLength = len; - if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) { - mem = &mem[fileOffset]; - len = fileLength; - } - else { - throw "no matching architecture in universal wrapper"; - } - } - - // try each loader - if ( isCompatibleMachO(mem, moduleName) ) { - ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); - // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list - if ( ! image->isBundle() ) - addImage(image); - return image; - } - - // try other file formats here... - - // throw error about what was found - switch (*(uint32_t*)mem) { - case MH_MAGIC: - case MH_CIGAM: - case MH_MAGIC_64: - case MH_CIGAM_64: - throw "mach-o, but wrong architecture"; - default: - throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", - mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]); - } -} - - -void registerAddCallback(ImageCallback func) -{ - // now add to list to get notified when any more images are added - sAddImageCallbacks.push_back(func); - - // call callback with all existing images - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) - (*func)(image->machHeader(), image->getSlide()); - } -#if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - dyld_image_info infos[allImagesCount()+1]; - unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos); - for (unsigned i=0; i < cacheCount; ++i) { - (*func)(infos[i].imageLoadAddress, sSharedCacheSlide); - } - } -#endif -} - -void registerRemoveCallback(ImageCallback func) -{ - // ignore calls to register a notification during a notification - if ( sRemoveImageCallbacksInUse ) - return; - sRemoveImageCallbacks.push_back(func); -} - -void clearErrorMessage() -{ - error_string[0] = '\0'; -} - -void setErrorMessage(const char* message) -{ - // save off error message in global buffer for CrashReporter to find - strlcpy(error_string, message, sizeof(error_string)); -} - -const char* getErrorMessage() -{ - return error_string; -} - -void halt(const char* message) -{ - dyld::log("dyld: %s\n", message); - setErrorMessage(message); - dyld::gProcessInfo->errorMessage = error_string; - if ( !gLinkContext.startedInitializingMainExecutable ) - dyld::gProcessInfo->terminationFlags = 1; - else - dyld::gProcessInfo->terminationFlags = 0; -#if 0 - char payloadBuffer[EXIT_REASON_PAYLOAD_MAX_LEN]; - dyld_abort_payload* payload = (dyld_abort_payload*)payloadBuffer; - payload->version = 1; - payload->flags = gLinkContext.startedInitializingMainExecutable ? 0 : 1; - payload->targetDylibPathOffset = 0; - payload->clientPathOffset = 0; - payload->symbolOffset = 0; - int payloadSize = sizeof(dyld_abort_payload); - - if ( dyld::gProcessInfo->errorTargetDylibPath != NULL ) { - payload->targetDylibPathOffset = payloadSize; - payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorTargetDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; - } - if ( dyld::gProcessInfo->errorClientOfDylibPath != NULL ) { - payload->clientPathOffset = payloadSize; - payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorClientOfDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; - } - if ( dyld::gProcessInfo->errorSymbol != NULL ) { - payload->symbolOffset = payloadSize; - payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorSymbol, sizeof(payloadBuffer)-payloadSize) + 1; - } - char truncMessage[EXIT_REASON_USER_DESC_MAX_LEN]; - strlcpy(truncMessage, message, EXIT_REASON_USER_DESC_MAX_LEN); - abort_with_payload(OS_REASON_DYLD, dyld::gProcessInfo->errorKind ? dyld::gProcessInfo->errorKind : DYLD_EXIT_REASON_OTHER, payloadBuffer, payloadSize, truncMessage, 0); -#endif - kill(0, SIGABRT); -} - -static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath, - const char* errorTargetDylibPath, const char* errorSymbol) -{ - dyld::gProcessInfo->errorKind = errorCode; - dyld::gProcessInfo->errorClientOfDylibPath = errorClientOfDylibPath; - dyld::gProcessInfo->errorTargetDylibPath = errorTargetDylibPath; - dyld::gProcessInfo->errorSymbol = errorSymbol; -} - - -uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) -{ - uintptr_t result = 0; - // acquire read-lock on dyld's data structures -#if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved - if ( gLibSystemHelpers != NULL ) - (*gLibSystemHelpers->lockForReading)(); -#endif - // lookup and bind lazy pointer and get target address - try { - ImageLoader* target; - #if __i386__ - // fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer) - if ( mh == NULL ) - target = dyld::findImageContainingAddress(lazyPointer); - else - target = dyld::findImageByMachHeader(mh); - #else - // note, target should always be mach-o, because only mach-o lazy handler wired up to this - target = dyld::findImageByMachHeader(mh); - #endif - if ( target == NULL ) - throwf("image not found for lazy pointer at %p", lazyPointer); - result = target->doBindLazySymbol(lazyPointer, gLinkContext); - } - catch (const char* message) { - dyld::log("dyld: lazy symbol binding failed: %s\n", message); - halt(message); - } - // release read-lock on dyld's data structures -#if 0 - if ( gLibSystemHelpers != NULL ) - (*gLibSystemHelpers->unlockForReading)(); -#endif - // return target address to glue which jumps to it with real parameters restored - return result; -} - - -uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) -{ - uintptr_t result = 0; - // get image - if ( *imageLoaderCache == NULL ) { - // save in cache - *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache); - if ( *imageLoaderCache == NULL ) { -#if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - const mach_header* mh; - const char* path; - unsigned index; - if ( sAllCacheImagesProxy->addressInCache(imageLoaderCache, &mh, &path, &index) ) { - result = sAllCacheImagesProxy->bindLazy(lazyBindingInfoOffset, gLinkContext, mh, index); - if ( result == 0 ) { - halt("dyld: lazy symbol binding failed for image in dyld shared\n"); - } - return result; - } - } -#endif - const char* message = "fast lazy binding from unknown image"; - dyld::log("dyld: %s\n", message); - halt(message); - } - } - - // bind lazy pointer and return it - try { - result = (*imageLoaderCache)->doBindFastLazySymbol((uint32_t)lazyBindingInfoOffset, gLinkContext, - (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL, - (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL); - } - catch (const char* message) { - dyld::log("dyld: lazy symbol binding failed: %s\n", message); - halt(message); - } - - // return target address to glue which jumps to it with real parameters restored - return result; -} - - - -void registerUndefinedHandler(UndefinedHandler handler) -{ - sUndefinedHandler = handler; -} - -static void undefinedHandler(const char* symboName) -{ - if ( sUndefinedHandler != NULL ) { - (*sUndefinedHandler)(symboName); - } -} - -static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) -{ - // search all images in order - const ImageLoader* firstWeakImage = NULL; - const ImageLoader::Symbol* firstWeakSym = NULL; - const size_t imageCount = sAllImages.size(); - for(size_t i=0; i < imageCount; ++i) { - ImageLoader* anImage = sAllImages[i]; - // the use of inserted libraries alters search order - // so that inserted libraries are found before the main executable - if ( sInsertedDylibCount > 0 ) { - if ( i < sInsertedDylibCount ) - anImage = sAllImages[i+1]; - else if ( i == sInsertedDylibCount ) - anImage = sAllImages[0]; - } - if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) { - *sym = anImage->findExportedSymbol(name, false, image); - if ( *sym != NULL ) { - // if weak definition found, record first one found - if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) { - if ( firstWeakImage == NULL ) { - firstWeakImage = *image; - firstWeakSym = *sym; - } - } - else { - // found non-weak, so immediately return with it - return true; - } - } - } - } - if ( firstWeakSym != NULL ) { - // found a weak definition, but no non-weak, so return first weak found - *sym = firstWeakSym; - *image = firstWeakImage; - return true; - } -#if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) ) - return true; - } -#endif - - return false; -} - -bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) -{ - return findExportedSymbol(name, false, sym, image); -} - -bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) -{ - return findExportedSymbol(name, true, sym, image); -} - - -bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image) -{ - // search all images in order - const size_t imageCount = sAllImages.size(); - for(size_t i=0; i < imageCount; ++i){ - ImageLoader* anImage = sAllImages[i]; - // only look at images whose paths contain the hint string (NULL hint string is wildcard) - if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) { - *sym = anImage->findExportedSymbol(name, false, image); - if ( *sym != NULL ) { - return true; - } - } - } - return false; -} - - -unsigned int getCoalescedImages(ImageLoader* images[], unsigned imageIndex[]) -{ - unsigned int count = 0; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - if ( image->participatesInCoalescing() ) { - images[count] = *it; - imageIndex[count] = 0; - ++count; - } - } -#if SUPPORT_ACCELERATE_TABLES - if ( sAllCacheImagesProxy != NULL ) { - sAllCacheImagesProxy->appendImagesNeedingCoalescing(images, imageIndex, count); - } -#endif - return count; -} - - -static ImageLoader::MappedRegion* getMappedRegions(ImageLoader::MappedRegion* regions) -{ - ImageLoader::MappedRegion* end = regions; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - (*it)->getMappedRegions(end); - } - return end; -} - -void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) -{ - // mark the image that the handler is in as never-unload because dyld has a reference into it - ImageLoader* handlerImage = findImageContainingAddress((void*)handler); - if ( handlerImage != NULL ) - handlerImage->setNeverUnload(); - - // add to list of handlers - std::vector* handlers = stateToHandlers(state, sSingleHandlers); - if ( handlers != NULL ) { - // need updateAllImages() to be last in dyld_image_state_mapped list - // so that if ObjC adds a handler that prevents a load, it happens before the gdb list is updated - if ( state == dyld_image_state_mapped ) - handlers->insert(handlers->begin(), handler); - else - handlers->push_back(handler); - - // call callback with all existing images - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - dyld_image_info info; - info.imageLoadAddress = image->machHeader(); - info.imageFilePath = image->getRealPath(); - info.imageFileModDate = image->lastModified(); - // should only call handler if state == image->state - if ( image->getState() == state ) - (*handler)(state, 1, &info); - // ignore returned string, too late to do anything - } - } -} - -void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) -{ - // mark the image that the handler is in as never-unload because dyld has a reference into it - ImageLoader* handlerImage = findImageContainingAddress((void*)handler); - if ( handlerImage != NULL ) - handlerImage->setNeverUnload(); - - // add to list of handlers - std::vector* handlers = stateToHandlers(state, sBatchHandlers); - if ( handlers != NULL ) { - // insert at front, so that gdb handler is always last - handlers->insert(handlers->begin(), handler); - - // call callback with all existing images - try { - notifyBatchPartial(state, true, handler, false, false); - } - catch (const char* msg) { - // ignore request to abort during registration - } - } -} - - -void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped) -{ - // record functions to call - sNotifyObjCMapped = mapped; - sNotifyObjCInit = init; - sNotifyObjCUnmapped = unmapped; - - // call 'mapped' function with all images mapped so far - try { - notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true); - } - catch (const char* msg) { - // ignore request to abort during registration - } -} - -bool sharedCacheUUID(uuid_t uuid) -{ -#if DYLD_SHARED_CACHE_SUPPORT - if ( sSharedCache == NULL ) - return false; - - memcpy(uuid, sSharedCache->uuid, 16); - return true; -#else - return false; -#endif -} - -#if SUPPORT_ACCELERATE_TABLES - -bool dlopenFromCache(const char* path, int mode, void** handle) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - bool result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, path, mode, handle); - if ( !result && (strchr(path, '/') == NULL) ) { - // POSIX says you can call dlopen() with a leaf name (e.g. dlopen("libz.dylb")) - char fallbackPath[PATH_MAX]; - strcpy(fallbackPath, "/usr/lib/"); - strlcat(fallbackPath, path, PATH_MAX); - result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, fallbackPath, mode, handle); - } - return result; -} - -bool makeCacheHandle(ImageLoader* image, unsigned cacheIndex, int mode, void** result) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - return sAllCacheImagesProxy->makeCacheHandle(gLinkContext, cacheIndex, mode, result); -} - -bool isCacheHandle(void* handle) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - return sAllCacheImagesProxy->isCacheHandle(handle, NULL, NULL); -} - -bool isPathInCache(const char* path) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - unsigned index; - return sAllCacheImagesProxy->hasDylib(path, &index); -} - -const char* getPathFromIndex(unsigned cacheIndex) -{ - if ( sAllCacheImagesProxy == NULL ) - return NULL; - return sAllCacheImagesProxy->getIndexedPath(cacheIndex); -} - -void* dlsymFromCache(void* handle, const char* symName, unsigned index) -{ - if ( sAllCacheImagesProxy == NULL ) - return NULL; - return sAllCacheImagesProxy->dlsymFromCache(gLinkContext, handle, symName, index); -} - -bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - unsigned ignore; - return sAllCacheImagesProxy->addressInCache(address, mh, path, index ? index : &ignore); -} - -bool findUnwindSections(const void* addr, dyld_unwind_sections* info) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - return sAllCacheImagesProxy->findUnwindSections(addr, info); -} - -bool dladdrFromCache(const void* address, Dl_info* info) -{ - if ( sAllCacheImagesProxy == NULL ) - return false; - return sAllCacheImagesProxy->dladdrFromCache(address, info); -} -#endif - -static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex) -{ - dyld::LoadContext context; - context.useSearchPaths = search; - context.useFallbackPaths = search; - context.useLdLibraryPath = false; - context.implicitRPath = false; - context.matchByInstallName = false; - context.dontLoad = false; - context.mustBeBundle = false; - context.mustBeDylib = true; - context.canBePIE = false; - context.origin = origin; - context.rpath = rpaths; - return load(libraryName, context, cacheIndex); -} - -static const char* basename(const char* path) -{ - const char* last = path; - for (const char* s = path; *s != '\0'; s++) { - if (*s == '/') - last = s+1; - } - return last; -} - -static void setContext(const macho_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) -{ - gLinkContext.loadLibrary = &libraryLocator; - gLinkContext.terminationRecorder = &terminationRecorder; - gLinkContext.flatExportFinder = &flatFindExportedSymbol; - gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol; - gLinkContext.getCoalescedImages = &getCoalescedImages; - gLinkContext.undefinedHandler = &undefinedHandler; - gLinkContext.getAllMappedRegions = &getMappedRegions; - gLinkContext.bindingHandler = NULL; - gLinkContext.notifySingle = ¬ifySingle; - gLinkContext.notifyBatch = ¬ifyBatch; - gLinkContext.removeImage = &removeImage; - gLinkContext.registerDOFs = ®isterDOFs; - gLinkContext.clearAllDepths = &clearAllDepths; - gLinkContext.printAllDepths = &printAllDepths; - gLinkContext.imageCount = &imageCount; - gLinkContext.setNewProgramVars = &setNewProgramVars; -#if DYLD_SHARED_CACHE_SUPPORT - gLinkContext.inSharedCache = &inSharedCache; -#endif - gLinkContext.setErrorStrings = &setErrorStrings; -#if SUPPORT_OLD_CRT_INITIALIZATION - gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; -#endif - gLinkContext.findImageContainingAddress = &findImageContainingAddress; - gLinkContext.addDynamicReference = &addDynamicReference; -#if SUPPORT_ACCELERATE_TABLES - gLinkContext.notifySingleFromCache = ¬ifySingleFromCache; - gLinkContext.getPreInitNotifyHandler= &getPreInitNotifyHandler; - gLinkContext.getBoundBatchHandler = &getBoundBatchHandler; -#endif - gLinkContext.bindingOptions = ImageLoader::kBindingNone; - gLinkContext.argc = argc; - gLinkContext.argv = argv; - gLinkContext.envp = envp; - gLinkContext.apple = apple; - gLinkContext.progname = (argv[0] != NULL) ? basename(argv[0]) : ""; - gLinkContext.programVars.mh = mainExecutableMH; - gLinkContext.programVars.NXArgcPtr = &gLinkContext.argc; - gLinkContext.programVars.NXArgvPtr = &gLinkContext.argv; - gLinkContext.programVars.environPtr = &gLinkContext.envp; - gLinkContext.programVars.__prognamePtr=&gLinkContext.progname; - gLinkContext.mainExecutable = NULL; - gLinkContext.imageSuffix = NULL; - gLinkContext.dynamicInterposeArray = NULL; - gLinkContext.dynamicInterposeCount = 0; - gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding; -#if TARGET_IPHONE_SIMULATOR - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; -#else - gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; -#endif -} - - - -// -// Look for a special segment in the mach header. -// Its presences means that the binary wants to have DYLD ignore -// DYLD_ environment variables. -// -static bool hasRestrictedSegment(const macho_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - - //dyld::log("seg name: %s\n", seg->segname); - if (strcmp(seg->segname, "__RESTRICT") == 0) { - 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 (strcmp(sect->sectname, "__restrict") == 0) - return true; - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - return false; -} - -#if __IPHONE_OS_VERSION_MIN_REQUIRED -static bool isFairPlayEncrypted(const macho_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_ENCRYPT_COMMAND ) { - const encryption_info_command* enc = (encryption_info_command*)cmd; - return (enc->cryptid != 0); - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - return false; -} -#endif - -#if SUPPORT_VERSIONED_PATHS - -static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) -{ - firstPage[0] = 0; - // open file (automagically closed when this function exits) - FileOpener file(dylibPath); - - if ( file.getFileDescriptor() == -1 ) - return false; - - if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 ) - return false; - - // if fat wrapper, find usable sub-file - const fat_header* fileStartAsFat = (fat_header*)firstPage; - if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - uint64_t fileOffset; - uint64_t fileLength; - if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { - if ( pread(file.getFileDescriptor(), firstPage, 4096, fileOffset) != 4096 ) - return false; - } - else { - return false; - } - } - - return true; -} - -// -// Peeks at a dylib file and returns its current_version and install_name. -// Returns false on error. -// -static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName) -{ - uint8_t firstPage[4096]; - const macho_header* mh = (macho_header*)firstPage; - if ( !readFirstPage(dylibPath, firstPage) ) { - #if DYLD_SHARED_CACHE_SUPPORT - // If file cannot be read, check to see if path is in shared cache - const macho_header* mhInCache; - const char* pathInCache; - long slideInCache; - if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) - return false; - mh = mhInCache; - #else - return false; - #endif - } - - // check mach-o header - if ( mh->magic != sMainExecutableMachHeader->magic ) - return false; - if ( mh->cputype != sMainExecutableMachHeader->cputype ) - return false; - - // scan load commands for LC_ID_DYLIB - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_ID_DYLIB: - { - const struct dylib_command* id = (struct dylib_command*)cmd; - *version = id->dylib.current_version; - if ( installName != NULL ) - strlcpy(installName, (char *)id + id->dylib.name.offset, PATH_MAX); - return true; - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - if ( cmd > cmdsReadEnd ) - return false; - } - - return false; -} -#endif // SUPPORT_VERSIONED_PATHS - - -#if 0 -static void printAllImages() -{ - dyld::log("printAllImages()\n"); - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - dyld_image_states imageState = image->getState(); - dyld::log(" state=%d, dlopen-count=%d, never-unload=%d, in-use=%d, name=%s\n", - imageState, image->dlopenCount(), image->neverUnload(), image->isMarkedInUse(), image->getShortName()); - } -} -#endif - -void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) -{ - // add to list of known images. This did not happen at creation time for bundles - if ( image->isBundle() && !image->isLinked() ) - addImage(image); - - // we detect root images as those not linked in yet - if ( !image->isLinked() ) - addRootImage(image); - - // process images - try { - const char* path = image->getPath(); -#if SUPPORT_ACCELERATE_TABLES - if ( image == sAllCacheImagesProxy ) - path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); -#endif - image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path); - } - catch (const char* msg) { - garbageCollectImages(); - throw; - } -} - - -void runInitializers(ImageLoader* image) -{ - // do bottom up initialization - ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; - initializerTimes[0].count = 0; - image->runInitializers(gLinkContext, initializerTimes[0]); -} - -// This function is called at the end of dlclose() when the reference count goes to zero. -// The dylib being unloaded may have brought in other dependent dylibs when it was loaded. -// Those dependent dylibs need to be unloaded, but only if they are not referenced by -// something else. We use a standard mark and sweep garbage collection. -// -// The tricky part is that when a dylib is unloaded it may have a termination function that -// can run and itself call dlclose() on yet another dylib. The problem is that this -// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose() -// which calls garbageCollectImages() will just set a flag to re-do the garbage collection -// when the current pass is done. -// -// Also note that this is done within the dyld global lock, so it is always single threaded. -// -void garbageCollectImages() -{ - static bool sDoingGC = false; - static bool sRedo = false; - - if ( sDoingGC ) { - // GC is currently being run, just set a flag to have it run again. - sRedo = true; - return; - } - - sDoingGC = true; - do { - sRedo = false; - - // mark phase: mark all images not-in-use - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - //dyld::log("gc: neverUnload=%d name=%s\n", image->neverUnload(), image->getShortName()); - image->markNotUsed(); - } - - // sweep phase: mark as in-use, images reachable from never-unload or in-use image - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - if ( (image->dlopenCount() != 0) || image->neverUnload() || (image == sMainExecutable) ) { - OSSpinLockLock(&sDynamicReferencesLock); - image->markedUsedRecursive(sDynamicReferences); - OSSpinLockUnlock(&sDynamicReferencesLock); - } - } - - // collect phase: build array of images not marked in-use - ImageLoader* deadImages[sAllImages.size()]; - unsigned deadCount = 0; - int maxRangeCount = 0; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - if ( ! image->isMarkedInUse() ) { - deadImages[deadCount++] = image; - if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName()); - maxRangeCount += image->segmentCount(); - } - } - - // 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; - } - } - try { - runImageStaticTerminators(image); - } - catch (const char* msg) { - dyld::warn("problem running terminators for image: %s\n", msg); - } - } - - // 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; - do { - mightBeMore = false; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* image = *it; - if ( ! image->isMarkedInUse() ) { - try { - if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName()); - removeImage(image); - ImageLoader::deleteImage(image); - mightBeMore = true; - break; // interator in invalidated by this removal - } - catch (const char* msg) { - dyld::warn("problem deleting image: %s\n", msg); - } - } - } - } while ( mightBeMore ); - } while (sRedo); - sDoingGC = false; - - //printAllImages(); - -} - - -static void preflight_finally(ImageLoader* image) -{ - if ( image->isBundle() ) { - removeImageFromAllImages(image->machHeader()); - ImageLoader::deleteImage(image); - } - sBundleBeingLoaded = NULL; - dyld::garbageCollectImages(); -} - - -void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) -{ - try { - if ( image->isBundle() ) - sBundleBeingLoaded = image; // hack - const char* path = image->getPath(); -#if SUPPORT_ACCELERATE_TABLES - if ( image == sAllCacheImagesProxy ) - path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); -#endif - image->link(gLinkContext, false, true, false, loaderRPaths, path); - } - catch (const char* msg) { - preflight_finally(image); - throw; - } - preflight_finally(image); -} - -static void loadInsertedDylib(const char* path) -{ - ImageLoader* image = NULL; - unsigned cacheIndex; - try { - LoadContext context; - context.useSearchPaths = false; - context.useFallbackPaths = false; - context.useLdLibraryPath = false; - context.implicitRPath = false; - context.matchByInstallName = false; - context.dontLoad = false; - context.mustBeBundle = false; - context.mustBeDylib = true; - context.canBePIE = false; - 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); - 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) -{ - uint32_t flags; -#if TARGET_IPHONE_SIMULATOR - sEnvMode = envAll; - gLinkContext.requireCodeSignature = true; -#elif __IPHONE_OS_VERSION_MIN_REQUIRED - sEnvMode = envNone; - gLinkContext.requireCodeSignature = true; - if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { - if ( flags & CS_ENFORCEMENT ) { - if ( flags & CS_GET_TASK_ALLOW ) { - // Xcode built app for Debug allowed to use DYLD_* variables - sEnvMode = envAll; - } - else { - // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app - uint32_t secureValue = 0; - size_t secureValueSize = sizeof(secureValue); - if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) { - sEnvMode = envPrintOnly; - } - } - } - else { - // Development kernel can run unsigned code - sEnvMode = envAll; - gLinkContext.requireCodeSignature = false; - } - } - if ( issetugid() ) { - sEnvMode = envNone; - } -#elif __MAC_OS_X_VERSION_MIN_REQUIRED - sEnvMode = envAll; - gLinkContext.requireCodeSignature = false; - gLinkContext.processIsRestricted = false; - gLinkContext.processUsingLibraryValidation = false; - // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted - if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { - gLinkContext.processIsRestricted = true; - } -#ifndef DARLING - if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { - // On OS X CS_RESTRICT means the program was signed with entitlements - if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) { - 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; - } - } -#endif -#endif -} - - -bool processIsRestricted() -{ -#if __MAC_OS_X_VERSION_MIN_REQUIRED - return gLinkContext.processIsRestricted; -#else - return false; -#endif -} - - -// Add dyld to uuidArray to enable symbolication of stackshots -static void addDyldImageToUUIDList() -{ - const struct macho_header* mh = (macho_header*)&__dso_handle; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_UUID: { - uuid_command* uc = (uuid_command*)cmd; - dyld_uuid_info info; - info.imageLoadAddress = (mach_header*)mh; - memcpy(info.imageUUID, uc->uuid, 16); - addNonSharedCacheImageUUID(info); - return; - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } -} - -void notifyKernelAboutDyld() -{ - const struct macho_header* mh = (macho_header*)&__dso_handle; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_UUID: { - // Add dyld to the kernel image info - uuid_command* uc = (uuid_command*)cmd; - dyld_kernel_image_info_t kernelInfo; - memcpy(kernelInfo.uuid, uc->uuid, 16); - kernelInfo.load_addr = (uint64_t)mh; - kernelInfo.fsobjid.fid_objno = 0; - kernelInfo.fsobjid.fid_generation = 0; - kernelInfo.fsid.val[0] = 0; - kernelInfo.fsid.val[1] = 0; -#if 0 - task_register_dyld_image_infos(mach_task_self(), &kernelInfo, 1); -#endif - return; - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } -} - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -typedef int (*open_proc_t)(const char*, int, int); -typedef int (*fcntl_proc_t)(int, int, void*); -typedef int (*ioctl_proc_t)(int, unsigned long, void*); -static void* getProcessInfo() { return dyld::gProcessInfo; } -static SyscallHelpers sSysCalls = { - 7, - // added in version 1 - (open_proc_t)&open, - &close, - &pread, - &write, - &mmap, - &munmap, - &madvise, - &stat, - (fcntl_proc_t)&fcntl, - (ioctl_proc_t)&ioctl, - &issetugid, - &getcwd, - &realpath, - &vm_allocate, - &vm_deallocate, - &vm_protect, - &vlog, - &vwarn, - &pthread_mutex_lock, - &pthread_mutex_unlock, - &mach_thread_self, - &mach_port_deallocate, - &task_self_trap, - &mach_timebase_info, - &OSAtomicCompareAndSwapPtrBarrier, - &OSMemoryBarrier, - &getProcessInfo, - &__error, - &mach_absolute_time, - // added in version 2 - &thread_switch, - // added in version 3 - &opendir, - &readdir_r, - &closedir, - // added in version 4 - NULL, //&coresymbolication_load_notifier, - NULL, //&coresymbolication_unload_notifier, - // Added in version 5 - &proc_regionfilename, - &getpid, - &mach_port_insert_right, - &mach_port_allocate, - &mach_msg, - // Added in version 6 - NULL, //&abort_with_payload, - // Added in version 7 - NULL, //&task_register_dyld_image_infos, - NULL, //&task_unregister_dyld_image_infos, - NULL, //&task_get_dyld_image_infos, - NULL, //&task_register_dyld_shared_cache_image_info, - NULL, //&task_register_dyld_set_dyld_state, - NULL, //&task_register_dyld_get_process_state -}; - -__attribute__((noinline)) -static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath, - int argc, const char* argv[], const char* envp[], const char* apple[], - uintptr_t* startGlue, uintptr_t* mainAddr) -{ - *startGlue = 0; - *mainAddr = 0; - - // 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 cannot be loaded in a restricted process"; - - // get file size of dyld_sim - struct stat sb; - if ( fstat(fd, &sb) == -1 ) - return "stat(dyld_sim) failed"; - - // read first page of dyld_sim file - uint8_t firstPage[4096]; - if ( pread(fd, firstPage, 4096, 0) != 4096 ) - return "pread(dyld_sim) failed"; - - // if fat file, pick matching slice - uint64_t fileOffset = 0; - uint64_t fileLength = sb.st_size; - const fat_header* fileStartAsFat = (fat_header*)firstPage; - if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { - if ( !fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) - return "no matching arch in dyld_sim"; - // re-read buffer from start of mach-o slice in fat file - if ( pread(fd, firstPage, 4096, fileOffset) != 4096 ) - return "pread(dyld_sim) failed"; - } - else if ( !isCompatibleMachO(firstPage, dyldPath) ) { - return "dyld_sim not compatible mach-o"; - } - - // calculate total size of dyld segments - const macho_header* mh = (const macho_header*)firstPage; - struct macho_segment_command* lastSeg = NULL; - struct macho_segment_command* firstSeg = NULL; - uintptr_t mappingSize = 0; - uintptr_t preferredLoadAddress = 0; - const uint32_t cmd_count = mh->ncmds; - if ( mh->sizeofcmds > 4096 ) - return "dyld_sim load commands to large"; - if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 ) - return "dyld_sim load commands to large"; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - uint32_t cmdLength = cmd->cmdsize; - if ( cmdLength < 8 ) - return "dyld_sim load command too small"; - const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); - if ( (nextCmd > endCmds) || (nextCmd < cmd) ) - return "dyld_sim load command too large"; - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( seg->vmaddr + seg->vmsize < seg->vmaddr ) - return "dyld_sim seg wraps address space"; - if ( seg->vmsize < seg->filesize ) - return "dyld_sim seg vmsize too small"; - if ( (seg->fileoff + seg->filesize) < seg->fileoff ) - return "dyld_sim seg size wraps address space"; - if ( lastSeg == NULL ) { - // first segment must be __TEXT and start at beginning of file/slice - firstSeg = seg; - if ( strcmp(seg->segname, "__TEXT") != 0 ) - return "dyld_sim first segment not __TEXT"; - if ( seg->fileoff != 0 ) - return "dyld_sim first segment not at file offset zero"; - if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) - return "dyld_sim first segment smaller than load commands"; - preferredLoadAddress = seg->vmaddr; - } - else { - // other sements must be continguous with previous segment and not executable - if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff ) - return "dyld_sim segments not contiguous"; - if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr ) - return "dyld_sim segments not address contiguous"; - if ( (seg->initprot & VM_PROT_EXECUTE) != 0 ) - return "dyld_sim non-first segment is executable"; - } - mappingSize += seg->vmsize; - lastSeg = seg; - } - break; - case LC_SEGMENT_COMMAND_WRONG: - return "dyld_sim wrong load segment load command"; - } - cmd = nextCmd; - } - // last segment must be named __LINKEDIT and not writable - if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 ) - return "dyld_sim last segment not __LINKEDIT"; - if ( lastSeg->initprot & VM_PROT_WRITE ) - return "dyld_sim __LINKEDIT segment writable"; - - // reserve space, then mmap each segment - vm_address_t loadAddress = 0; - if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 ) - return "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) { - case LC_SEGMENT_COMMAND: - { - struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - uintptr_t requestedLoadAddress = seg->vmaddr - preferredLoadAddress + loadAddress; - void* segAddress = ::mmap((void*)requestedLoadAddress, seg->filesize, seg->initprot, MAP_FIXED | MAP_PRIVATE, fd, fileOffset + seg->fileoff); - //dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress); - if ( segAddress == (void*)(-1) ) - return "dyld_sim mmap() of segment failed"; - if ( ((uintptr_t)segAddress < loadAddress) || ((uintptr_t)segAddress+seg->filesize > loadAddress+mappingSize) ) - 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 = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header)); - const uint32_t count = ((macho_header*)(loadAddress))->ncmds; - for (uint32_t i = 0; i < count; ++i) { - if (cmd->cmd == LC_UNIXTHREAD) { - #if __i386__ - const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); - // entry point must be in first segment - if ( registers->__eip < firstSeg->vmaddr ) - return "dyld_sim entry point not in __TEXT segment"; - if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) ) - return "dyld_sim entry point not in __TEXT segment"; - entry = (registers->__eip + loadAddress - preferredLoadAddress); - #elif __x86_64__ - const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); - // entry point must be in first segment - if ( registers->__rip < firstSeg->vmaddr ) - return "dyld_sim entry point not in __TEXT segment"; - if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) ) - return "dyld_sim entry point not in __TEXT segment"; - entry = (registers->__rip + loadAddress - preferredLoadAddress); - #endif - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // notify debugger that dyld_sim is loaded - dyld_image_info info; - info.imageLoadAddress = (mach_header*)loadAddress; - info.imageFilePath = strdup(dyldPath); - info.imageFileModDate = sb.st_mtime; - addImagesToAllImages(1, &info); - dyld::gProcessInfo->notification(dyld_image_adding, 1, &info); - - const char** appleParams = apple; - // jump into new simulator dyld - typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[], - const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, - const dyld::SyscallHelpers* vtable, uintptr_t* startGlue); - sim_entry_proc_t newDyld = (sim_entry_proc_t)entry; - *mainAddr = (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress, - loadAddress - preferredLoadAddress, - &sSysCalls, startGlue); - return NULL; -} -#endif - - -// -// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which -// sets up some registers and call this function. -// -// Returns address of main() in target program which __dyld_start jumps to -// -uintptr_t -_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, - int argc, const char* argv[], const char* envp[], const char* apple[], - uintptr_t* startGlue) -{ - uintptr_t result = 0; - sMainExecutableMachHeader = mainExecutableMH; -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // if this is host dyld, check to see if iOS simulator is being run - const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH"); - if ( rootPath != NULL ) { - // Add dyld to the kernel image info before we jump to the sim - notifyKernelAboutDyld(); - - // look to see if simulator has its own dyld - char simDyldPath[PATH_MAX]; - strlcpy(simDyldPath, rootPath, PATH_MAX); - strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); - int fd = my_open(simDyldPath, O_RDONLY, 0); - if ( fd != -1 ) { - const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result); - if ( errMessage != NULL ) - halt(errMessage); - return result; - } - } -#endif - - CRSetCrashLogMessage("dyld: launch started"); - - setContext(mainExecutableMH, argc, argv, envp, apple); - - // Pickup the pointer to the exec path. - sExecPath = _simple_getenv(apple, "executable_path"); - - // Remove interim apple[0] transition code from dyld - if (!sExecPath) sExecPath = apple[0]; - - if ( sExecPath[0] != '/' ) { - // have relative path, use cwd to make absolute - char cwdbuff[MAXPATHLEN]; - if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { - // maybe use static buffer to avoid calling malloc so early... - char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2]; - strcpy(s, cwdbuff); - strcat(s, "/"); - strcat(s, sExecPath); - sExecPath = s; - } - } - // Remember short name of process for later logging - sExecShortName = ::strrchr(sExecPath, '/'); - if ( sExecShortName != NULL ) - ++sExecShortName; - else - sExecShortName = sExecPath; - - configureProcessRestrictions(mainExecutableMH); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.processIsRestricted ) { - pruneEnvironmentVariables(envp, &apple); - // set again because envp and apple may have changed or moved - setContext(mainExecutableMH, argc, argv, envp, apple); - } - else -#endif - { - checkEnvironmentVariables(envp); - defaultUninitializedFallbackPaths(envp); - } - if ( sEnv.DYLD_PRINT_OPTS ) - printOptions(argv); - if ( sEnv.DYLD_PRINT_ENV ) - printEnvironmentVariables(envp); - getHostInfo(mainExecutableMH, mainExecutableSlide); - // install gdb notifier - stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB); - stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages); - // make initial allocations large enough that it is unlikely to need to be re-alloced - sImageRoots.reserve(16); - sAddImageCallbacks.reserve(4); - sRemoveImageCallbacks.reserve(4); - sImageFilesNeedingTermination.reserve(16); - sImageFilesNeedingDOFUnregistration.reserve(8); - -#if !TARGET_IPHONE_SIMULATOR -#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE - // Add gating mechanism to dyld support system order file generation process - WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag); -#endif -#endif - - - try { - // add dyld itself to UUID list - addDyldImageToUUIDList(); - notifyKernelAboutDyld(); - -#if SUPPORT_ACCELERATE_TABLES - bool mainExcutableAlreadyRebased = false; - -reloadAllImages: -#endif - - CRSetCrashLogMessage(sLoadingCrashMessage); - // instantiate ImageLoader for main executable - sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); - gLinkContext.mainExecutable = sMainExecutable; - gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); - -#if TARGET_IPHONE_SIMULATOR - // check main executable is not too new for this OS - { - if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) { - throwf("program was built for a platform that is not supported by this runtime"); - } - uint32_t mainMinOS = sMainExecutable->minOSVersion(); - - // dyld is always built for the current OS, so we can get the current OS version - // from the load command in dyld itself. - uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle); - if ( mainMinOS > dyldMinOS ) { - #if TARGET_OS_WATCH - throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d", - mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), - dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); - #elif TARGET_OS_TV - throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d", - mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), - dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); - #else - throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d", - mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), - dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); - #endif - } - } -#endif - - - #if __MAC_OS_X_VERSION_MIN_REQUIRED - // be less strict about old mach-o binaries - uint32_t mainSDK = sMainExecutable->sdkVersion(); - gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation; - #else - // simulators, iOS, tvOS, and watchOS are always strict - gLinkContext.strictMachORequired = true; - #endif - - // load shared cache - checkSharedRegionDisable(); - #if DYLD_SHARED_CACHE_SUPPORT - if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) { - mapSharedCache(); - } else { - dyld_kernel_image_info_t kernelCacheInfo; - bzero(&kernelCacheInfo.uuid[0], sizeof(uuid_t)); - kernelCacheInfo.load_addr = 0; - kernelCacheInfo.fsobjid.fid_objno = 0; - kernelCacheInfo.fsobjid.fid_generation = 0; - kernelCacheInfo.fsid.val[0] = 0; - kernelCacheInfo.fsid.val[0] = 0; -#if 0 - task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false); -#endif - } - #endif - - #if SUPPORT_ACCELERATE_TABLES - sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT); - #else - sAllImages.reserve(INITIAL_IMAGE_COUNT); - #endif - - // Now that shared cache is loaded, setup an versioned dylib overrides - #if SUPPORT_VERSIONED_PATHS - checkVersionedPaths(); - #endif - - - // dyld_all_image_infos image list does not contain dyld - // add it as dyldPath field in dyld_all_image_infos - // for simulator, dyld_sim is in image list, need host dyld added -#if TARGET_IPHONE_SIMULATOR - // get path of host dyld from table of syscall vectors in host dyld - void* addressInDyld = gSyscallHelpers; -#else - // get path of dyld itself - void* addressInDyld = (void*)&__dso_handle; -#endif - char dyldPathBuffer[MAXPATHLEN+1]; - int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN); - if ( (len != 0) && (strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0) ) { - gProcessInfo->dyldPath = strdup(dyldPathBuffer); - } - - // load any inserted libraries - if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { - for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) - loadInsertedDylib(*lib); - } - // record count of inserted libraries so that a flat search will look at - // inserted libraries, then main, then others. - sInsertedDylibCount = sAllImages.size()-1; - - // link main executable - gLinkContext.linkingMainExecutable = true; -#if SUPPORT_ACCELERATE_TABLES - if ( mainExcutableAlreadyRebased ) { - // previous link() on main executable has already adjusted its internal pointers for ASLR - // work around that by rebasing by inverse amount - sMainExecutable->rebase(gLinkContext, -mainExecutableSlide); - } -#endif - link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); - sMainExecutable->setNeverUnloadRecursive(); - if ( sMainExecutable->forceFlat() ) { - gLinkContext.bindFlat = true; - gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; - } - - // link any inserted libraries - // do this after linking main executable so that any dylibs pulled in by inserted - // dylibs (e.g. libSystem) will not be in front of dylibs the program uses - if ( sInsertedDylibCount > 0 ) { - for(unsigned int i=0; i < sInsertedDylibCount; ++i) { - ImageLoader* image = sAllImages[i+1]; - link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); - image->setNeverUnloadRecursive(); - } - // only INSERTED libraries can interpose - // 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(); - } - } - - // dyld should support interposition even without DYLD_INSERT_LIBRARIES - for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { - ImageLoader* image = sAllImages[i]; - if ( image->inSharedCache() ) - continue; - image->registerInterposing(); - } - #if SUPPORT_ACCELERATE_TABLES - if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) { - // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled - ImageLoader::clearInterposingTuples(); - // unmap all loaded dylibs (but not main executable) - for (long i=1; i < sAllImages.size(); ++i) { - ImageLoader* image = sAllImages[i]; - if ( image == sMainExecutable ) - continue; - if ( image == sAllCacheImagesProxy ) - continue; - image->setCanUnload(); - ImageLoader::deleteImage(image); - } - // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table - sAllImages.clear(); - sImageRoots.clear(); - sImageFilesNeedingTermination.clear(); - sImageFilesNeedingDOFUnregistration.clear(); - sAddImageCallbacks.clear(); - sRemoveImageCallbacks.clear(); - sDisableAcceleratorTables = true; - sAllCacheImagesProxy = NULL; - sMappedRangesStart = NULL; - mainExcutableAlreadyRebased = true; - gLinkContext.linkingMainExecutable = false; - resetAllImages(); - goto reloadAllImages; - } - #endif - - // apply interposing to initial set of images - for(int i=0; i < sImageRoots.size(); ++i) { - sImageRoots[i]->applyInterposing(gLinkContext); - } - gLinkContext.linkingMainExecutable = false; - - // do weak binding only after all inserted images linked - sMainExecutable->weakBind(gLinkContext); - - #if DYLD_SHARED_CACHE_SUPPORT - // If cache has branch island dylibs, tell debugger about them - if ( (sSharedCache != NULL) && (sSharedCache->mappingOffset >= 0x78) && (sSharedCache->branchPoolsOffset != 0) ) { - uint32_t count = sSharedCache->branchPoolsCount; - dyld_image_info info[count]; - const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCache + sSharedCache->branchPoolsOffset); - // empty branch pools can be in development cache - if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) { - for (int poolIndex=0; poolIndex < count; ++poolIndex) { - uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheSlide; - info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr; - info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands"; - info[poolIndex].imageFileModDate = 0; - } - // add to all_images list - addImagesToAllImages(count, info); - // tell gdb about new branch island images - gProcessInfo->notification(dyld_image_adding, count, info); - } - } - #endif - - CRSetCrashLogMessage("dyld: launch, running initializers"); - #if SUPPORT_OLD_CRT_INITIALIZATION - // Old way is to run initializers via a callback from crt1.o - if ( ! gRunInitializersOldWay ) - initializeMainExecutable(); - #else - // run all initializers - initializeMainExecutable(); - #endif - // find entry point for main executable - result = (uintptr_t)sMainExecutable->getThreadPC(); - if ( result != 0 ) { - // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib - if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) ) - *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; - else - halt("libdyld.dylib support not present for LC_MAIN"); - } - else { - // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main() - result = (uintptr_t)sMainExecutable->getMain(); - *startGlue = 0; - } - } - catch(const char* message) { - syncAllImages(); - halt(message); - } - catch(...) { - dyld::log("dyld: launch failed\n"); - } - - CRSetCrashLogMessage(NULL); - - return result; -} - - - -} // namespace - - - diff --git a/src/dyld2.cpp b/src/dyld2.cpp index 843d556..7a5a7fc 100644 --- a/src/dyld2.cpp +++ b/src/dyld2.cpp @@ -33,7 +33,8 @@ #include #include #include // mach_absolute_time() -#include +#include +#include #include #include #include @@ -41,13 +42,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -62,8 +63,30 @@ #include #include #include +#include -#if TARGET_OS_SIMULATOR || DARLING +#define SUPPORT_LOGGING_TO_CONSOLE !TARGET_OS_SIMULATOR +#if SUPPORT_LOGGING_TO_CONSOLE +#include // for logging to console +#endif + +#if !TARGET_OS_SIMULATOR + +// The comm page is being renamed, so set our define to the new value if the old +// value is missing +#ifndef _COMM_PAGE_DYLD_SYSTEM_FLAGS + +#ifndef _COMM_PAGE_DYLD_FLAGS +#error Must define _COMM_PAGE_DYLD_FLAGS or _COMM_PAGE_DYLD_SYSTEM_FLAGS +#endif + +#define _COMM_PAGE_DYLD_SYSTEM_FLAGS _COMM_PAGE_DYLD_FLAGS + +#endif + +#endif + +#if TARGET_OS_SIMULATOR enum { AMFI_DYLD_INPUT_PROC_IN_SIMULATOR = (1 << 0), }; @@ -74,14 +97,13 @@ 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), + AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING = (1 << 6), }; extern "C" int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags); -#ifdef DARLING - int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags) { *output_flags = 0x3F; return 0; } -#endif #else #include #endif + #include #include #if __has_feature(ptrauth_calls) @@ -124,6 +146,8 @@ extern "C" int __fork(); #include "ClosureFileSystemPhysical.h" #include "FileUtils.h" #include "BootArgs.h" +#include "Defines.h" +#include "RootsChecker.h" #ifndef MH_HAS_OBJC #define MH_HAS_OBJC 0x40000000 @@ -150,7 +174,7 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd #define macho_section section #endif - +#define DYLD_CLOSURE_XATTR_NAME "com.apple.dyld" #define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ @@ -158,6 +182,7 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd /* implemented in dyld_gdb.cpp */ extern void resetAllImages(); extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[]); extern void removeImageFromAllImages(const mach_header* mh); extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info); extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]); @@ -171,6 +196,8 @@ extern "C" { // magic linker symbol for start of dyld binary extern "C" const macho_header __dso_handle; +extern bool gEnableSharedCacheDataConst; + // // The file contains the core of dyld used to get a process to main(). @@ -256,7 +283,8 @@ static uintptr_t sMainExecutableSlide = 0; static cpu_type_t sHostCPU; static cpu_subtype_t sHostCPUsubtype; #endif -static ImageLoaderMachO* sMainExecutable = NULL; +typedef ImageLoaderMachO* __ptrauth_dyld_address_auth MainExecutablePointerType; +static MainExecutablePointerType sMainExecutable = NULL; static size_t sInsertedDylibCount = 0; static std::vector sAllImages; static std::vector sImageRoots; @@ -271,7 +299,7 @@ static void* sSingleHandlers[7][3]; static void* sBatchHandlers[7][3]; static ImageLoader* sLastImageByAddressCache; static EnvironmentVariables sEnv; -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; @@ -315,7 +343,7 @@ static _dyld_objc_notify_mapped sNotifyObjCMapped; static _dyld_objc_notify_init sNotifyObjCInit; static _dyld_objc_notify_unmapped sNotifyObjCUnmapped; -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR static bool sForceStderr = false; #endif @@ -327,10 +355,17 @@ static bool sDisableAcceleratorTables = true; #endif bool gUseDyld3 = false; +static uint32_t sLaunchModeUsed = 0; static bool sSkipMain = false; -static void (*sEntryOveride)() = nullptr; +static void (*sEntryOverride)() = nullptr; static bool sJustBuildClosure = false; +#if !TARGET_OS_SIMULATOR static bool sLogClosureFailure = false; +#endif +static bool sKeysDisabled = false; +static bool sOnlyPlatformArm64e = false; // arm64e binaries can only be loaded if they are part of the OS + +static dyld3::RootsChecker sRootsChecker; enum class ClosureMode { // Unset means we haven't provided an env variable or boot-arg to explicitly choose a mode @@ -342,13 +377,39 @@ enum class ClosureMode { // -force_dyld2=1 env variable or an internal cache on iOS Off, // PreBuiltOnly means only use a shared cache closure and don't try build a new one - PreBuiltOnly + PreBuiltOnly, +}; + +enum class ClosureKind { + unset, + full, + minimal, }; static ClosureMode sClosureMode = ClosureMode::Unset; +static ClosureKind sClosureKind = ClosureKind::unset; static bool sForceInvalidSharedCacheClosureFormat = false; static uint64_t launchTraceID = 0; +// These flags are the values in the 64-bit _COMM_PAGE_DYLD_SYSTEM_FLAGS entry +// Note we own this and can write it from PID 1 +enum CommPageFlags : uint64_t { + None = 0, + + // The boot args can set the low 32-bits of the comm page. We'll reserve the high 32-bits + // for runtime (launchd) set values. + CommPageBootArgMask = 0xFFFFFFFF, + + // Are the simulator support dylibs definitely roots when launchd scanned them + libsystemKernelIsRoot = 1ULL << 32, + libsystemPlatformIsRoot = 1ULL << 33, + libsystemPThreadIsRoot = 1ULL << 34, + + // Is the file system writable, ie, could the simulator support dylibs be written + // later, after PID 1 + fileSystemCanBeModified = 1ULL << 35 +}; + // // The MappedRanges structure is used for fast address->image lookups. // The table is only updated when the dyld lock is held, so we don't @@ -546,7 +607,7 @@ static void socket_syslogv(int priority, const char* format, va_list list) void vlog(const char* format, va_list list) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // log to console when running iOS app from Xcode if ( !sLogToFile && !sForceStderr && useSyslog() ) #else @@ -581,6 +642,21 @@ void warn(const char* format, ...) va_end(list); } +void logToConsole(const char* format, ...) { +#if SUPPORT_LOGGING_TO_CONSOLE + int cfd = open(_PATH_CONSOLE, O_WRONLY|O_NOCTTY); + if (cfd == -1) + return; + + va_list list; + va_start(list, format); + _simple_vdprintf(cfd, format, list); + va_end(list); + + close(cfd); +#endif +} + #else extern void vlog(const char* format, va_list list); #endif // !TARGET_OS_SIMULATOR @@ -618,7 +694,7 @@ private: FileOpener::FileOpener(const char* path) : fd(-1) { - fd = my_open(path, O_RDONLY, 0); + fd = dyld3::open(path, O_RDONLY, 0); } FileOpener::~FileOpener() @@ -675,7 +751,7 @@ static void registerDOFs(const std::vector& dofs) static void unregisterDOF(int registrationID) { - int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR, 0); if ( fd < 0 ) { dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n"); } @@ -793,95 +869,188 @@ static void notifySingleFromCache(dyld_image_states state, const mach_header* mh #endif #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; - } - 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; - } - // 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); +#define DYLD_PROCESS_INFO_NOTIFY_MAGIC 0x49414E46 + +struct RemoteNotificationResponder { + RemoteNotificationResponder(const RemoteNotificationResponder&) = delete; + RemoteNotificationResponder(RemoteNotificationResponder&&) = delete; + RemoteNotificationResponder() { + if (dyld::gProcessInfo->notifyPorts[0] != DYLD_PROCESS_INFO_NOTIFY_MAGIC) { + // No notifier found, early out + _namesCnt = 0; + return; + } + kern_return_t kr = task_dyld_process_info_notify_get(_names, &_namesCnt); + while (kr == KERN_NO_SPACE) { + // In the future the SPI may return the size we need, but for now we just double the count. Since we don't want to depend on the + // return value in _nameCnt we set it to have a minimm of 16, double the inline storage value + _namesCnt = std::max(16, 2*_namesCnt); + _namesSize = _namesCnt*sizeof(mach_port_t); + kr = vm_allocate(mach_task_self(), (vm_address_t*)&_names, _namesSize, VM_FLAGS_ANYWHERE); + if (kr != KERN_SUCCESS) { + // We could not allocate memory, time to error out + break; + } + kr = task_dyld_process_info_notify_get(_names, &_namesCnt); + if (kr != KERN_SUCCESS) { + // We failed, so deallocate the memory. If the failures was KERN_NO_SPACE we will loop back and try again + (void)vm_deallocate(mach_task_self(), (vm_address_t)_names, _namesSize); + _namesSize = 0; + } + } + if (kr != KERN_SUCCESS) { + // We failed, set _namesCnt to 0 so nothing else will happen + _namesCnt = 0; } } - mach_port_destruct(mach_task_self(), replyPort, 0, (mach_port_context_t)&replyPort); + ~RemoteNotificationResponder() { + if (_namesCnt) { + for (auto i = 0; i < _namesCnt; ++i) { + (void)mach_port_deallocate(mach_task_self(), _names[i]); + } + if (_namesSize != 0) { + // We are not using inline memory, we need to free it + (void)vm_deallocate(mach_task_self(), (vm_address_t)_names, _namesSize); + } + } + } + void sendMessage(mach_msg_id_t msgId, mach_msg_size_t sendSize, mach_msg_header_t* buffer) { + if (_namesCnt == 0) { return; } + // Allocate a port to listen on in this monitoring task + 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; + } + for (auto i = 0; i < _namesCnt; ++i) { + if (_names[i] == MACH_PORT_NULL) { continue; } + // Assemble a message + uint8_t replyBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE]; + mach_msg_header_t* msg = buffer; + msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE); + msg->msgh_id = msgId; + msg->msgh_local_port = replyPort; + msg->msgh_remote_port = _names[i]; + msg->msgh_reserved = 0; + msg->msgh_size = sendSize; + kr = mach_msg_overwrite(msg, MACH_SEND_MSG | MACH_RCV_MSG, msg->msgh_size, sizeof(replyBuffer), replyPort, 0, MACH_PORT_NULL, + (mach_msg_header_t*)&replyBuffer[0], 0); + if (kr != KERN_SUCCESS) { + // Send failed, we may have been psuedo recieved. destroy the message + (void)mach_msg_destroy(msg); + // Mark the port as null. It does not matter why we failed... if it is s single message we will not retry, if it + // is a fragmented message then subsequent messages will not decode correctly + _names[i] = MACH_PORT_NULL; + } + } + (void)mach_port_destruct(mach_task_self(), replyPort, 0, (mach_port_context_t)&replyPort); + } + + bool const active() const { + for (auto i = 0; i < _namesCnt; ++i) { + if (_names[i] != MACH_PORT_NULL) { + return true; + } + } + return false; + } +private: + mach_port_t _namesArray[8] = {0}; + mach_port_name_array_t _names = (mach_port_name_array_t)&_namesArray[0]; + mach_msg_type_number_t _namesCnt = 8; + vm_size_t _namesSize = 0; +}; + +//FIXME: Remove this once we drop support for iOS 11 simulators +// This is an enormous hack to keep remote introspection of older simulators working +// It works by interposing mach_msg, and redirecting message sent to a special port name. Messages to that portname will trigger a full set +// of sends to all kernel registered notifiers. In this mode mach_msg_sim_interposed() must return KERN_SUCCESS or the older dyld_sim may +// try to cleanup the notifer array. +kern_return_t mach_msg_sim_interposed( mach_msg_header_t* msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify) { + if (msg->msgh_remote_port != DYLD_PROCESS_INFO_NOTIFY_MAGIC) { + // Not the magic port, so just pass through to the real mach_msg() + return mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify); + } + + // The magic port. We know dyld_sim is trying to message observers, so lets call into our messaging code directly. + // This is kind of weird since we effectively built a buffer in dyld_sim, then pass it to mach_msg, which we interpose, unpack, and then + // pass to send_message which then sends the buffer back out vis mach_message_overwrite(), but it should work at least as well as the old + // way. + RemoteNotificationResponder responder; + responder.sendMessage(msg->msgh_id, send_size, msg); + + // We always return KERN_SUCCESS, otherwise old dyld_sims might clear the port + return KERN_SUCCESS; +} + +static void notifyMonitoringDyld(RemoteNotificationResponder& responder, bool unloading, unsigned imageCount, + const struct mach_header* loadAddresses[], const char* imagePaths[]) +{ + // Make sure there is at least enough room to hold a the largest single file entry that can exist. + static_assert((MAXPATHLEN + sizeof(dyld_process_info_image_entry) + 1 + MAX_TRAILER_SIZE) <= DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE); + + 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(struct dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align + // The reciever has a fixed buffer of DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE, whcih needs to hold both the message and a trailer. + // If the total size exceeds that we need to fragment the message. + if ( (totalSize + MAX_TRAILER_SIZE) > 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(responder, unloading, imageHalfCount, loadAddresses, imagePaths); + notifyMonitoringDyld(responder, 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) { + responder.sendMessage(DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID, totalSize, (mach_msg_header_t*)buffer); + } else { + responder.sendMessage(DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer); + } } 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); - } - } + RemoteNotificationResponder responder; + if (!responder.active()) { return; } + notifyMonitoringDyld(responder, unloading, imageCount, loadAddresses, imagePaths); } -static void notifyMonitoringDyldMain() -{ +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) 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); - } + RemoteNotificationResponder responder; + uint8_t buffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE]; + responder.sendMessage(DYLD_PROCESS_INFO_NOTIFY_MAIN_ID, sizeof(mach_msg_header_t), (mach_msg_header_t*)buffer); } #else extern void notifyMonitoringDyldMain() VIS_HIDDEN; @@ -927,7 +1096,9 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag } if ( state == dyld_image_state_mapped ) { // Save load addr + UUID for images from outside the shared cache - if ( !image->inSharedCache() ) { + // Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches + if (!image->inSharedCache() + || (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) { dyld_uuid_info info; if ( image->getUUID(info.imageUUID) ) { info.imageLoadAddress = image->machHeader(); @@ -1181,7 +1352,7 @@ static void notifyBatch(dyld_image_states state, bool preflightOnly) notifyBatchPartial(state, false, NULL, preflightOnly, false); } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX static void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) { @@ -1294,7 +1465,7 @@ static void setRunInitialzersOldWay() static bool sandboxBlocked(const char* path, const char* kind) { -#if TARGET_OS_SIMULATOR || DARLING +#if TARGET_OS_SIMULATOR // sandbox calls not yet supported in simulator runtime return false; #else @@ -1474,12 +1645,13 @@ void removeImage(ImageLoader* image) } // If this image is the potential canonical definition of any weak defs, then set them to a tombstone value - if ( gLinkContext.weakDefMapInitialized && image->hasCoalescedExports() ) { + if ( gLinkContext.weakDefMapInitialized && image->hasCoalescedExports() && (image->getState() >= dyld_image_state_bound) ) { Diagnostics diag; const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader(); - ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) { + ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) { auto it = gLinkContext.weakDefMap.find(symbolName); - assert(it != gLinkContext.weakDefMap.end()); + if ( it == gLinkContext.weakDefMap.end() ) + return; it->second = { nullptr, 0 }; if ( !isFromExportTrie ) { // The string was already duplicated if we are an export trie @@ -1531,7 +1703,15 @@ void runImageStaticTerminators(ImageLoader* image) static void terminationRecorder(ImageLoader* image) { - sImageFilesNeedingTermination.push_back(image); + bool add = true; +#if __arm64e__ + // Don't run static terminator for arm64e + const mach_header* mh = image->machHeader(); + if ( (mh->cputype == CPU_TYPE_ARM64) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) + add = false; +#endif + if ( add ) + sImageFilesNeedingTermination.push_back(image); } const char* getExecutablePath() @@ -1849,7 +2029,7 @@ static void appendParsedColonList(const char* list, const char* mainExecutableDi } } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX static void paths_expand_roots(const char **paths, const char *key, const char *val) { // assert(val != NULL); @@ -1998,13 +2178,13 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch } else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) { sEnv.DYLD_PRINT_STATISTICS = true; -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps sForceStderr = true; #endif } else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps sForceStderr = true; #endif @@ -2067,6 +2247,9 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) { // Handled elsewhere } + else if ( strcmp(key, "DYLD_SHARED_REGION_DATA_CONST") == 0 ) { + // Handled elsewhere + } else if ( strcmp(key, "DYLD_FORCE_INVALID_CACHE_CLOSURES") == 0 ) { if ( dyld3::internalInstall() ) { sForceInvalidSharedCacheClosureFormat = true; @@ -2107,7 +2290,7 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch #endif #if !TARGET_OS_SIMULATOR else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && gLinkContext.allowEnvVarsSharedCache ) { - int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644); + int fd = dyld3::open(value, O_WRONLY | O_CREAT | O_APPEND, 0644); if ( fd != -1 ) { sLogfile = fd; sLogToFile = true; @@ -2220,7 +2403,7 @@ static void checkVersionedPaths() #endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // // For security, setuid programs ignore DYLD_* environment variables. // Additionally, the DYLD_* enviroment variables are removed @@ -2272,7 +2455,7 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) static void defaultUninitializedFallbackPaths(const char* envp[]) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX if ( !gLinkContext.allowClassicFallbackPaths ) { sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths; sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths; @@ -2440,7 +2623,7 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // if main executable has segments that overlap the shared region, // then disable using the shared region if ( mainExecutableMH->intersectsRange(SHARED_REGION_BASE, SHARED_REGION_SIZE) ) { @@ -2454,6 +2637,9 @@ static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; } #endif +#endif +#if TARGET_OS_SIMULATOR + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; #endif // iOS cannot run without shared region } @@ -2655,34 +2841,29 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = { }; #endif -#if __arm64__ -// -// ARM64 sub-type lists -// -const int kARM64_RowCount = 2; -static const cpu_subtype_t kARM64[kARM64_RowCount][4] = { - - // armv64e can run: 64e, 64 - { CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }, - - // armv64 can run: 64 - { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }, -}; - #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 }, -}; +// +// arm64_32 sub-type lists +// + static const cpu_subtype_t kARM64_32[] = { CPU_SUBTYPE_ARM64_32_V8, CPU_SUBTYPE_END_OF_LIST }; #endif - + +#if __arm64__ && __LP64__ +// +// arm64[e] sub-type handing +// + #if __arm64e__ + // arm64e with keys on + static const cpu_subtype_t kARM64e[] = { CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_END_OF_LIST }; + // arm64 or arm64e with keys off + static const cpu_subtype_t kARM64eKeysOff[] = { CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }; + #else + // arm64 main binary + static const cpu_subtype_t kARM64[] = { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }; + #endif // __arm64e__ #endif + #if __x86_64__ // // x86_64 sub-type lists @@ -2713,21 +2894,20 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub break; #endif #if __arm64__ + #if __LP64__ case CPU_TYPE_ARM64: - for (int i=0; i < kARM64_RowCount ; ++i) { - if ( kARM64[i][0] == subtype ) - return kARM64[i]; - } + #if __arm64e__ + return ( sKeysDisabled ? kARM64eKeysOff : kARM64e); + #else + return kARM64; + #endif break; + #endif -#if __ARM64_ARCH_8_32__ + #if !__LP64__ 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 + return kARM64_32; + #endif #endif #if __x86_64__ @@ -2743,18 +2923,26 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub } - - // scan fat table-of-contents for best most preferred subtype -static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len) +static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, int fd, uint64_t* offset, uint64_t* len) { const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) { for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) { - if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) - && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) { - *offset = OSSwapBigToHostInt32(archs[fatIndex].offset); - *len = OSSwapBigToHostInt32(archs[fatIndex].size); + cpu_type_t sliceCpuType = OSSwapBigToHostInt32(archs[fatIndex].cputype); + cpu_subtype_t sliceCpuSubType = OSSwapBigToHostInt32(archs[fatIndex].cpusubtype) & ~CPU_SUBTYPE_MASK; + uint64_t sliceOffset = OSSwapBigToHostInt32(archs[fatIndex].offset); + uint64_t sliceLen = OSSwapBigToHostInt32(archs[fatIndex].size); + if ( (sliceCpuType == cpu) && ((list[subTypeIndex] & ~CPU_SUBTYPE_MASK) == sliceCpuSubType) ) { +#if TARGET_OS_OSX && __has_feature(ptrauth_calls) + if ( sOnlyPlatformArm64e && (sliceCpuType == CPU_TYPE_ARM64) && (sliceCpuSubType == CPU_SUBTYPE_ARM64E) ) { + // if we can only load arm64e slices that are platform binaries, skip over slices that are not + if ( !dyld3::MachOAnalyzer::sliceIsOSBinary(fd, sliceOffset, sliceLen) ) + continue; + } +#endif + *offset = sliceOffset; + *len = sliceLen; return true; } } @@ -2762,6 +2950,7 @@ static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[ return false; } +#if !TARGET_OS_OSX || !__has_feature(ptrauth_calls) // scan fat table-of-contents for exact match of cpu and cpu-sub-type static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len) { @@ -2776,6 +2965,7 @@ static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_h } return false; } +#endif // scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len) @@ -2884,7 +3074,7 @@ static bool fatValidate(const fat_header* fh) // each optimized for a different cpu-sub-type (e.g G3 or G5). // This routine picks the optimal sub-image. // -static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) +static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len, int fd=-1) { if ( !fatValidate(fh) ) return false; @@ -2900,13 +3090,17 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) // use ordered list to find best sub-image in fat file if ( subTypePreferenceList != NULL ) { - if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) ) + if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, fd, offset, len) ) return true; } - +#if TARGET_OS_OSX && __has_feature(ptrauth_calls) + // don't use fallbacks for macOS arm64e to ensure only compatible binaries are loaded + return false; +#else // if running cpu is not in list, try for an exact match if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) ) return true; +#endif } // running on an uknown cpu, can only load generic code @@ -2925,12 +3119,21 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) #endif } +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR +#ifndef kIsTranslated + #define kIsTranslated 0x4000000000000000ULL +#endif +bool isTranslated() +{ + return ((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64) & kIsTranslated); +} +#endif // // This is used to validate if a non-fat (aka thin or raw) mach-o file can be used // on the current processor. // -bool isCompatibleMachO(const uint8_t* firstPage, const char* path) +bool isCompatibleMachO(const uint8_t* firstPage, const char* path, int fd=-1, uint64_t sliceOffset=0, uint64_t sliceLen=-1) { #if CPU_SUBTYPES_SUPPORTED // It is deemed compatible if any of the following are true: @@ -2941,22 +3144,31 @@ bool isCompatibleMachO(const uint8_t* firstPage, const char* path) if ( mh->magic == sMainExecutableMachHeader->magic ) { if ( mh->cputype == sMainExecutableMachHeader->cputype ) { if ( mh->cputype == sHostCPU ) { + const cpu_subtype_t mhCPUSubtype = mh->cpusubtype & ~CPU_SUBTYPE_MASK; // get preference ordered list of subtypes that this machine can use const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype); if ( subTypePreferenceList != NULL ) { // if image's subtype is in the list, it is compatible for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) { - if ( *p == mh->cpusubtype ) + if ( *p == mhCPUSubtype ) { + #if TARGET_OS_OSX && __has_feature(ptrauth_calls) + if ( mhCPUSubtype == CPU_SUBTYPE_ARM64E ) { + if ( !sOnlyPlatformArm64e || dyld3::MachOAnalyzer::sliceIsOSBinary(fd, sliceOffset, sliceLen) ) + return true; + } + else + #endif return true; + } } // have list and not in list, so not compatible - throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path); + throwf("incompatible cpu-subtype: 0x%08X in %s", mhCPUSubtype, path); } // unknown cpu sub-type, but if exact match for current subtype then ok to use - if ( mh->cpusubtype == sHostCPUsubtype ) + if ( mhCPUSubtype == sHostCPUsubtype ) return true; } - + // cpu type has no ordered list of subtypes switch (mh->cputype) { case CPU_TYPE_I386: @@ -2987,13 +3199,13 @@ bool isCompatibleMachO(const uint8_t* firstPage, const char* path) static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) { // try mach-o loader - if ( isCompatibleMachO((const uint8_t*)mh, path) ) { +// if ( isCompatibleMachO((const uint8_t*)mh, path) ) { ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); addImage(image); return (ImageLoaderMachO*)image; - } +// } - throw "main executable not a known format"; +// throw "main executable not a known format"; } #if SUPPORT_ACCELERATE_TABLES @@ -3111,7 +3323,7 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0) || (strcmp(path, "/sbin/launchd_sim_trampoline") == 0) || (strcmp(path, "/usr/sbin/iokitsimd") == 0) || - (strcmp(path, "/usr/lib/system/host/liblaunch_sim.dylib") == 0)) + (strcmp(path, "/usr/lib/system/host/liblaunch_sim.dylib") == 0)) return true; return false; case LC_BUILD_VERSION: @@ -3124,10 +3336,6 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) 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) || @@ -3149,33 +3357,6 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) } #endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED -static bool iOSonMacDenied(const char* path) -{ - static char* blackListBuffer = nullptr; - static size_t blackListSize = 0; - static bool tried = false; - if ( !tried ) { - // only try to map file once -#ifndef DARLING - blackListBuffer = (char*)mapFileReadOnly("/System/iOSSupport/dyld/macOS-deny-list.txt", blackListSize); -#endif - 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) @@ -3189,7 +3370,6 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* 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 @@ -3209,7 +3389,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch)); - if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { + if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength, fd) ) { if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); if (pread(fd, firstPages, 4096, fileOffset) != 4096) @@ -3224,7 +3404,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* if ( shortPage ) throw "file too short"; - if ( isCompatibleMachO(firstPages, path) ) { + if ( isCompatibleMachO(firstPages, path, fd, fileOffset, fileLength) ) { // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded const mach_header* mh = (mach_header*)firstPages; @@ -3238,60 +3418,45 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* } uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds; - if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) - throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); - if ( headerAndLoadCommandsSize > fileLength ) dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength); - if ( headerAndLoadCommandsSize > 4096 ) { + vm_address_t vmAllocatedFirstPages = 0; + if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) { + if ( ::vm_allocate(mach_task_self(), &vmAllocatedFirstPages, headerAndLoadCommandsSize, VM_FLAGS_ANYWHERE) == 0 ) { + if ( ::pread(fd, (void*)vmAllocatedFirstPages, headerAndLoadCommandsSize, fileOffset) != headerAndLoadCommandsSize ) + throwf("pread of all load commands failed: %d", errno); + mh = (mach_header*)vmAllocatedFirstPages; + } + else { + throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); + } + } + else if ( headerAndLoadCommandsSize > 4096 ) { // read more pages unsigned readAmount = headerAndLoadCommandsSize - 4096; if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount ) throwf("pread of extra load commands past 4KB failed: %d", errno); } -#if TARGET_OS_SIMULATOR - // dyld_sim should restrict loading osx binaries - if ( !isSimulatorBinary(firstPages, path) ) { - #if TARGET_OS_WATCH - throw "mach-o, but not built for watchOS simulator"; - #elif TARGET_OS_TV - throw "mach-o, but not built for tvOS simulator"; - #else - throw "mach-o, but not built for iOS simulator"; - #endif + if ( !((dyld3::MachOFile*)mh)->loadableIntoProcess((dyld3::Platform)gProcessInfo->platform, path) ) { + throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform)gProcessInfo->platform)); } -#endif -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.iOSonMac ) { - const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages; - bool supportsiOSMac = mf->supportsPlatform(dyld3::Platform::iOSMac); - if ( !supportsiOSMac && iOSonMacDenied(path) ) { - throw "mach-o, but not built for UIKitForMac"; - } - } - else if ( gLinkContext.driverKit ) { - const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages; - bool isDriverKitDylib = mf->supportsPlatform(dyld3::Platform::driverKit); - if ( !isDriverKitDylib ) { - throw "mach-o, but not built for driverkit"; - } - } -#endif - -#if __arm64e__ - if ( (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64E) && (mh->cpusubtype != CPU_SUBTYPE_ARM64E) ) +#if __has_feature(ptrauth_calls) + if ( !sKeysDisabled && ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) != CPU_SUBTYPE_ARM64E) ) 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); + image = ImageLoaderMachO::instantiateFromFile(path, fd, (uint8_t*)mh, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); timer.setData4((uint64_t)image->machHeader()); } - + + if ( vmAllocatedFirstPages != 0 ) + ::vm_deallocate(mach_task_self(), (vm_address_t)vmAllocatedFirstPages, headerAndLoadCommandsSize); + // validate return checkandAddImage(image, context); } @@ -3358,6 +3523,8 @@ static bool isFileRelativePath(const char* path) return false; } +static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context); + // try to open file static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) @@ -3375,20 +3542,30 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) ) return sAllCacheImagesProxy; } -#endif -#if TARGET_OS_SIMULATOR - // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath - const char* pathToFindInCache = orgPath; -#else - const char* pathToFindInCache = path; #endif uint statErrNo; struct stat statBuf; bool didStat = false; bool existsOnDisk; - dyld3::SharedCacheFindDylibResults shareCacheResults; + __block dyld3::SharedCacheFindDylibResults shareCacheResults; shareCacheResults.image = nullptr; - if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) { + +#if TARGET_OS_SIMULATOR + + auto findSharedCacheImage = ^() { + // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath + return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults); + }; + +#else + + auto findSharedCacheImage = ^() { + return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults); + }; + +#endif + + if ( findSharedCacheImage() ) { // see if this image in the cache was already loaded via a different path for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { ImageLoader* anImage = *it; @@ -3398,7 +3575,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const // if RTLD_NOLOAD, do nothing if not already loaded if ( context.dontLoad ) { // possible that there is an override of cache - if ( my_stat(path, &statBuf) == 0 ) { + if ( dyld3::stat(path, &statBuf) == 0 ) { ImageLoader* imageLoader = findLoadedImage(statBuf); if ( imageLoader != NULL ) return imageLoader; @@ -3408,7 +3585,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const bool useCache = false; if ( shareCacheResults.image == nullptr ) { // HACK to support old caches - existsOnDisk = ( my_stat(path, &statBuf) == 0 ); + existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 ); didStat = true; statErrNo = errno; useCache = !existsOnDisk; @@ -3417,44 +3594,58 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const // zero out stat buffer so mtime, etc are zero for items from the shared cache bzero(&statBuf, sizeof(statBuf)); if ( shareCacheResults.image->overridableDylib() ) { - existsOnDisk = ( my_stat(path, &statBuf) == 0 ); - didStat = true; + existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 ); statErrNo = errno; if ( sSharedCacheLoadInfo.loadAddress->header.dylibsExpectedOnDisk ) { + // old style macOS with dylibs on disk uint64_t expectedINode; uint64_t expectedMtime; if ( shareCacheResults.image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) { + // if dylib found has same inode/mtime as one in cache, use one in cache if ( (expectedMtime == statBuf.st_mtime) && (expectedINode == statBuf.st_ino) ) useCache = true; } } else { - if ( !existsOnDisk ) + // MRM style where dylibs are not on disk + if ( !existsOnDisk ) { + // looking at path where dylib should be, and we expect it to not be there but rather in the cache + // Its possible we are looking at a deleted symlink path. For example, we are trying to open .../AppKit but + // there's already a loaded root of .../Versions/C/AppKit. That used to work when the symlink was on-disk as + // we'd realpath to find the shared cache path. Now we record the aliases in the cache and delete the symlinks. + const char* pathInSharedCache = shareCacheResults.image->path(); + if ( strcmp(path, pathInSharedCache) != 0 ) { + ImageLoader* imageLoader = loadPhase5check(pathInSharedCache, orgPath, context); + if ( imageLoader != NULL ) + return imageLoader; + } useCache = true; + } + else if ( !sRootsChecker.onDiskFileIsRoot(path, sSharedCacheLoadInfo.loadAddress, + shareCacheResults.image, nullptr, statBuf.st_ino, statBuf.st_mtime) ) { + // we found a file on disk, at the same path as the dyld cache has a dylib and it is one of the magic three + useCache = true; + } } } else { + // we are trying to override a dylib in the cache that does not allow overrides, ignore override and use cache useCache = true; } } if ( useCache ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gLinkContext.iOSonMac ) { - const dyld3::MachOFile* mf = (dyld3::MachOFile*)shareCacheResults.mhInCache; - bool supportsiOSMac = mf->supportsPlatform(dyld3::Platform::iOSMac); - if ( !supportsiOSMac && iOSonMacDenied(path) ) { - throw "mach-o, but not built for UIKitForMac"; - } - } -#endif - ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext); + const dyld3::MachOFile* cacheDylibMH = (dyld3::MachOFile*)shareCacheResults.mhInCache; + if ( !cacheDylibMH->loadableIntoProcess((dyld3::Platform)gProcessInfo->platform, path) ) + throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform)gProcessInfo->platform)); + + ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)cacheDylibMH, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext); return checkandAddImage(imageLoader, context); } } // not in cache or cache not usable if ( !didStat ) { - existsOnDisk = ( my_stat(path, &statBuf) == 0 ); + existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 ); statErrNo = errno; } if ( existsOnDisk ) { @@ -3733,8 +3924,18 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load 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) ) + if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) ) { image->setOverridesCachedDylib(shareCacheResults.image->imageNum()); + } +#if SUPPORT_ROOT_PATH + else if ( (gLinkContext.rootPaths != nullptr) + && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults) + && (shareCacheResults.image != nullptr) ) { + // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden + // as the root prefix has been applied to 'path', but the framework path searches without a root path prefix + image->setOverridesCachedDylib(shareCacheResults.image->imageNum()); + } +#endif return image; } } @@ -3757,11 +3958,21 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load image = loadPhase2cache(libpath, orgPath, context, cacheIndex, exceptions); 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; - } + dyld3::SharedCacheFindDylibResults shareCacheResults; + if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) ) { + image->setOverridesCachedDylib(shareCacheResults.image->imageNum()); + } +#if SUPPORT_ROOT_PATH + else if ( (gLinkContext.rootPaths != nullptr) + && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults) + && (shareCacheResults.image != nullptr) ) { + // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden + // as the root prefix has been applied to 'path', but the library path searches without a root path prefix + image->setOverridesCachedDylib(shareCacheResults.image->imageNum()); + } +#endif + return image; + } } } return NULL; @@ -3818,15 +4029,25 @@ static ImageLoader* loadPhase1(const char* path, const char* orgPath, const Load return image; } +#if SUPPORT_VERSIONED_PATHS + // DYLD_VERSIONED_FRAMEWORK_PATH fails to load a framework if it does not also exist at the system install path + // Scan to see if the dylib appears in a versioned path. Don't worry if we find the newest, that will handled later + if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_VERSIONED_LIBRARY_PATH != NULL)) ) { + image = loadPhase2(path, orgPath, context, sEnv.DYLD_VERSIONED_FRAMEWORK_PATH, sEnv.DYLD_VERSIONED_LIBRARY_PATH, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } +#endif + return NULL; } // try root substitutions static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) { - // dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // handle macOS dylibs dlopen()ing versioned path which needs to map to flat path in mazipan simulator if ( gLinkContext.iOSonMac && strstr(path, ".framework/Versions/")) { uintptr_t sourceOffset = 0; @@ -3924,6 +4145,11 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && dyld3::MachOFile::isSharedCacheEligiblePath(path) && inSharedCache(path) ) { gSharedCacheOverridden = true; } + // if file loaded via symlink to a root of something in dyld cache, mark it as an override + dyld3::SharedCacheFindDylibResults shareCacheResults; + if ( !image->inSharedCache() && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, image->getRealPath(), &shareCacheResults) && (shareCacheResults.image != nullptr) ) + image->setOverridesCachedDylib(shareCacheResults.image->imageNum()); + return image; } else if ( exceptions.size() == 0 ) { @@ -3954,19 +4180,20 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI -static void mapSharedCache() +static void mapSharedCache(uintptr_t mainExecutableSlide) { dyld3::SharedCacheOptions opts; opts.cacheDirOverride = sSharedCacheOverrideDir; opts.forcePrivate = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion); - - #if __x86_64__ && !TARGET_OS_SIMULATOR opts.useHaswell = sHaswell; #else opts.useHaswell = false; #endif opts.verbose = gLinkContext.verboseMapping; + // respect -disable_aslr boot-arg + // kern.bootargs is now blocked + opts.disableASLR = (mainExecutableSlide == 0) && dyld3::internalInstall(); // infer ASLR is off if main executable is not slid loadDyldCache(opts, &sSharedCacheLoadInfo); // update global state @@ -3979,7 +4206,7 @@ static void mapSharedCache() dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, sSharedCacheLoadInfo.path, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress); } -//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +//#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // RAM disk booting does not have shared cache yet // Don't make lack of a shared cache fatal in that case // if ( sSharedCacheLoadInfo.loadAddress == nullptr ) { @@ -4163,6 +4390,24 @@ void halt(const char* message) strlcat(error_string, sSharedCacheLoadInfo.errorMessage, sizeof(error_string)); strlcat(error_string, "\n", sizeof(error_string)); strlcat(error_string, message, sizeof(error_string)); + } else if ( dyld::gProcessInfo->errorKind == DYLD_EXIT_REASON_DYLIB_MISSING ) { + // If a dylib is missing, but we have the cache, print the cache UUID to make it easier + // to see what might have gone wrong + if ( sSharedCacheLoadInfo.loadAddress == nullptr ) { + strlcpy(error_string, "dyld: No shared cache present\n", sizeof(error_string)); + } else { + uuid_t cacheUUID; + sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID); + uuid_string_t uuidStr; + uuid_unparse_upper(cacheUUID, uuidStr); + + strlcpy(error_string, "dyld: Using shared cache: ", sizeof(error_string)); + strlcat(error_string, uuidStr, sizeof(error_string)); + strlcat(error_string, "\n", sizeof(error_string)); + } + + dyld::log("dyld: %s\n", message); + strlcat(error_string, message, sizeof(error_string)); } else { dyld::log("dyld: %s\n", message); @@ -4234,7 +4479,8 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) #endif if ( target == NULL ) throwf("image not found for lazy pointer at %p", lazyPointer); - result = target->doBindLazySymbol(lazyPointer, gLinkContext); + DyldSharedCache::DataConstLazyScopedWriter patcher(gLinkContext.dyldCache, mach_task_self(), gLinkContext.verboseMapping ? &dyld::log : nullptr); + result = target->doBindLazySymbol(lazyPointer, gLinkContext, patcher); } catch (const char* message) { dyld::log("dyld: lazy symbol binding failed: %s\n", message); @@ -4706,7 +4952,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha // Its presences means that the binary wants to have DYLD ignore // DYLD_ environment variables. // -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX static bool hasRestrictedSegment(const macho_header* mh) { const uint32_t cmd_count = mh->ncmds; @@ -4737,7 +4983,7 @@ static bool hasRestrictedSegment(const macho_header* mh) } #endif -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR static bool isFairPlayEncrypted(const macho_header* mh) { const uint32_t cmd_count = mh->ncmds; @@ -4757,7 +5003,9 @@ static bool isFairPlayEncrypted(const macho_header* mh) #if SUPPORT_VERSIONED_PATHS -static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) +#define FIRST_PAGE_BUFFER_SIZE 16384 + +static bool readFirstPage(const char* dylibPath, uint8_t firstPage[FIRST_PAGE_BUFFER_SIZE]) { firstPage[0] = 0; // open file (automagically closed when this function exits) @@ -4766,7 +5014,7 @@ static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) if ( file.getFileDescriptor() == -1 ) return false; - if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 ) + if ( pread(file.getFileDescriptor(), firstPage, FIRST_PAGE_BUFFER_SIZE, 0) != FIRST_PAGE_BUFFER_SIZE ) return false; // if fat wrapper, find usable sub-file @@ -4774,8 +5022,8 @@ static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { uint64_t fileOffset; uint64_t fileLength; - if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { - if ( pread(file.getFileDescriptor(), firstPage, 4096, fileOffset) != 4096 ) + if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength, file.getFileDescriptor()) ) { + if ( pread(file.getFileDescriptor(), firstPage, FIRST_PAGE_BUFFER_SIZE, fileOffset) != FIRST_PAGE_BUFFER_SIZE ) return false; } else { @@ -4792,7 +5040,7 @@ static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) // static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName) { - uint8_t firstPage[4096]; + uint8_t firstPage[FIRST_PAGE_BUFFER_SIZE]; const macho_header* mh = (macho_header*)firstPage; if ( !readFirstPage(dylibPath, firstPage) ) { // If file cannot be read, check to see if path is in shared cache @@ -4813,7 +5061,7 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi // scan load commands for LC_ID_DYLIB const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096); + const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+FIRST_PAGE_BUFFER_SIZE); const struct load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { @@ -5066,18 +5314,21 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c uint64_t amfiInputFlags = 0; #if TARGET_OS_SIMULATOR amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR; -#elif __MAC_OS_X_VERSION_MIN_REQUIRED && !defined(DARLING) +#elif TARGET_OS_OSX if ( hasRestrictedSegment(mainExecutableMH) ) amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG; -#elif __IPHONE_OS_VERSION_MIN_REQUIRED +#elif TARGET_OS_IPHONE if ( isFairPlayEncrypted(mainExecutableMH) ) amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED; #endif uint64_t amfiOutputFlags = 0; const char* amfiFake = nullptr; - if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) { + if constexpr(BUILD_FOR_TESTING == 1) { + amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE"); + } else if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) { amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE"); } + if ( amfiFake != nullptr ) { amfiOutputFlags = hexToUInt64(amfiFake, nullptr); } @@ -5088,9 +5339,14 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c 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); +#ifdef AMFI_RETURNS_INTERPOSING_FLAG + gLinkContext.allowInterposing = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING); +#else + gLinkContext.allowInterposing = true; +#endif } else { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // support chrooting from old kernel bool isRestricted = false; bool libraryValidation = false; @@ -5117,6 +5373,7 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP; gLinkContext.allowClassicFallbackPaths = !isRestricted; gLinkContext.allowInsertFailures = false; + gLinkContext.allowInterposing = true; #else halt("amfi_check_dyld_policy_self() failed\n"); #endif @@ -5126,15 +5383,15 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c // called by _dyld_register_driverkit_main() void setMainEntry(void (*main)()) { - if ( sEntryOveride == nullptr ) - sEntryOveride = main; + if ( sEntryOverride == nullptr ) + sEntryOverride = main; else halt("_dyld_register_driverkit_main() may only be called once"); } bool processIsRestricted() { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX return !gLinkContext.allowEnvVarsPath; #else return false; @@ -5173,7 +5430,7 @@ void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo) fsobj_id_t fsobj_id = {0}; if (endptr != nullptr) { fsobj_id_scalar = hexToUInt64(endptr+1, &endptr); - fsobj_id = *reinterpret_cast(&tmp); + fsobj_id = *reinterpret_cast(&fsobj_id_scalar); } const uint32_t cmd_count = mh->ncmds; const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header)); @@ -5195,27 +5452,12 @@ void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo) } } - - -#if __MAC_OS_X_VERSION_MIN_REQUIRED -typedef int (*open_proc_t)(const char*, int, int); -typedef int (*fcntl_proc_t)(int, int, void*); -typedef int (*ioctl_proc_t)(int, unsigned long, void*); +#if TARGET_OS_OSX static void* getProcessInfo() { return dyld::gProcessInfo; } -#ifdef DARLING -#undef kdebug_is_enabled -#undef kdebug_trace -#undef kdebug_trace_string -static bool kdebug_is_enabled(uint32_t code) { return false; } -static int kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) { return 0;} -static uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str) { return 0; } -//int (*amfi_check_dyld_policy_self)(uint64_t input_flags, uint64_t* output_flags); -#endif - static const SyscallHelpers sSysCalls = { - 12, + 14, // added in version 1 - (open_proc_t)&open, + &open, &close, &pread, &write, @@ -5223,8 +5465,8 @@ static const SyscallHelpers sSysCalls = { &munmap, &madvise, &stat, - (fcntl_proc_t)&fcntl, - (ioctl_proc_t)&ioctl, + &fcntl, + &ioctl, &issetugid, &getcwd, &realpath, @@ -5258,7 +5500,7 @@ static const SyscallHelpers sSysCalls = { &getpid, &mach_port_insert_right, &mach_port_allocate, - &mach_msg, + &mach_msg_sim_interposed, // Added in version 6 &abort_with_payload, // Added in version 7 @@ -5283,7 +5525,12 @@ static const SyscallHelpers sSysCalls = { // Add in version 12 &mach_msg_destroy, &mach_port_construct, - &mach_port_destruct + &mach_port_destruct, + // Added in version 13 + &fstat, + &vm_copy, + // Added in version 14 + &task_dyld_process_info_notify_get }; __attribute__((noinline)) @@ -5326,7 +5573,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH if ( pread(fd, firstPage, 4096, fileOffset) != 4096 ) return "pread(dyld_sim) failed"; } - else if ( !isCompatibleMachO(firstPage, dyldPath) ) { + else if ( !isCompatibleMachO(firstPage, dyldPath, fd, fileOffset, fileLength) ) { return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch"; } @@ -5458,34 +5705,22 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH } close(fd); - // walk newly mapped dyld_sim __TEXT load commands to find entry point + // Walk newly mapped dyld_sim load commands to find entry point uintptr_t entry = 0; - cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header)); - const uint32_t count = ((macho_header*)(loadAddress))->ncmds; - for (uint32_t i = 0; i < count; ++i) { - if (cmd->cmd == LC_UNIXTHREAD) { - #if __i386__ - const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); - // entry point must be in first segment - if ( registers->__eip < firstSeg->vmaddr ) - return "dyld_sim entry point not in __TEXT segment"; - if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) ) - return "dyld_sim entry point not in __TEXT segment"; - entry = (registers->__eip + loadAddress - preferredLoadAddress); - #elif __x86_64__ - const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); - // entry point must be in first segment - if ( registers->__rip < firstSeg->vmaddr ) - return "dyld_sim entry point not in __TEXT segment"; - if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) ) - return "dyld_sim entry point not in __TEXT segment"; - entry = (registers->__rip + loadAddress - preferredLoadAddress); - #endif - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - if ( entry == 0 ) + bool unusedUsesCRT = false; + uint64_t entryOffset = 0; + if ( !((dyld3::MachOAnalyzer*)loadAddress)->getEntry(entryOffset, unusedUsesCRT) ) { return "dyld_sim entry not found"; + } + + // Translate the load address by the entry offset in order to get the runtime address. + entry = (uintptr_t)loadAddress; + entry += entryOffset; + +#if __arm64e__ + // It's necessary to sign the entry pointer. + entry = (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)entry, ptrauth_key_asia, 0); +#endif // notify debugger that dyld_sim is loaded dyld_image_info info; @@ -5501,7 +5736,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH fsobj.fid_objno = (uint32_t)inode; fsobj.fid_generation = (uint32_t)(inode>>32); fsid.val[0] = sb.st_dev; - dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, dyldPath, (const uuid_t *)&uuidCmd->uuid[0], fsobj, fsid, (const mach_header *)mh); + dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, dyldPath, (const uuid_t *)&uuidCmd->uuid[0], fsobj, fsid, (const mach_header *)loadAddress); const char** appleParams = apple; @@ -5595,6 +5830,29 @@ static bool envVarsMatch(const dyld3::closure::LaunchClosure* mainClosure, const return true; } + +void getCDHashString(const uint8_t cdHash[20], char* cdHashBuffer) { + 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 ) { + *cdHashBuffer = '0' + nibbleH; + ++cdHashBuffer; + } else { + *cdHashBuffer = 'a' + (nibbleH-10); + ++cdHashBuffer; + } + if ( nibbleL < 10 ) { + *cdHashBuffer = '0' + nibbleL; + ++cdHashBuffer; + } else { + *cdHashBuffer = 'a' + (nibbleL-10); + ++cdHashBuffer; + } + } +} + static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const dyld3::closure::LoadedFileInfo& mainFileInfo, const uint8_t* mainExecutableCDHash, bool closureInCache, const char* envp[]) { @@ -5638,33 +5896,6 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const return false; } } -#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 ) { - // If we got a boot UUID then we should have also recorded it in the closure - if ( expectedBootUUID == nullptr) { - // The closure didn't have a UUID but now we do. This isn't valid. - if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: closure %p missing boot-UUID\n", mainClosure); - return false; - } else if ( strcmp(expectedBootUUID, actualBootSessionUUID) != 0 ) { - if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: closure %p built in different boot context\n", mainClosure); - return false; - } - } else { - // We didn't get a UUID now, which is ok so long as the closure also doesn't have one. - if ( expectedBootUUID != nullptr) { - if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: closure %p has boot-UUID\n", mainClosure); - return false; - } - } -#endif } // verify all mach-o files have not changed since closure was built @@ -5674,7 +5905,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const __block uint64_t expectedMtime; if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) { struct stat statBuf; - if ( ::stat(image->path(), &statBuf) == 0 ) { + if ( dyld3::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()); @@ -5694,7 +5925,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const return false; // verify cdHash of main executable is same as recorded in closure - const dyld3::closure::Image* mainImage = mainClosure->images()->imageForNum(mainClosure->topImage()); + const dyld3::closure::Image* mainImage = mainClosure->topImage(); __block bool foundCDHash = false; __block bool foundValidCDHash = false; @@ -5716,27 +5947,6 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const // If we found cd hashes, but they were all invalid, then print them out if ( foundCDHash && !foundValidCDHash ) { - auto getCDHashString = [](const uint8_t cdHash[20], char* cdHashBuffer) { - 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 ) { - *cdHashBuffer = '0' + nibbleH; - ++cdHashBuffer; - } else { - *cdHashBuffer = 'a' + (nibbleH-10); - ++cdHashBuffer; - } - if ( nibbleL < 10 ) { - *cdHashBuffer = '0' + nibbleL; - ++cdHashBuffer; - } else { - *cdHashBuffer = 'a' + (nibbleL-10); - ++cdHashBuffer; - } - } - }; if ( gLinkContext.verboseWarnings ) { mainImage->forEachCDHash(^(const uint8_t *expectedHash, bool &stop) { char mainExecutableCDHashBuffer[128] = { '\0' }; @@ -5778,7 +5988,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const // verify files that are supposed to be missing actually are missing mainClosure->forEachMustBeMissingFile(^(const char* path, bool& stop) { struct stat statBuf; - if ( ::stat(path, &statBuf) == 0 ) { + if ( dyld3::stat(path, &statBuf) == 0 ) { stop = true; foundFileThatInvalidatesClosure = true; if ( gLinkContext.verboseWarnings ) @@ -5789,7 +5999,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const // verify files that are supposed to exist are there with the mainClosure->forEachSkipIfExistsFile(^(const dyld3::closure::LaunchClosure::SkippedFile &file, bool &stop) { struct stat statBuf; - if ( ::stat(file.path, &statBuf) == 0 ) { + if ( dyld3::stat(file.path, &statBuf) == 0 ) { if ( (statBuf.st_mtime != file.mtime) || (statBuf.st_ino != file.inode) ) { if ( gLinkContext.verboseWarnings ) dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure, file.path); @@ -5810,7 +6020,11 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const dyld::log("dyld: closure %p not used because is used default fallback paths, but process does not allow that\n", mainClosure); return false; } - + if ( mainClosure->usedInterposing() && !gLinkContext.allowInterposing ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because is uses interposing, but process does not allow that\n", mainClosure); + return false; + } return !foundFileThatInvalidatesClosure; } @@ -5831,9 +6045,12 @@ static bool dolog(const char* format, ...) static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, const DyldSharedCache* dyldCache, const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide, - int argc, const char* argv[], const char* envp[], const char* apple[], - uintptr_t* entry, uintptr_t* startGlue) + int argc, const char* argv[], const char* envp[], const char* apple[], Diagnostics& diag, + uintptr_t* entry, uintptr_t* startGlue, bool* closureOutOfDate, bool* recoverable) { + *closureOutOfDate = false; + *recoverable = true; + // 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(); @@ -5846,19 +6063,23 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, // allocate space for Array STACK_ALLOC_ARRAY(dyld3::LoadedImage, allImages, mainClosure->initialLoadCount()); + STACK_ALLOC_ARRAY(dyld3::LoadedImage, noImages, 1); // Get the pre-optimized Objective-C so that we can bind the selectors const dyld3::closure::ObjCSelectorOpt* selectorOpt = nullptr; dyld3::Array selectorImages; mainClosure->selectorHashTable(selectorImages, selectorOpt); - __block dyld3::Loader loader({}, allImages, dyldCache, imagesArrays, - selectorOpt, selectorImages, + __block dyld3::Loader loader(noImages, allImages, dyldCache, imagesArrays, + selectorOpt, selectorImages, sRootsChecker, + (dyld3::Platform)gProcessInfo->platform, (gLinkContext.verboseLoading ? &dolog : &nolog), (gLinkContext.verboseMapping ? &dolog : &nolog), (gLinkContext.verboseBind ? &dolog : &nolog), - (gLinkContext.verboseDOF ? &dolog : &nolog)); - dyld3::closure::ImageNum mainImageNum = mainClosure->topImage(); + (gLinkContext.verboseDOF ? &dolog : &nolog), + (sClosureKind == ClosureKind::minimal), + (dyld3::LaunchErrorInfo*)&gProcessInfo->errorKind); + dyld3::closure::ImageNum mainImageNum = mainClosure->topImageNum(); mainClosureImages->forEachImage(^(const dyld3::closure::Image* image, bool& stop) { if ( image->imageNum() == mainImageNum ) { // add main executable (which is already mapped by kernel) to list @@ -5876,13 +6097,32 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, // recursively load all dependents and fill in allImages array bool someCacheImageOverridden = false; - Diagnostics diag; loader.completeAllDependents(diag, someCacheImageOverridden); if ( diag.noError() ) - loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled()); + loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled(), false, closureOutOfDate, recoverable); if ( diag.hasError() ) { if ( gLinkContext.verboseWarnings ) dyld::log("dyld: %s\n", diag.errorMessage()); + if ( !*recoverable ) { + // we won't make it to libDyldEntry, so the image list will never be set up + // hack together an image list here so crash reports show the binaries involved + __block unsigned loadImageCount = 0; + loader.forEachImage(^(const dyld3::LoadedImage& li, bool& stop) { + ++loadImageCount; + }); + dyld_image_info* tempArray = new dyld_image_info[loadImageCount]; + __block unsigned i = 0; + loader.forEachImage(^(const dyld3::LoadedImage& li, bool& stop) { + tempArray[i].imageFilePath = li.image()->path(); + tempArray[i].imageLoadAddress = li.loadedAddress(); + tempArray[i].imageFileModDate = 0; + ++i; + }); + dyld::gProcessInfo->infoArray = tempArray; + dyld::gProcessInfo->infoArrayCount = loadImageCount; + dyld::gProcessInfo->initialImageCount= loadImageCount; + dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time(); + } return false; } @@ -5896,8 +6136,29 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, mainClosure->libDyldEntry(dyldEntry); const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)loader.resolveTarget(dyldEntry); + // Set the logging function first so that libdyld can log from inside all other entry vector functions +#if !TARGET_OS_SIMULATOR + if ( libDyldEntry->vectorVersion > 3 ) + libDyldEntry->setLogFunction(&dyld::vlog); +#endif + // send info on all images to libdyld.dylb - libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple); + libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple, sKeysDisabled, sOnlyPlatformArm64e, gEnableSharedCacheDataConst); +#if TARGET_OS_OSX + uint32_t progVarsOffset; + if ( mainClosure->hasProgramVars(progVarsOffset) ) { + if ( libDyldEntry->vectorVersion >= 8 ) { + // main executable contains globals to hold argc, argv, envp, and progname, but they need to be filled in + ProgramVars* vars = (ProgramVars*)((uint8_t*)mainExecutableMH + progVarsOffset); + *vars->NXArgcPtr = argc; + *vars->NXArgvPtr = argv; + *vars->environPtr = envp; + *vars->__prognamePtr = (argv[0] != NULL) ? basename(argv[0]) : ""; + // set up so libSystem gets ProgramVars struct embedded in main executable + libDyldEntry->setProgramVars(vars); + } + } +#endif if ( libDyldEntry->vectorVersion > 4 ) libDyldEntry->setRestrictions(gLinkContext.allowAtPaths, gLinkContext.allowEnvVarsPath, gLinkContext.allowClassicFallbackPaths); libDyldEntry->setHaltFunction(&halt); @@ -5911,13 +6172,14 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, if ( libDyldEntry->vectorVersion > 2 ) libDyldEntry->setChildForkFunction(&_dyld_fork_child); -#if !TARGET_OS_SIMULATOR - if ( libDyldEntry->vectorVersion > 3 ) - libDyldEntry->setLogFunction(&dyld::vlog); -#endif + if ( libDyldEntry->vectorVersion >= 9 ) + libDyldEntry->setLaunchMode(sLaunchModeUsed); + + libDyldEntry->setOldAllImageInfo(gProcessInfo); dyld3::LoadedImage* libSys = loader.findImage(mainClosure->libSystemImageNum()); - libDyldEntry->setInitialImageList(mainClosure, dyldCache, sSharedCacheLoadInfo.path, allImages, *libSys); + libDyldEntry->setInitialImageList(mainClosure, dyldCache, sSharedCacheLoadInfo.path, allImages, *libSys, + mach_task_self()); // run initializers CRSetCrashLogMessage("dyld3: launch, running initializers"); libDyldEntry->runInitialzersBottomUp((mach_header*)mainExecutableMH); @@ -5926,11 +6188,14 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, 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); } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX if ( gLinkContext.driverKit ) { - *entry = (uintptr_t)sEntryOveride; + if (libDyldEntry->vectorVersion >= 10) + *entry = (uintptr_t)libDyldEntry->getDriverkitMain(); if ( *entry == 0 ) halt("no entry point registered"); + if ( sClosureKind != ClosureKind::minimal ) + halt("driverkit process should run with minimal closures"); *startGlue = (uintptr_t)(libDyldEntry->startFunc); } else @@ -5943,6 +6208,10 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, // set entry to "main" function in program *startGlue = (uintptr_t)(libDyldEntry->startFunc); *entry = loader.resolveTarget(progEntry); +#if __has_feature(ptrauth_calls) + // start() calls the result pointer as a function pointer so we need to sign it. + *entry = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)*entry, 0, 0); +#endif } else if ( mainClosure->startEntry(progEntry) ) { // old style app linked with crt1.o @@ -5959,29 +6228,17 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure, } -static const char* getTempDir(const char* envp[]) -{ - if (const char* tempDir = _simple_getenv(envp, "TMPDIR")) - return tempDir; - -#if __MAC_OS_X_VERSION_MIN_REQUIRED - return "/private/tmp/"; -#else - return "/private/var/tmp/"; -#endif -} - static const dyld3::closure::LaunchClosure* mapClosureFile(const char* closurePath) { struct stat statbuf; - if ( ::stat(closurePath, &statbuf) == -1 ) + if ( dyld3::stat(closurePath, &statbuf) == -1 ) return nullptr; // check for tombstone file if ( statbuf.st_size == 0 ) return nullptr; - int fd = ::open(closurePath, O_RDONLY); + int fd = dyld3::open(closurePath, O_RDONLY, 0); if ( fd < 0 ) return nullptr; @@ -6001,10 +6258,12 @@ static bool needsDyld2ErrorMessage(const char* msg) return false; } - // Note: buildLaunchClosure calls halt() if there is an error building the closure -static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* mainExecutableCDHash, - const dyld3::closure::LoadedFileInfo& mainFileInfo, const char* envp[]) +static const dyld3::closure::LaunchClosure* buildLaunchClosure(bool canUseClosureFromDisk, + const uint8_t* mainExecutableCDHash, + const dyld3::closure::LoadedFileInfo& mainFileInfo, + const char* envp[], + const dyld3::Array& bootToken) { const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent; dyld3::closure::PathOverrides pathOverrides; @@ -6018,22 +6277,30 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma } char closurePath[PATH_MAX]; - dyld3::closure::ClosureBuilder::LaunchErrorInfo* errorInfo = (dyld3::closure::ClosureBuilder::LaunchErrorInfo*)&gProcessInfo->errorKind; + bool canSaveClosureToDisk = canUseClosureFromDisk && !bootToken.empty() && dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, envp, true, closurePath); + dyld3::LaunchErrorInfo* errorInfo = (dyld3::LaunchErrorInfo*)&gProcessInfo->errorKind; + const dyld3::GradedArchs& archs = dyld3::GradedArchs::forCurrentOS(sKeysDisabled, sOnlyPlatformArm64e); dyld3::closure::FileSystemPhysical fileSystem; - const dyld3::GradedArchs& archs = dyld3::GradedArchs::forCurrentOS(mainExecutableMH); 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, - archs, pathOverrides, atPathHanding, gLinkContext.allowEnvVarsPath, errorInfo); + dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, sRootsChecker, sSharedCacheLoadInfo.loadAddress, true, + archs, pathOverrides, atPathHanding, gLinkContext.allowEnvVarsPath, errorInfo, (dyld3::Platform)gProcessInfo->platform); if (sForceInvalidSharedCacheClosureFormat) builder.setDyldCacheInvalidFormatVersion(); + if (sClosureKind == ClosureKind::minimal) + builder.makeMinimalClosures(); + else if ( canSaveClosureToDisk ) + builder.setCanSkipEncodingRebases(); // large iOS apps with massive number of rebases can overflow 16MB closure file limit + if ( !gLinkContext.allowInterposing ) + builder.disableInterposing(); + const dyld3::closure::LaunchClosure* result = builder.makeLaunchClosure(mainFileInfo, gLinkContext.allowInsertFailures); if ( builder.diagnostics().hasError() ) { const char* errMsg = builder.diagnostics().errorMessage(); // let apps with this error fallback to dyld2 mode if ( needsDyld2ErrorMessage(errMsg) ) { - if ( dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), true) ) { + if ( canSaveClosureToDisk ) { // create empty file as a tombstone to not keep trying - int fd = ::open(closurePath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + int fd = dyld3::open(closurePath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); if ( fd != -1 ) { ::fchmod(fd, S_IRUSR); ::close(fd); @@ -6060,8 +6327,24 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma dyld::log("dyld: somehow just built closure is invalid\n"); return nullptr; } - // try to atomically save closure to disk to speed up next launch - if ( dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), true) ) { + + // write closure file but only if we have boot-token + if ( canSaveClosureToDisk ) { + if ( const dyld3::closure::LaunchClosure* existingClosure = mapClosureFile(closurePath) ) { + if ( (existingClosure->size() == result->size()) && (memcmp(existingClosure, result, result->size()) == 0) ) { + // closure file already exists and has same content, so re-use file by altering boot-token + ::chmod(closurePath, S_IRUSR|S_IWUSR); // file has to be writable to alter attributes + // handle both attribute size change and missing attribute + if ( ::setxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, XATTR_REPLACE) != 0 ) + ::setxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, 0); + ::chmod(closurePath, S_IRUSR); + result->deallocate(); + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: reusing previous boot %s closure %p (size=%lu) for %s\n", existingClosure->topImage()->variantString(), existingClosure, existingClosure->size(), sExecPath); + return existingClosure; + } + } + // make new file char closurePathTemp[PATH_MAX]; strlcpy(closurePathTemp, closurePath, PATH_MAX); int mypid = getpid(); @@ -6074,16 +6357,63 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma putHexByte(mypid, s); *s = '\0'; strlcat(closurePathTemp, pidBuf, PATH_MAX); - int fd = ::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); +#if TARGET_OS_OSX + int fd = dyld3::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); +#else + int fd = ::open_dprotected_np(closurePathTemp, O_WRONLY|O_CREAT, PROTECTION_CLASS_D, 0, S_IRUSR|S_IWUSR); +#endif 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); + auto saveClosure = [&]() -> bool { + if ( ::ftruncate(fd, result->size()) == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not ftruncate closure (errno=%d) at: %s\n", errno, closurePathTemp); + return false; + } + + ssize_t writeResult = ::write(fd, result, result->size()); + if ( writeResult == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not write closure (errno=%d) at: %s\n", errno, closurePathTemp); + return false; + } else if ( writeResult != result->size() ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not write whole closure at: %s\n", closurePathTemp); + return false; + } + + if ( ::fsetxattr(fd, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, 0) == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not fsetxattr closure (errno=%d) at: %s\n", errno, closurePathTemp); + return false; + } + + if ( ::fchmod(fd, S_IRUSR) == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not fchmod closure (errno=%d) at: %s\n", errno, closurePathTemp); + return false; + } + + if ( ::close(fd) == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not close closure (errno=%d) at: %s\n", errno, closurePathTemp); + return false; + } + + if ( ::rename(closurePathTemp, closurePath) == -1 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("could not rename closure (errno=%d) from: %s to: %s\n", errno, closurePathTemp, closurePath); + return false; + } + + return true; + }; + + if ( saveClosure() ) { + // free built closure and mmap file() to reduce dirty memory + result->deallocate(); + result = mapClosureFile(closurePath); + sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_SAVED_TO_FILE; + } } else if ( gLinkContext.verboseWarnings ) { dyld::log("could not save closure (errno=%d) to: %s\n", errno, closurePathTemp); @@ -6091,19 +6421,35 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma } if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: just built closure %p (size=%lu) for %s\n", result, result->size(), sExecPath); + dyld::log("dyld: just built %s closure %p (size=%lu) for %s\n", result->topImage()->variantString(), result, result->size(), sExecPath); return result; } static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_t* mainExecutableCDHash, const dyld3::closure::LoadedFileInfo& mainFileInfo, - const char* envp[]) + const char* envp[], + const dyld3::Array& bootToken) { + // get path to where closure file will be store for this program char closurePath[PATH_MAX]; - // build base path of $TMPDIR/dyld/- - if ( !dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), false) ) + if ( !dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, envp, false, closurePath) ) { + // if cannot construct path to use/store closure file, then use minimal closures + if ( sClosureKind == ClosureKind::unset ) + sClosureKind = ClosureKind::minimal; return nullptr; + } + + // if file exists, but extended attribute is wrong, ignore file (might be re-used later) + if ( bootToken.empty() ) + return nullptr; + uint8_t filesBootToken[bootToken.count()]; + ssize_t attrSize = ::getxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, filesBootToken, bootToken.count(), 0, 0); + if ( attrSize != bootToken.count() ) + return nullptr; + if ( memcmp(bootToken.begin(), filesBootToken, bootToken.count()) != 0 ) + return nullptr; + const dyld3::closure::LaunchClosure* closure = mapClosureFile(closurePath); if ( closure == nullptr ) return nullptr; @@ -6114,7 +6460,7 @@ static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_ } if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: used cached closure %p (size=%lu) for %s\n", closure, closure->size(), sExecPath); + dyld::log("dyld: used cached %s closure %p (size=%lu) for %s\n", closure->topImage()->variantString(), closure, closure->size(), sExecPath); return closure; } @@ -6123,7 +6469,7 @@ static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_ static ClosureMode getPlatformDefaultClosureMode() { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX #if __i386__ // rdar://problem/32701418: Don't use dyld3 for i386 for now. return ClosureMode::Off; @@ -6138,7 +6484,7 @@ static ClosureMode getPlatformDefaultClosureMode() { return ClosureMode::On; else return ClosureMode::Off; -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED +#endif // TARGET_OS_OSX } // @@ -6159,11 +6505,29 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, //Check and see if there are any kernel flags dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr)); +#if __has_feature(ptrauth_calls) + // Check and see if kernel disabled JOP pointer signing (which lets us load plain arm64 binaries) + if ( const char* disableStr = _simple_getenv(apple, "ptrauth_disabled") ) { + if ( strcmp(disableStr, "1") == 0 ) + sKeysDisabled = true; + } + else { + // needed until kernel passes ptrauth_disabled for arm64 main executables + if ( (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_V8) || (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_ALL) ) + sKeysDisabled = true; + } +#endif + // Grab the cdHash of the main executable from the environment uint8_t mainExecutableCDHashBuffer[20]; const uint8_t* mainExecutableCDHash = nullptr; - if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) ) - mainExecutableCDHash = mainExecutableCDHashBuffer; + if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) { + unsigned bufferLenUsed; + if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, sizeof(mainExecutableCDHashBuffer), bufferLenUsed) ) + mainExecutableCDHash = mainExecutableCDHashBuffer; + } + + getHostInfo(mainExecutableMH, mainExecutableSlide); #if !TARGET_OS_SIMULATOR // Trace dyld's load @@ -6179,7 +6543,8 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, // Set the platform ID in the all image infos so debuggers can tell the process type // FIXME: This can all be removed once we make the kernel handle it in rdar://43369446 - if (gProcessInfo->version >= 16) { + // The host may not have the platform field in its struct, but there's space for it in the padding, so always set it + { __block bool platformFound = false; ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) { if (platformFound) { @@ -6191,7 +6556,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, if (gProcessInfo->platform == (uint32_t)dyld3::Platform::unknown) { // There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries. // It should never occur on any of our embedded platforms. -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX gProcessInfo->platform = (uint32_t)dyld3::Platform::macOS; #else halt("MH_EXECUTE binaries must specify a minimum supported OS version"); @@ -6199,16 +6564,21 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, } } -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // Check to see if we need to override the platform const char* forcedPlatform = _simple_getenv(envp, "DYLD_FORCE_PLATFORM"); if (forcedPlatform) { - if (strncmp(forcedPlatform, "6", 1) != 0) { - halt("DYLD_FORCE_PLATFORM is only supported for platform 6"); + dyld_platform_t forcedPlatformType = 0; + if (strncmp(forcedPlatform, "6", 1) == 0) { + forcedPlatformType = PLATFORM_MACCATALYST; + } else if (strncmp(forcedPlatform, "2", 1) == 0) { + forcedPlatformType = PLATFORM_IOS; + } else { + halt("DYLD_FORCE_PLATFORM is only supported for platform 2 or 6."); } const dyld3::MachOFile* mf = (dyld3::MachOFile*)sMainExecutableMachHeader; if (mf->allowsAlternatePlatform()) { - gProcessInfo->platform = PLATFORM_IOSMAC; + gProcessInfo->platform = forcedPlatformType; } } @@ -6219,7 +6589,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, char simDyldPath[PATH_MAX]; strlcpy(simDyldPath, rootPath, PATH_MAX); strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); - int fd = my_open(simDyldPath, O_RDONLY, 0); + int fd = dyld3::open(simDyldPath, O_RDONLY, 0); if ( fd != -1 ) { const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result); if ( errMessage != NULL ) @@ -6245,7 +6615,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, // Remove interim apple[0] transition code from dyld if (!sExecPath) sExecPath = apple[0]; -#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR +#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR // kernel is not passing a real path for main executable if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) { if ( char* newPath = (char*)malloc(strlen(sExecPath)+10) ) { @@ -6276,6 +6646,17 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, else sExecShortName = sExecPath; +#if TARGET_OS_OSX && __has_feature(ptrauth_calls) + // on Apple Silicon macOS, only Apple signed ("platform binary") arm64e can be loaded + sOnlyPlatformArm64e = true; + + // internal builds, or if boot-arg is set, then non-platform-binary arm64e slices can be run + if ( const char* abiMode = _simple_getenv(apple, "arm64e_abi") ) { + if ( strcmp(abiMode, "all") == 0 ) + sOnlyPlatformArm64e = false; + } +#endif + configureProcessRestrictions(mainExecutableMH, envp); // Check if we should force dyld3. Note we have to do this outside of the regular env parsing due to AMFI @@ -6284,20 +6665,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, if ( strcmp(useClosures, "0") == 0 ) { sClosureMode = ClosureMode::Off; } else if ( strcmp(useClosures, "1") == 0 ) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED - -#if __i386__ - // don't support dyld3 for 32-bit macOS -#else - // Also don't support dyld3 for iOSMac right now - if ( gProcessInfo->platform != PLATFORM_IOSMAC ) { - sClosureMode = ClosureMode::On; - } -#endif // __i386__ - -#else + #if !__i386__ // don't support dyld3 for 32-bit macOS sClosureMode = ClosureMode::On; -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED + sClosureKind = ClosureKind::full; + #endif + } else if ( strcmp(useClosures, "2") == 0 ) { + sClosureMode = ClosureMode::On; + sClosureKind = ClosureKind::minimal; } else { dyld::warn("unknown option to DYLD_USE_CLOSURES. Valid options are: 0 and 1\n"); } @@ -6305,7 +6679,36 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, } } -#if __MAC_OS_X_VERSION_MIN_REQUIRED + // Check if we should force the shared cache __DATA_CONST to read-only or read-write + if ( dyld3::BootArgs::forceReadWriteDataConst() ) { + gEnableSharedCacheDataConst = false; + } else if ( dyld3::BootArgs::forceReadOnlyDataConst() ) { + gEnableSharedCacheDataConst = true; + } else { + // __DATA_CONST is enabled by default for arm64(e) for now +#if __arm64__ && __LP64__ + gEnableSharedCacheDataConst = true; +#else + gEnableSharedCacheDataConst = false; +#endif + } + bool sharedCacheDataConstIsEnabled = gEnableSharedCacheDataConst; + + if ( dyld3::internalInstall() ) { + if (const char* dataConst = _simple_getenv(envp, "DYLD_SHARED_REGION_DATA_CONST")) { + if ( strcmp(dataConst, "RW") == 0 ) { + gEnableSharedCacheDataConst = false; + } else if ( strcmp(dataConst, "RO") == 0 ) { + gEnableSharedCacheDataConst = true; + } else { + dyld::warn("unknown option to DYLD_SHARED_REGION_DATA_CONST. Valid options are: RW and RO\n"); + } + + } + } + + +#if TARGET_OS_OSX if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) { pruneEnvironmentVariables(envp, &apple); // set again because envp and apple may have changed or moved @@ -6317,18 +6720,25 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, checkEnvironmentVariables(envp); defaultUninitializedFallbackPaths(envp); } -#if __MAC_OS_X_VERSION_MIN_REQUIRED - if ( gProcessInfo->platform == PLATFORM_IOSMAC ) { - gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL); - gLinkContext.iOSonMac = 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; - } - else if ( ((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::driverKit) ) { - gLinkContext.driverKit = true; - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; +#if TARGET_OS_OSX + switch (gProcessInfo->platform) { +#if (TARGET_OS_OSX && TARGET_CPU_ARM64) + case PLATFORM_IOS: + sClosureMode = ClosureMode::On; // Run iOS apps on macOS in dyld3 mode + [[clang::fallthrough]]; +#endif + case PLATFORM_MACCATALYST: + gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL); + gLinkContext.iOSonMac = 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; + break; + case PLATFORM_DRIVERKIT: + gLinkContext.driverKit = true; + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + break; } #endif if ( sEnv.DYLD_PRINT_OPTS ) @@ -6341,18 +6751,11 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, #if !TARGET_OS_SIMULATOR if ( _simple_getenv(envp, "DYLD_JUST_BUILD_CLOSURE") != nullptr ) { #if TARGET_OS_IPHONE - const char* tempDir = getTempDir(envp); - if ( (tempDir != nullptr) && (geteuid() != 0) ) { - // Use realpath to prevent something like TMPRIR=/tmp/../usr/bin - char realPath[PATH_MAX]; - if ( realpath(tempDir, realPath) != NULL ) - tempDir = realPath; - if (strncmp(tempDir, "/private/var/mobile/Containers/", strlen("/private/var/mobile/Containers/")) == 0) { - sJustBuildClosure = true; - } - } + char tempClosurePath[PATH_MAX]; + if ( dyld3::closure::LaunchClosure::buildClosureCachePath(sExecPath, envp, false, tempClosurePath) ) + sJustBuildClosure = true; #endif - // If we didn't like the format of TMPDIR, just exit. We don't want to launch the app as that would bring up the UI + // If the env vars for the data contain look wrong, don't want to launch the app as that would bring up the UI if (!sJustBuildClosure) { _exit(EXIT_SUCCESS); } @@ -6361,28 +6764,113 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, if ( sJustBuildClosure ) sClosureMode = ClosureMode::On; - getHostInfo(mainExecutableMH, mainExecutableSlide); // load shared cache checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide); if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) { #if TARGET_OS_SIMULATOR if ( sSharedCacheOverrideDir) - mapSharedCache(); + mapSharedCache(mainExecutableSlide); #else - mapSharedCache(); + mapSharedCache(mainExecutableSlide); #endif + + // If this process wants a different __DATA_CONST state from the shared region, then override that now + if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (gEnableSharedCacheDataConst != sharedCacheDataConstIsEnabled) ) { + uint32_t permissions = gEnableSharedCacheDataConst ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE); + sSharedCacheLoadInfo.loadAddress->changeDataConstPermissions(mach_task_self(), permissions, + (gLinkContext.verboseMapping ? &dyld::log : nullptr)); + } } +#if !TARGET_OS_SIMULATOR + if ( getpid() == 1 ) { + // Get the value as set by the boot-args + uint64_t commPageValue = 0; + size_t commPageValueSize = sizeof(commPageValue); + if ( sysctlbyname("kern.dyld_flags", &commPageValue, &commPageValueSize, nullptr, 0) != 0 ) { + // Try again with the old name + // TODO: Remove this when we are always on new enough kernels + sysctlbyname("kern.dyld_system_flags", &commPageValue, &commPageValueSize, nullptr, 0); + } + + commPageValue &= CommPageBootArgMask; + // logToConsole("dyld: got comm page flags 0x%llx\n", commPageValue); + + // If we are PID 1 (launchd) and on macOS, then we should check if the simulator support dylibs + // are roots or not. + // If they are not roots at launchd time, and the file system is read-only, then we know for sure + // they will not be roots later +#if DYLD_SIMULATOR_ROOTS_SUPPORT + bool fileSystemIsWritable = true; + + // logToConsole("dyld: in launchd\n"); + struct statfs statBuffer; + int statResult = statfs("/", &statBuffer); + if ( statResult == 0 ) { + if ( !strcmp(statBuffer.f_fstypename, "apfs") ) { + if ( (statBuffer.f_flags & (MNT_RDONLY | MNT_SNAPSHOT)) == (MNT_RDONLY | MNT_SNAPSHOT) ) { + // logToConsole("dyld: got statfs flags 0x%llx\n", statBuffer.f_flags); + fileSystemIsWritable = false; + } + } + } else { + int error = errno; + logToConsole("dyld: could not stat '/', errno = %d\n", error); + } + + // If the file system is read-only, then we can check now whether any of the simulator support + // dylibs are roots + bool libsystemKernelIsRoot = false; + bool libsystemPlatformIsRoot = false; + bool libsystemPThreadIsRoot = false; + if ( !fileSystemIsWritable && (sSharedCacheLoadInfo.loadAddress != nullptr)) { + dyld3::closure::FileSystemPhysical fileSystem; + libsystemKernelIsRoot = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_kernel.dylib", + &fileSystem, sSharedCacheLoadInfo.loadAddress); + libsystemPlatformIsRoot = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_platform.dylib", + &fileSystem, sSharedCacheLoadInfo.loadAddress); + libsystemPThreadIsRoot = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_pthread.dylib", + &fileSystem, sSharedCacheLoadInfo.loadAddress); + } + commPageValue |= (fileSystemIsWritable ? CommPageFlags::fileSystemCanBeModified : CommPageFlags::None); + commPageValue |= (libsystemKernelIsRoot ? CommPageFlags::libsystemKernelIsRoot : CommPageFlags::None); + commPageValue |= (libsystemPlatformIsRoot ? CommPageFlags::libsystemPlatformIsRoot : CommPageFlags::None); + commPageValue |= (libsystemPThreadIsRoot ? CommPageFlags::libsystemPThreadIsRoot : CommPageFlags::None); +#endif // DYLD_SIMULATOR_ROOTS_SUPPORT + + logToConsole("dyld: setting comm page to 0x%llx\n", commPageValue); + if ( sysctlbyname("kern.dyld_flags", nullptr, 0, &commPageValue, sizeof(commPageValue)) != 0 ) { + // Try again with the old name + // TODO: Remove this when we are always on new enough kernels + sysctlbyname("kern.dyld_system_flags", nullptr, 0, &commPageValue, sizeof(commPageValue)); + } + } + +#if DYLD_SIMULATOR_ROOTS_SUPPORT + // Set the roots checker to the state from the comm page + { + uint64_t dyldFlags = *((uint64_t*)_COMM_PAGE_DYLD_SYSTEM_FLAGS); + bool fileSystemCanBeModified = dyldFlags & CommPageFlags::fileSystemCanBeModified; + bool libsystemKernelIsRoot = dyldFlags & CommPageFlags::libsystemKernelIsRoot; + bool libsystemPlatformIsRoot = dyldFlags & CommPageFlags::libsystemPlatformIsRoot; + bool libsystemPThreadIsRoot = dyldFlags & CommPageFlags::libsystemPThreadIsRoot; + sRootsChecker.setFileSystemCanBeModified(fileSystemCanBeModified); + sRootsChecker.setLibsystemKernelIsRoot(libsystemKernelIsRoot); + sRootsChecker.setLibsystemPlatformIsRoot(libsystemPlatformIsRoot); + sRootsChecker.setLibsystemPThreadIsRoot(libsystemPThreadIsRoot); + } +#endif // DYLD_SIMULATOR_ROOTS_SUPPORT + +#endif // !TARGET_OS_SIMULATOR + // If we haven't got a closure mode yet, then check the environment and cache type if ( sClosureMode == ClosureMode::Unset ) { // First test to see if we forced in dyld2 via a kernel boot-arg if ( dyld3::BootArgs::forceDyld2() ) { sClosureMode = ClosureMode::Off; -#ifndef DARLING } else if ( inDenyList(sExecPath) ) { sClosureMode = ClosureMode::Off; -#endif } else if ( sEnv.hasOverride ) { sClosureMode = ClosureMode::Off; } else if ( dyld3::BootArgs::forceDyld3() ) { @@ -6395,8 +6883,9 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, #if !TARGET_OS_SIMULATOR if ( sClosureMode == ClosureMode::Off ) { if ( gLinkContext.verboseWarnings ) - dyld::log("dyld: not using closure because of DYLD_USE_CLOSURES or -force_dyld2=1 override\n"); + dyld::log("dyld: not using closures\n"); } else { + sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE; const dyld3::closure::LaunchClosure* mainClosure = nullptr; dyld3::closure::LoadedFileInfo mainFileInfo; mainFileInfo.fileContent = mainExecutableMH; @@ -6405,7 +6894,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, mainFileInfo.sliceOffset = 0; mainFileInfo.sliceLen = -1; struct stat mainExeStatBuf; - if ( ::stat(sExecPath, &mainExeStatBuf) == 0 ) { + if ( dyld3::stat(sExecPath, &mainExeStatBuf) == 0 ) { mainFileInfo.inode = mainExeStatBuf.st_ino; mainFileInfo.mtime = mainExeStatBuf.st_mtime; } @@ -6414,27 +6903,101 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 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 ( mainClosure != nullptr ) + sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS; } // We only want to try build a closure at runtime if its an iOS third party binary, or a macOS binary from the shared cache bool allowClosureRebuilds = false; - if ( sClosureMode == ClosureMode::On ) { + if ( sJustBuildClosure ) { + // If sJustBuildClosure is set, always allow rebuilding + // In this case dyld will exit before using the closure allowClosureRebuilds = true; + } else if ( sClosureMode == ClosureMode::On ) { + // Only rebuild shared cache closures on internal + if ( mainClosure != nullptr ) { + if ( dyld3::internalInstall() ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: allowing closure build as this is an internal OS\n"); + allowClosureRebuilds = true; + } else { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: denying closure build as this is a customer OS\n"); + } + } else { + // Always allow rebuilding on-disk closures + allowClosureRebuilds = true; + } } else if ( (sClosureMode == ClosureMode::PreBuiltOnly) && (mainClosure != nullptr) ) { - allowClosureRebuilds = true; + // Only allow closure rebuilds on macOS if internal. Customers should never have out-of-date closures as roots + // aren't used there. + if ( dyld3::internalInstall() ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: allowing closure build as this is an internal OS\n"); + allowClosureRebuilds = true; + } else { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: denying closure build as this is a customer OS\n"); + } } - if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) + if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) { mainClosure = nullptr; + sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS; + } + + // On customer devices, don't allow a binary with a shared cache prebuilt closure to find one on disk + bool canUseClosureFromDisk = true; + if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (mainExecutableCDHash != nullptr) ) { + char mainExecutableCDHashStringBuffer[128] = { '\0' }; + strcat(mainExecutableCDHashStringBuffer, "/cdhash/"); + getCDHashString(mainExecutableCDHash, mainExecutableCDHashStringBuffer + strlen("/cdhash/")); + + const dyld3::closure::LaunchClosure* mainClosureByCDHash = sSharedCacheLoadInfo.loadAddress->findClosure(mainExecutableCDHashStringBuffer); + + if ( mainClosureByCDHash != nullptr ) { + if ( dyld3::internalInstall() ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: allowing closure from disk on internal OS\n"); + } else { + if ( mainClosure == nullptr ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: rejecting closure from disk on customer OS as shared cache closure was not used\n"); + } + + canUseClosureFromDisk = false; + } + } else { + // If we found a cache closure already, then we should have also found a cdHash for it + if ( mainClosure != nullptr ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: somehow cache closure was found, but cdHash is not in cache lookup table\n"); + mainClosure = nullptr; + } + } + } + + // bootToken is a concat of boot-hash kernel passes down for app and dyld's uuid + uint8_t bootTokenBufer[128]; + unsigned bootTokenBufferLen = 0; + if ( const char* bootHashStr = _simple_getenv(apple, "executable_boothash") ) { + if ( hexStringToBytes(bootHashStr, bootTokenBufer, sizeof(bootTokenBufer), bootTokenBufferLen) ) { + if ( ((dyld3::MachOFile*)&__dso_handle)->getUuid(&bootTokenBufer[bootTokenBufferLen]) ) + bootTokenBufferLen += sizeof(uuid_t); + } + } + dyld3::Array bootToken(bootTokenBufer, bootTokenBufferLen, bootTokenBufferLen); // If we didn't find a valid cache closure then try build a new one if ( (mainClosure == nullptr) && allowClosureRebuilds ) { // if forcing closures, and no closure in cache, or it is invalid, check for cached closure - if ( !sForceInvalidSharedCacheClosureFormat ) - mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp); + if ( !sForceInvalidSharedCacheClosureFormat && canUseClosureFromDisk ) + mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken); if ( mainClosure == nullptr ) { // if no cached closure found, build new one - mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp); + mainClosure = buildLaunchClosure(canUseClosureFromDisk, mainExecutableCDHash, mainFileInfo, envp, bootToken); + if ( mainClosure != nullptr ) + sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; } } @@ -6445,21 +7008,29 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, // try using launch closure if ( mainClosure != nullptr ) { CRSetCrashLogMessage("dyld3: launch started"); + if ( mainClosure->topImage()->fixupsNotEncoded() ) + sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; + Diagnostics diag; + bool closureOutOfDate; + bool recoverable; bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH, - mainExecutableSlide, argc, argv, envp, apple, &result, startGlue); - if ( !launched && allowClosureRebuilds ) { + mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable); + if ( !launched && closureOutOfDate && allowClosureRebuilds ) { // closure is out of date, build new one - mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp); + mainClosure = buildLaunchClosure(canUseClosureFromDisk, mainExecutableCDHash, mainFileInfo, envp, bootToken); if ( mainClosure != nullptr ) { + diag.clearError(); + sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; + if ( mainClosure->topImage()->fixupsNotEncoded() ) + sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; + else + sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH, - mainExecutableSlide, argc, argv, envp, apple, &result, startGlue); + mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable); } } 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 + gLinkContext.startedInitializingMainExecutable = true; if (sSkipMain) result = (uintptr_t)&fake_main; return result; @@ -6468,12 +7039,14 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, if ( gLinkContext.verboseWarnings ) { dyld::log("dyld: unable to use closure %p\n", mainClosure); } + if ( !recoverable ) + halt(diag.errorMessage()); } } } #endif // TARGET_OS_SIMULATOR // could not use closure info, launch old way - + sLaunchModeUsed = 0; // install gdb notifier @@ -6502,13 +7075,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, #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_ARM64E) + if ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) sDisableAcceleratorTables = true; #endif bool mainExcutableAlreadyRebased = false; if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) { struct stat statBuf; - if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 ) + if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 ) sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext); } @@ -6516,7 +7089,7 @@ reloadAllImages: #endif - #if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX gLinkContext.strictMachORequired = false; // be less strict about old macOS mach-o binaries ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) { @@ -6574,6 +7147,54 @@ reloadAllImages: sAllImages.reserve(INITIAL_IMAGE_COUNT); #endif +#if defined(__x86_64__) && !TARGET_OS_SIMULATOR + if (dyld::isTranslated()) { + struct dyld_all_runtime_info { + uint32_t image_count; + dyld_image_info* images; + uint32_t uuid_count; + dyld_uuid_info* uuids; + uint32_t aot_image_count; + dyld_aot_image_info* aots; + dyld_aot_shared_cache_info aot_cache_info; + }; + + dyld_all_runtime_info* runtime_info; + int ret = syscall(0x7000004, &runtime_info); + if (ret == 0) { + for (int i = 0; i < runtime_info->uuid_count; i++) { + dyld_image_info image_info = runtime_info->images[i]; + dyld_uuid_info uuid_info = runtime_info->uuids[i]; + + // add the arm64 cambria runtime to uuid info + addNonSharedCacheImageUUID(uuid_info); + + struct stat sb; + if (stat(image_info.imageFilePath, &sb) == 0) { + fsid_t fsid = {{0, 0}}; + fsobj_id_t fsobj = {0}; + ino_t inode = sb.st_ino; + fsobj.fid_objno = (uint32_t)inode; + fsobj.fid_generation = (uint32_t)(inode>>32); + fsid.val[0] = sb.st_dev; + + dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, image_info.imageFilePath, &(uuid_info.imageUUID), fsobj, fsid, image_info.imageLoadAddress); + } + } + + // add aot images to dyld_all_image_info + addAotImagesToAllAotImages(runtime_info->aot_image_count, runtime_info->aots); + + // add the arm64 cambria runtime to dyld_all_image_info + addImagesToAllImages(runtime_info->image_count, runtime_info->images); + + // set the aot shared cache info in dyld_all_image_info + dyld::gProcessInfo->aotSharedCacheBaseAddress = runtime_info->aot_cache_info.cacheBaseAddress; + memcpy(dyld::gProcessInfo->aotSharedCacheUUID, runtime_info->aot_cache_info.cacheUUID, sizeof(uuid_t)); + } + } +#endif + // Now that shared cache is loaded, setup an versioned dylib overrides #if SUPPORT_VERSIONED_PATHS checkVersionedPaths(); @@ -6632,20 +7253,24 @@ reloadAllImages: link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); image->setNeverUnloadRecursive(); } - // only INSERTED libraries can interpose - // 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(gLinkContext); + if ( gLinkContext.allowInterposing ) { + // only INSERTED libraries can interpose + // 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(gLinkContext); + } } } - // dyld should support interposition even without DYLD_INSERT_LIBRARIES - for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { - ImageLoader* image = sAllImages[i]; - if ( image->inSharedCache() ) - continue; - image->registerInterposing(gLinkContext); + if ( gLinkContext.allowInterposing ) { + // dyld should support interposition even without DYLD_INSERT_LIBRARIES + for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { + ImageLoader* image = sAllImages[i]; + if ( image->inSharedCache() ) + continue; + image->registerInterposing(gLinkContext); + } } #if SUPPORT_ACCELERATE_TABLES if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) { @@ -6697,7 +7322,7 @@ reloadAllImages: 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); + image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr); } } @@ -6724,9 +7349,9 @@ reloadAllImages: } ARIADNEDBG_CODE(220, 1); -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX if ( gLinkContext.driverKit ) { - result = (uintptr_t)sEntryOveride; + result = (uintptr_t)sEntryOverride; if ( result == 0 ) halt("no entry point registered"); *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; @@ -6749,10 +7374,6 @@ reloadAllImages: *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(); @@ -6779,7 +7400,7 @@ reloadAllImages: result = (uintptr_t)&fake_main; *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; } - + return result; } diff --git a/src/dyld2.cpp.rej b/src/dyld2.cpp.rej deleted file mode 100644 index 8f5e244..0000000 --- a/src/dyld2.cpp.rej +++ /dev/null @@ -1,1642 +0,0 @@ ---- src/dyld.cpp -+++ src/dyld2.cpp -@@ -60,79 +62,78 @@ - #include - #include - #include -+#include -+#include -+ -+#if TARGET_OS_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 -+#endif - #include - #include -- --#include -- --#ifndef CPU_SUBTYPE_ARM_V5TEJ -- #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) --#endif --#ifndef CPU_SUBTYPE_ARM_XSCALE -- #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) --#endif --#ifndef CPU_SUBTYPE_ARM_V7 -- #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) --#endif --#ifndef CPU_SUBTYPE_ARM_V7F -- #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) --#endif --#ifndef CPU_SUBTYPE_ARM_V7S -- #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) --#endif --#ifndef CPU_SUBTYPE_ARM_V7K -- #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) --#endif --#ifndef LC_DYLD_ENVIRONMENT -- #define LC_DYLD_ENVIRONMENT 0x27 -+#if __has_feature(ptrauth_calls) -+ #include - #endif - --#ifndef CPU_SUBTYPE_X86_64_H -- #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) --#endif -- --#ifndef VM_PROT_SLIDE -- #define VM_PROT_SLIDE 0x20 --#endif -+extern "C" int __fork(); - --#include -+#include - #include -+#include - --#include "mach-o/dyld_gdb.h" - --#include "dyld.h" -+#include "dyld2.h" - #include "ImageLoader.h" - #include "ImageLoaderMachO.h" - #include "dyldLibSystemInterface.h" --#if DYLD_SHARED_CACHE_SUPPORT - #include "dyld_cache_format.h" --#endif - #include "dyld_process_info_internal.h" --#include --#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" - #endif - --#if TARGET_IPHONE_SIMULATOR -+#if TARGET_OS_SIMULATOR - extern "C" void* gSyscallHelpers; - #else - #include "dyldSyscallInterface.h" - #endif - -+#include "Closure.h" -+#include "libdyldEntryVector.h" -+#include "MachOLoaded.h" -+#include "Loading.h" -+#include "DyldSharedCache.h" -+#include "SharedCacheRuntime.h" -+#include "StringUtils.h" -+#include "Tracing.h" -+#include "ClosureBuilder.h" -+#include "ClosureFileSystemPhysical.h" -+#include "FileUtils.h" -+#include "BootArgs.h" -+ -+#ifndef MH_HAS_OBJC -+ #define MH_HAS_OBJC 0x40000000 -+#endif - - // not libc header for send() syscall interface - extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); - - - // ARM and x86_64 are the only architecture that use cpu-sub-types --#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) -+#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __arm64__ || __x86_64__) && !TARGET_OS_SIMULATOR) - - #if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 -@@ -784,125 +818,124 @@ static void notifySingleFromCache(dyld_image_states state, const mach_header* mh - } - } - 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 void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[]) --{ -- unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry); -- unsigned pathsSize = 0; -- for (unsigned j=0; j < imageCount; ++j) { -- pathsSize += (strlen(infos[j].imageFilePath) + 1); -+#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 totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128; // align -- if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) { -- // Putting all image paths into one message would make buffer too big. -- // Instead split into two messages. Recurse as needed until paths fit in buffer. -- unsigned imageHalfCount = imageCount/2; -- notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos); -- notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]); -+ 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 = mach_absolute_time(); -- dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset]; -- char* const pathPoolStart = (char*)&buffer[header->stringsOffset]; -- char* pathPool = pathPoolStart; -- for (unsigned j=0; j < imageCount; ++j) { -- strcpy(pathPool, infos[j].imageFilePath); -- uint32_t len = (uint32_t)strlen(pathPool); -- bzero(entries->uuid, 16); -- const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress); -- if ( image != NULL ) { -- image->getUUID(entries->uuid); -- } --#if SUPPORT_ACCELERATE_TABLES -- else if ( sAllCacheImagesProxy != NULL ) { -- const mach_header* mh; -- const char* path; -- unsigned index; -- if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) { -- sAllCacheImagesProxy->getDylibUUID(index, entries->uuid); -- } -+ // 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); -+ } -+ } -+ 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; - } --#endif -- entries->loadAddress = (uint64_t)infos[j].imageLoadAddress; -- entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart); -- entries->pathLength = len; -- pathPool += (len +1); -- ++entries; -- } -- -- if ( sNotifyReplyPorts[portSlot] == 0 ) { -- if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) ) -- mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND); -- //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]); -- } -- //dyld::log("found port to send to\n"); -- mach_msg_header_t* h = (mach_msg_header_t*)buffer; -- h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE -- h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID; -- h->msgh_local_port = sNotifyReplyPorts[portSlot]; -- h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot]; -- h->msgh_reserved = 0; -- h->msgh_size = (mach_msg_size_t)sizeof(buffer); -- //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id); -- kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_SEND_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 100, MACH_PORT_NULL); -- //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); -- if ( sendResult == MACH_SEND_INVALID_DEST ) { -- // sender is not responding, detatch -- //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]); -- mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]); -- mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); -- dyld::gProcessInfo->notifyPorts[portSlot] = 0; -- sNotifyReplyPorts[portSlot] = 0; -- } --} -- --#define MAX_KERNEL_IMAGES_PER_CALL (100) -- --static void flushKernelNotifications(bool loading, bool force, std::array& kernelInfos, uint32_t &kernelInfoCount) { -- if ((force && kernelInfoCount != 0) || kernelInfoCount == MAX_KERNEL_IMAGES_PER_CALL) { -- if (loading) { -- task_register_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); -+ 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 { -- task_unregister_dyld_image_infos(mach_task_self(), kernelInfos.data(), kernelInfoCount); -+ sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE); - } -- kernelInfoCount = 0; - } - } - --static --void queueKernelNotification(const ImageLoader& image, bool loading, std::array& kernelInfos, uint32_t &kernelInfoCount) { -- if ( !image.inSharedCache() ) { -- ino_t inode = image.getInode(); -- image.getUUID(kernelInfos[kernelInfoCount].uuid); -- memcpy(&kernelInfos[kernelInfoCount].fsobjid, &inode, 8); -- kernelInfos[kernelInfoCount].load_addr = (uint64_t)image.machHeader(); -- // FIXME we should also be grabbing the device ID, but that is not necessary yet, -- // and requires threading it through the ImageLoader -- kernelInfos[kernelInfoCount].fsid.val[0] = 0; -- kernelInfos[kernelInfoCount].fsid.val[1] = 0; -- kernelInfoCount++; -+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) 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); - } -- flushKernelNotifications(loading, false, kernelInfos, kernelInfoCount); - } -+#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) { -- std::array kernelInfos; -- uint32_t kernelInfoCount = 0; -- queueKernelNotification(image, loading, kernelInfos, kernelInfoCount); -- flushKernelNotifications(loading, true, kernelInfos, kernelInfoCount); -+ uint32_t baseCode = loading ? DBG_DYLD_UUID_MAP_A : DBG_DYLD_UUID_UNMAP_A; -+ uuid_t uuid; -+ image.getUUID(uuid); -+ if ( image.inSharedCache() ) { -+ dyld3::kdebug_trace_dyld_image(baseCode, image.getInstallPath(), (const uuid_t *)&uuid, {0}, {{ 0, 0 }}, image.machHeader()); -+ } else { -+ fsid_t fsid = {{0, 0}}; -+ fsobj_id_t fsobj = {0}; -+ ino_t inode = image.getInode(); -+ fsobj.fid_objno = (uint32_t)inode; -+ fsobj.fid_generation = (uint32_t)(inode>>32); -+ fsid.val[0] = image.getDevice(); -+ dyld3::kdebug_trace_dyld_image(baseCode, image.getPath(), (const uuid_t *)&uuid, fsobj, fsid, image.machHeader()); -+ } - } - - static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) -@@ -949,32 +983,10 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag - // 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; -- } -- } -+ const struct mach_header* loadAddress[] = { image->machHeader() }; -+ const char* loadPath[] = { image->getPath() }; -+ notifyMonitoringDyld(true, 1, loadAddress, loadPath); - } -- - } - - -@@ -1186,32 +1237,80 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image - 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; iprocessDetachedFromSharedRegion = opts.forcePrivate; -+ dyld::gProcessInfo->sharedCacheSlide = sSharedCacheLoadInfo.slide; -+ dyld::gProcessInfo->sharedCacheBaseAddress = (unsigned long)sSharedCacheLoadInfo.loadAddress; -+ sSharedCacheLoadInfo.loadAddress->getUUID(dyld::gProcessInfo->sharedCacheUUID); -+ dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, sSharedCacheLoadInfo.path, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress); -+ } - --static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address) --{ -- if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) -- return syscall(294, start_address); -- return -1; -+//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR -+// RAM disk booting does not have shared cache yet -+// Don't make lack of a shared cache fatal in that case -+// if ( sSharedCacheLoadInfo.loadAddress == nullptr ) { -+// if ( sSharedCacheLoadInfo.errorMessage != nullptr ) -+// halt(sSharedCacheLoadInfo.errorMessage); -+// else -+// halt("error loading dyld shared cache"); -+// } -+//#endif - } - - --static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo) --{ -- const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask); -- const uintptr_t valueMask = ~deltaMask; -- const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add); -- const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; - -- uint32_t pageOffset = startOffset; -- uint32_t delta = 1; -- while ( delta != 0 ) { -- uint8_t* loc = pageContent + pageOffset; -- uintptr_t rawValue = *((uintptr_t*)loc); -- delta = (uint32_t)((rawValue & deltaMask) >> deltaShift); -- uintptr_t value = (rawValue & valueMask); -- if ( value != 0 ) { -- value += valueAdd; -- value += slideAmount; -- } -- *((uintptr_t*)loc) = value; -- //dyld::log(" pageOffset=0x%03X, loc=%p, org value=0x%08llX, new value=0x%08llX, delta=0x%X\n", pageOffset, loc, (uint64_t)rawValue, (uint64_t)value, delta); -- pageOffset += delta; -- } -+// create when NSLinkModule is called for a second time on a bundle -+ImageLoader* cloneImage(ImageLoader* image) -+{ -+ // open file (automagically closed when this function exits) -+ FileOpener file(image->getPath()); -+ -+ struct stat stat_buf; -+ if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) -+ throw "stat error"; -+ -+ dyld::LoadContext context; -+ context.useSearchPaths = false; -+ context.useFallbackPaths = false; -+ context.useLdLibraryPath = false; -+ context.implicitRPath = false; -+ context.matchByInstallName = false; -+ context.dontLoad = false; -+ context.mustBeBundle = true; -+ context.mustBeDylib = false; -+ context.canBePIE = false; -+ context.origin = NULL; -+ context.rpath = NULL; -+ return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); - } - - --static void loadAndCheckCodeSignature(int fd, uint32_t count, const shared_file_mapping_np mappings[], -- off_t codeSignatureOffset, size_t codeSignatureSize, -- const void *firstPages, size_t firstPagesSize) -+ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName) - { -- // register code signature blob for whole dyld cache -- fsignatures_t siginfo; -- siginfo.fs_file_start = 0; // cache always starts at beginning of file -- siginfo.fs_blob_start = (void*)codeSignatureOffset; -- siginfo.fs_blob_size = codeSignatureSize; -+ // if fat wrapper, find usable sub-file -+ const fat_header* memStartAsFat = (fat_header*)mem; -+ uint64_t fileOffset = 0; -+ uint64_t fileLength = len; -+ if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { -+ if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) { -+ mem = &mem[fileOffset]; -+ len = fileLength; -+ } -+ else { -+ throw "no matching architecture in universal wrapper"; -+ } -+ } - -- int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); -- // don't warn in chrooted case because mapping syscall is about to fail too -- if ( result == -1 ) { --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- throwf("code signature registration for shared cache failed with errno=%d\n", errno); --#else -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: code signature registration for shared cache failed with errno=%d\n", errno); --#endif -+ // try each loader -+ if ( isCompatibleMachO(mem, moduleName) ) { -+ ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); -+ // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list -+ if ( ! image->isBundle() ) -+ addImage(image); -+ return image; - } -- uint64_t codeSignedLength = siginfo.fs_file_start; -- for (uint32_t i = 0; i < count; ++i) { -- if ( (mappings[i].sfm_size > codeSignedLength) || (mappings[i].sfm_file_offset > (codeSignedLength - mappings[i].sfm_size)) ) -- throw "dyld shared cache mapping not covered by code signature"; -+ -+ // try other file formats here... -+ -+ // throw error about what was found -+ switch (*(uint32_t*)mem) { -+ case MH_MAGIC: -+ case MH_CIGAM: -+ case MH_MAGIC_64: -+ case MH_CIGAM_64: -+ throw "mach-o, but wrong architecture"; -+ default: -+ throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", -+ mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]); - } -- -- void *fdata = xmmap(NULL, firstPagesSize, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); -- if ( fdata == MAP_FAILED ) -- throwf("mmap() errno=%d validating first page of shared cache", errno); -- if ( memcmp(fdata, firstPages, firstPagesSize) != 0 ) -- throwf("mmap() page compare failed for shared cache"); -- munmap(fdata, firstPagesSize); - } - --static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], -- long slide, void* slideInfo, unsigned long slideInfoSize) --{ -- if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) { -- return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize); -- } - -- // remove the shared region sub-map -- vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); -+void registerAddCallback(ImageCallback func) -+{ -+ // now add to list to get notified when any more images are added -+ sAddImageCallbacks.push_back(func); - -- // notify gdb or other lurkers that this process is no longer using the shared region -- dyld::gProcessInfo->processDetachedFromSharedRegion = true; -- -- // map cache just for this process with mmap() -- const shared_file_mapping_np* const start = mappings; -- const shared_file_mapping_np* const end = &mappings[count]; -- for (const shared_file_mapping_np* p = start; p < end; ++p ) { -- void* mmapAddress = (void*)(uintptr_t)(p->sfm_address); -- size_t size = p->sfm_size; -- //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size); -- int protection = 0; -- if ( p->sfm_init_prot & VM_PROT_EXECUTE ) -- protection |= PROT_EXEC; -- if ( p->sfm_init_prot & VM_PROT_READ ) -- protection |= PROT_READ; -- if ( p->sfm_init_prot & VM_PROT_WRITE ) -- protection |= PROT_WRITE; -- off_t offset = p->sfm_file_offset; -- if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) { -- // failed to map some chunk of this shared cache file -- // clear shared region -- vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); -- // go back to not using shared region at all -- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; -- if ( gLinkContext.verboseMapping ) { -- dyld::log("dyld: shared cached region cannot be mapped at address %p with size 0x%08lX\n", -- mmapAddress, size); -- } -- // return failure -- return -1; -- } -- } -- -- // update all __DATA pages with slide info -- const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo; -- if ( slideInfoHeader->version == 2 ) { -- const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo; -- const uint32_t page_size = slideHeader->page_size; -- const uint16_t* page_starts = (uint16_t*)((long)(slideInfo) + slideHeader->page_starts_offset); -- const uint16_t* page_extras = (uint16_t*)((long)(slideInfo) + slideHeader->page_extras_offset); -- const uintptr_t dataPagesStart = mappings[1].sfm_address; -- for (int i=0; i < slideHeader->page_starts_count; ++i) { -- uint8_t* page = (uint8_t*)(long)(dataPagesStart + (page_size*i)); -- uint16_t pageEntry = page_starts[i]; -- //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, pageEntry); -- if ( pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) -- continue; -- if ( pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { -- uint16_t chainIndex = (pageEntry & 0x3FFF); -- bool done = false; -- while ( !done ) { -- uint16_t info = page_extras[chainIndex]; -- uint16_t pageStartOffset = (info & 0x3FFF)*4; -- //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset); -- rebaseChain(page, pageStartOffset, slide, slideHeader); -- done = (info & DYLD_CACHE_SLIDE_PAGE_ATTR_END); -- ++chainIndex; -- } -- } -- else { -- uint32_t pageOffset = pageEntry * 4; -- //dyld::log(" start pageOffset=0x%03X\n", pageOffset); -- rebaseChain(page, pageOffset, slide, slideHeader); -- } -+ // call callback with all existing images -+ for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { -+ ImageLoader* image = *it; -+ 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()); - } - } -- else if ( slide != 0 ) { -- const uintptr_t dataPagesStart = mappings[1].sfm_address; -- const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset); -- const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset); -- for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) { -- const uint8_t* entry = &entries[toc[i]*slideInfoHeader->entries_size]; -- const uint8_t* page = (uint8_t*)(long)(dataPagesStart + (4096*i)); -- //dyld::log("page=%p toc[%d]=%d entries=%p\n", page, i, toc[i], entry); -- for(int j=0; j < 128; ++j) { -- uint8_t b = entry[j]; -- //dyld::log(" entry[%d] = 0x%02X\n", j, b); -- if ( b != 0 ) { -- for(int k=0; k < 8; ++k) { -- if ( b & (1<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); - } - } -- -- // succesfully mapped shared cache for just this process -- gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; -- -- return 0; --} -- -- --const void* imMemorySharedCacheHeader() --{ -- return sSharedCache; --} -- --const char* getStandardSharedCacheFilePath() --{ --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME; --#else -- #if __x86_64__ -- if ( sHaswell ) { -- const char* path2 = MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H; -- struct stat statBuf; -- if ( my_stat(path2, &statBuf) == 0 ) -- return path2; -- } -- #endif -- return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME; - #endif - } - --int openSharedCacheFile() -+void registerLoadCallback(LoadImageCallback func) - { -- char path[MAXPATHLEN]; -- strlcpy(path, sSharedCacheDir, MAXPATHLEN); -- strlcat(path, "/", MAXPATHLEN); --#if __x86_64__ -- if ( sHaswell ) { -- strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, MAXPATHLEN); -- int fd = my_open(path, O_RDONLY, 0); -- if ( fd != -1 ) { -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path); -- return fd; -- } -- strlcpy(path, sSharedCacheDir, MAXPATHLEN); -- } --#endif -- strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN); --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- struct stat enableStatBuf; -- struct stat devCacheStatBuf; -- struct stat prodCacheStatBuf; -- if ( ((my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0) -- && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) -- && (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0)) -- || (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &prodCacheStatBuf) != 0)) -- strlcat(path, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, MAXPATHLEN); --#endif -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path); -- return my_open(path, O_RDONLY, 0); --} -- -+ // now add to list to get notified when any more images are added -+ sAddLoadImageCallbacks.push_back(func); - --static void getCacheBounds(uint32_t mappingsCount, const shared_file_mapping_np mappings[], uint64_t& lowAddress, uint64_t& highAddress) --{ -- lowAddress = 0; -- highAddress = 0; -- for(uint32_t i=0; i < mappingsCount; ++i) { -- if ( lowAddress == 0 ) { -- lowAddress = mappings[i].sfm_address; -- highAddress = mappings[i].sfm_address + mappings[i].sfm_size; -- } -- else { -- if ( mappings[i].sfm_address < lowAddress ) -- lowAddress = mappings[i].sfm_address; -- if ( (mappings[i].sfm_address + mappings[i].sfm_size) > highAddress ) -- highAddress = mappings[i].sfm_address + mappings[i].sfm_size; -+ // 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()); - } - } --} -- --static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[]) --{ --#if __x86_64__ -- // x86_64 has a two memory regions: -- // 256MB at 0x00007FFF70000000 -- // 1024MB at 0x00007FFF80000000 -- // Some old shared caches have r/w region after rx region, so all regions slide within 1GB range -- // Newer shared caches have r/w region based at 0x7FFF70000000 and r/o regions at 0x7FFF80000000, so each part has max slide -- if ( (mappingsCount >= 3) && (mappings[1].sfm_init_prot == (VM_PROT_READ|VM_PROT_WRITE)) && (mappings[1].sfm_address == 0x00007FFF70000000) ) { -- const uint64_t rwSize = mappings[1].sfm_size; -- const uint64_t rwSlop = 0x10000000ULL - rwSize; -- const uint64_t roSize = (mappings[2].sfm_address + mappings[2].sfm_size) - mappings[0].sfm_address; -- const uint64_t roSlop = 0x40000000ULL - roSize; -- const uint64_t space = (rwSlop < roSlop) ? rwSlop : roSlop; -- -- // choose new random slide -- long slide = (arc4random() % space) & (-4096); -- //dyld::log("rwSlop=0x%0llX, roSlop=0x%0llX\n", rwSlop, roSlop); -- //dyld::log("space=0x%0llX, slide=0x%0lX\n", space, slide); -- -- // update mappings -- for(uint32_t i=0; i < mappingsCount; ++i) { -- mappings[i].sfm_address += slide; -+#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); - } -- -- return slide; - } -- // else fall through to handle old style cache - #endif -- // get bounds of cache -- uint64_t lowAddress; -- uint64_t highAddress; -- getCacheBounds(mappingsCount, mappings, lowAddress, highAddress); -- -- // find slop space -- const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress; -- -- // choose new random slide --#if __arm__ -- // change shared cache slide for 32-bit arm to always be 16k aligned -- long slide = ((arc4random() % space) & (-16384)); --#else -- long slide = dyld_page_trunc(arc4random() % space); --#endif -- //dyld::log("slideSpace=0x%0llX\n", space); -- //dyld::log("slide=0x%0lX\n", slide); -- -- // update mappings -- for(uint32_t i=0; i < mappingsCount; ++i) { -- mappings[i].sfm_address += slide; -- } -- -- return slide; - } - --static void mapSharedCache() -+void registerBulkLoadCallback(LoadImageBulkCallback func) - { -- uint64_t cacheBaseAddress = 0; -- // quick check if a cache is already mapped into shared region -- if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { -- sSharedCache = (dyld_cache_header*)cacheBaseAddress; -- // if we don't understand the currently mapped shared cache, then ignore --#if __x86_64__ -- const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); --#else -- const char* magic = ARCH_CACHE_MAGIC; --#endif -- if ( strcmp(sSharedCache->magic, magic) != 0 ) { -- sSharedCache = NULL; -- if ( gLinkContext.verboseMapping ) { -- dyld::log("dyld: existing shared cached in memory is not compatible\n"); -- return; -- } -- } -- // check if cache file is slidable -- const dyld_cache_header* header = sSharedCache; -- if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) { -- // solve for slide by comparing loaded address to address of first region -- const uint8_t* loadedAddress = (uint8_t*)sSharedCache; -- const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset); -- const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address); -- sSharedCacheSlide = loadedAddress - preferedLoadAddress; -- dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide; -- dyld::gProcessInfo->sharedCacheBaseAddress = cacheBaseAddress; -- //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress); -- } -- // if cache has a uuid, copy it -- if ( header->mappingOffset >= 0x68 ) { -- memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); -- } -- // verbose logging -- if ( gLinkContext.verboseMapping ) { -- dyld::log("dyld: re-using existing %s shared cache mapping\n", (header->cacheType == kDyldSharedCacheTypeDevelopment ? "development" : "production")); -- } -- if (header->mappingOffset >= 0x68) { -- dyld_kernel_image_info_t kernelCacheInfo; -- memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); -- kernelCacheInfo.load_addr = (uint64_t)sSharedCache; -- kernelCacheInfo.fsobjid.fid_objno = 0; -- kernelCacheInfo.fsobjid.fid_generation = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, false); -- } -- } -- else { --#if __i386__ || __x86_64__ -- // Safe Boot should disable dyld shared cache -- // if we are in safe-boot mode and the cache was not made during this boot cycle, -- // delete the cache file -- uint32_t safeBootValue = 0; -- size_t safeBootValueSize = sizeof(safeBootValue); -- if ( (sysctlbyname("kern.safeboot", &safeBootValue, &safeBootValueSize, NULL, 0) == 0) && (safeBootValue != 0) ) { -- // user booted machine in safe-boot mode -- struct stat dyldCacheStatInfo; -- // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path -- if ( my_stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { -- struct timeval bootTimeValue; -- size_t bootTimeValueSize = sizeof(bootTimeValue); -- if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) { -- // if the cache file was created before this boot, then throw it away and let it rebuild itself -- if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) { -- ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); -- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; -- return; -- } -- } -- } -- } --#endif -- // map in shared cache to shared region -- int fd = openSharedCacheFile(); -- if ( fd != -1 ) { -- uint8_t firstPages[8192]; -- if ( ::read(fd, firstPages, 8192) == 8192 ) { -- dyld_cache_header* header = (dyld_cache_header*)firstPages; -- #if __x86_64__ -- const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); -- #else -- const char* magic = ARCH_CACHE_MAGIC; -- #endif -- if ( strcmp(header->magic, magic) == 0 ) { -- const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset]; -- const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount]; -- #if __IPHONE_OS_VERSION_MIN_REQUIRED -- if ( (header->mappingCount != 3) -- || (header->mappingOffset > 256) -- || (fileMappingsStart[0].fileOffset != 0) -- || (fileMappingsStart[0].address != SHARED_REGION_BASE) -- || ((fileMappingsStart[0].address + fileMappingsStart[0].size) > fileMappingsStart[1].address) -- || ((fileMappingsStart[1].address + fileMappingsStart[1].size) > fileMappingsStart[2].address) -- || ((fileMappingsStart[0].fileOffset + fileMappingsStart[0].size) != fileMappingsStart[1].fileOffset) -- || ((fileMappingsStart[1].fileOffset + fileMappingsStart[1].size) != fileMappingsStart[2].fileOffset) ) -- throw "dyld shared cache file is invalid"; -- #endif -- shared_file_mapping_np mappings[header->mappingCount]; -- unsigned int mappingCount = header->mappingCount; -- int readWriteMappingIndex = -1; -- int readOnlyMappingIndex = -1; -- // validate that the cache file has not been truncated -- bool goodCache = false; -- struct stat stat_buf; -- if ( fstat(fd, &stat_buf) == 0 ) { -- goodCache = true; -- int i=0; -- for (const dyld_cache_mapping_info* p = fileMappingsStart; p < fileMappingsEnd; ++p, ++i) { -- mappings[i].sfm_address = p->address; -- mappings[i].sfm_size = p->size; -- mappings[i].sfm_file_offset = p->fileOffset; -- mappings[i].sfm_max_prot = p->maxProt; -- mappings[i].sfm_init_prot = p->initProt; -- // rdar://problem/5694507 old update_dyld_shared_cache tool could make a cache file -- // that is not page aligned, but otherwise ok. -- if ( p->fileOffset+p->size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) { -- dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); -- goodCache = false; -- } -- if ( (mappings[i].sfm_init_prot & (VM_PROT_READ|VM_PROT_WRITE)) == (VM_PROT_READ|VM_PROT_WRITE) ) { -- readWriteMappingIndex = i; -- } -- if ( mappings[i].sfm_init_prot == VM_PROT_READ ) { -- readOnlyMappingIndex = i; -- } -- } -- // if shared cache is code signed, add a mapping for the code signature -- uint64_t signatureSize = header->codeSignatureSize; -- // zero size in header means signature runs to end-of-file -- if ( signatureSize == 0 ) -- signatureSize = stat_buf.st_size - header->codeSignatureOffset; -- if ( signatureSize != 0 ) { --#if __arm__ || __arm64__ -- size_t alignedSignatureSize = (signatureSize+16383) & (-16384); --#else -- size_t alignedSignatureSize = (signatureSize+4095) & (-4096); --#endif -- // validate code signature covers entire shared cache -- loadAndCheckCodeSignature(fd, mappingCount, mappings, header->codeSignatureOffset, alignedSignatureSize, firstPages, sizeof(firstPages)); -- } --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- else { -- throw "dyld shared cache file not code signed"; -- } --#endif -- } --#if __MAC_OS_X_VERSION_MIN_REQUIRED -- // sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache -- if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) { -- bool foundLibSystem = false; -- if ( my_stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { -- const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset]; -- const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount]; -- for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) { -- if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { -- foundLibSystem = true; -- break; -- } -- } -- } -- if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) { -- dyld::log("dyld: shared cached file was built against a different libSystem.dylib, ignoring cache.\n" -- "to update dyld shared cache run: 'sudo update_dyld_shared_cache' then reboot.\n"); -- goodCache = false; -- } -- } --#endif --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- { -- uint64_t lowAddress; -- uint64_t highAddress; -- getCacheBounds(mappingCount, mappings, lowAddress, highAddress); -- if ( (highAddress-lowAddress) > SHARED_REGION_SIZE ) -- throw "dyld shared cache is too big to fit in shared region"; -- } --#endif -- -- if ( goodCache && (readWriteMappingIndex == -1) ) { -- dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); -- goodCache = false; -- } -- if ( goodCache && (readOnlyMappingIndex == -1) ) { -- dyld::log("dyld: shared cached file is missing read-only mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); -- goodCache = false; -- } -- if ( goodCache ) { -- long cacheSlide = 0; -- void* slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset));; -- uint64_t slideInfoSize = header->slideInfoSize; -- // check if shared cache contains slid info -- if ( slideInfoSize != 0 ) { -- // don't slide shared cache if ASLR disabled (main executable didn't slide) -- if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) ) { -- cacheSlide = 0; -- } -- else { -- // generate random slide amount -- cacheSlide = pickCacheSlide(mappingCount, mappings); -- } -- -- slideInfo = (void*)((uint8_t*)slideInfo + cacheSlide); -- // add VM_PROT_SLIDE bit to __DATA area of cache -- mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE; -- mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE; -- } -- if ( gLinkContext.verboseMapping ) { -- dyld::log("dyld: calling _shared_region_map_and_slide_np() with regions:\n"); -- for (int i=0; i < mappingCount; ++i) { -- dyld::log(" address=0x%08llX, size=0x%08llX, fileOffset=0x%08llX\n", mappings[i].sfm_address, mappings[i].sfm_size, mappings[i].sfm_file_offset); -- } -- } -- -- if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, cacheSlide, slideInfo, slideInfoSize) == 0) { -- // successfully mapped cache into shared region -- sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; -- sSharedCacheSlide = cacheSlide; -- dyld::gProcessInfo->sharedCacheSlide = cacheSlide; -- dyld::gProcessInfo->sharedCacheBaseAddress = mappings[0].sfm_address; -- //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide); -- // if cache has a uuid, copy it -- if ( header->mappingOffset >= 0x68 ) { -- const bool privateSharedCache = gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion; -- memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); -- dyld_kernel_image_info_t kernelCacheInfo; -- memcpy(&kernelCacheInfo.uuid[0], &sSharedCache->uuid[0], sizeof(uuid_t)); -- kernelCacheInfo.load_addr = (uint64_t)sSharedCache; -- kernelCacheInfo.fsobjid.fid_objno = 0; -- kernelCacheInfo.fsobjid.fid_generation = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- if (privateSharedCache) { -- kernelCacheInfo.fsobjid = *(fsobj_id_t*)(&stat_buf.st_ino); -- struct statfs statfs_buf; -- if ( fstatfs(fd, &statfs_buf) == 0 ) { -- kernelCacheInfo.fsid = statfs_buf.f_fsid; -- } -- } -- task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, false, privateSharedCache); -- } -- } -- else { --#if __IPHONE_OS_VERSION_MIN_REQUIRED -- throwf("dyld shared cache could not be mapped. errno=%d, slide=0x%08lX, slideInfo=%p, slideInfoSize=0x%08llX, mappingCount=%u, " -- "address/size/off/init/max [0]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [1]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X, [2]=0x%0llX/0x%0llX/0x%0llX/0x%02X/0x%02X", -- errno, cacheSlide, slideInfo, slideInfoSize, mappingCount, -- mappings[0].sfm_address, mappings[0].sfm_size, mappings[0].sfm_file_offset, mappings[0].sfm_init_prot, mappings[0].sfm_max_prot, -- mappings[1].sfm_address, mappings[1].sfm_size, mappings[1].sfm_file_offset, mappings[1].sfm_init_prot, mappings[1].sfm_max_prot, -- mappings[2].sfm_address, mappings[2].sfm_size, mappings[2].sfm_file_offset, mappings[2].sfm_init_prot, mappings[2].sfm_max_prot); --#endif -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: shared cached file could not be mapped\n"); -- } -- } -- } -- else { -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: shared cached file is invalid\n"); -- } -- } -- else { -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: shared cached file cannot be read\n"); -- } -- close(fd); -- } -- else { -- if ( gLinkContext.verboseMapping ) -- dyld::log("dyld: shared cached file cannot be opened\n"); -- } -- } -- -- // remember if dyld loaded at same address as when cache built -- if ( sSharedCache != NULL ) { -- gLinkContext.dyldLoadedAtSameAddressNeededBySharedCache = ((uintptr_t)(sSharedCache->dyldBaseAddress) == (uintptr_t)&_mh_dylinker_header); -- } -- -- // tell gdb where the shared cache is -- if ( sSharedCache != NULL ) { -- const dyld_cache_mapping_info* const start = (dyld_cache_mapping_info*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset); -- dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount; -- // only room to tell gdb about first four regions -- if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 ) -- dyld_shared_cache_ranges.sharedRegionsCount = 4; -- const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; -- int index = 0; -- for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) { -- dyld_shared_cache_ranges.ranges[index].start = p->address+sSharedCacheSlide; -- dyld_shared_cache_ranges.ranges[index].length = p->size; -- if ( gLinkContext.verboseMapping ) { -- dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n", -- p->address+sSharedCacheSlide, p->address+sSharedCacheSlide+p->size-1, -- ((p->initProt & VM_PROT_READ) ? "read " : ""), -- ((p->initProt & VM_PROT_WRITE) ? "write " : ""), -- ((p->initProt & VM_PROT_EXECUTE) ? "execute " : ""), p->initProt, p->maxProt); -- } -- #if __i386__ -- // If a non-writable and executable region is found in the R/W shared region, then this is __IMPORT segments -- // This is an old cache. Make writable. dyld no longer supports turn W on and off as it binds -- if ( (p->initProt == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->address & 0xF0000000) == 0xA0000000) ) { -- if ( p->size != 0 ) { -- vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ | VM_PROT_WRITE; -- vm_protect(mach_task_self(), p->address, p->size, false, prot); -- if ( gLinkContext.verboseMapping ) { -- dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->address, -- p->address+p->size-1, -- (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' ); -- } -- } -- } -- #endif -- } -- if ( gLinkContext.verboseMapping ) { -- // list the code blob -- dyld_cache_header* header = (dyld_cache_header*)sSharedCache; -- uint64_t signatureSize = header->codeSignatureSize; -- // zero size in header means signature runs to end-of-file -- if ( signatureSize == 0 ) { -- struct stat stat_buf; -- // FIXME: need size of cache file actually used -- if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) -- signatureSize = stat_buf.st_size - header->codeSignatureOffset; -- } -- if ( signatureSize != 0 ) { -- const dyld_cache_mapping_info* const last = &start[dyld_shared_cache_ranges.sharedRegionsCount-1]; -- uint64_t codeBlobStart = last->address + last->size; -- dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize); -- } -- } -- #if SUPPORT_ACCELERATE_TABLES -- if ( !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCache->mappingOffset > 0x80) && (sSharedCache->accelerateInfoAddr != 0) ) { -- sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(sSharedCache, sSharedCacheSlide, gLinkContext); -- } -- #endif -- } --} --#endif // #if DYLD_SHARED_CACHE_SUPPORT -- -- -- --// create when NSLinkModule is called for a second time on a bundle --ImageLoader* cloneImage(ImageLoader* image) --{ -- // open file (automagically closed when this function exits) -- FileOpener file(image->getPath()); -- -- struct stat stat_buf; -- if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) -- throw "stat error"; -- -- dyld::LoadContext context; -- context.useSearchPaths = false; -- context.useFallbackPaths = false; -- context.useLdLibraryPath = false; -- context.implicitRPath = false; -- context.matchByInstallName = false; -- context.dontLoad = false; -- context.mustBeBundle = true; -- context.mustBeDylib = false; -- context.canBePIE = false; -- context.origin = NULL; -- context.rpath = NULL; -- return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); --} -- -- --ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName) --{ -- // if fat wrapper, find usable sub-file -- const fat_header* memStartAsFat = (fat_header*)mem; -- uint64_t fileOffset = 0; -- uint64_t fileLength = len; -- if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { -- if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) { -- mem = &mem[fileOffset]; -- len = fileLength; -- } -- else { -- throw "no matching architecture in universal wrapper"; -+ // call callback with all existing images -+ unsigned count = dyld::gProcessInfo->infoArrayCount; -+ const dyld_image_info* infoArray = dyld::gProcessInfo->infoArray; -+ if ( infoArray != NULL ) { -+ const mach_header* mhs[count]; -+ const char* paths[count]; -+ for (unsigned i=0; i < count; ++i) { -+ mhs[i] = infoArray[i].imageLoadAddress; -+ paths[i] = infoArray[i].imageFilePath; - } -+ dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)mhs[0], (uint64_t)func, 0); -+ func(count, mhs, paths); - } - -- // try each loader -- if ( isCompatibleMachO(mem, moduleName) ) { -- ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); -- // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list -- if ( ! image->isBundle() ) -- addImage(image); -- return image; -- } -- -- // try other file formats here... -- -- // throw error about what was found -- switch (*(uint32_t*)mem) { -- case MH_MAGIC: -- case MH_CIGAM: -- case MH_MAGIC_64: -- case MH_CIGAM_64: -- throw "mach-o, but wrong architecture"; -- default: -- throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", -- mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]); -- } --} -- -- --void registerAddCallback(ImageCallback func) --{ - // now add to list to get notified when any more images are added -- sAddImageCallbacks.push_back(func); -- -- // call callback with all existing images -- for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { -- ImageLoader* image = *it; -- if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) -- (*func)(image->machHeader(), image->getSlide()); -- } --#if SUPPORT_ACCELERATE_TABLES -- if ( sAllCacheImagesProxy != NULL ) { -- dyld_image_info infos[allImagesCount()+1]; -- unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos); -- for (unsigned i=0; i < cacheCount; ++i) { -- (*func)(infos[i].imageLoadAddress, sSharedCacheSlide); -- } -- } --#endif -+ sAddBulkLoadImageCallbacks.push_back(func); - } - - void registerRemoveCallback(ImageCallback func) -@@ -5626,75 +5386,81 @@ static void loadInsertedDylib(const char* path) - } - - --// --// Sets: --// sEnvMode --// gLinkContext.requireCodeSignature --// gLinkContext.processIsRestricted // Mac OS X only --// gLinkContext.processUsingLibraryValidation // Mac OS X only --// --static void configureProcessRestrictions(const macho_header* mainExecutableMH) -+static void configureProcessRestrictions(const macho_header* mainExecutableMH, const char* envp[]) - { -- uint32_t flags; --#if TARGET_IPHONE_SIMULATOR -- sEnvMode = envAll; -- gLinkContext.requireCodeSignature = true; -+ uint64_t amfiInputFlags = 0; -+#if TARGET_OS_SIMULATOR -+ 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; -- 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; -+ const char* amfiFake = nullptr; -+ if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) { -+ amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE"); -+ } -+ if ( amfiFake != nullptr ) { -+ amfiOutputFlags = hexToUInt64(amfiFake, nullptr); -+ } -+ if ( (amfiFake != nullptr) || (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; -- } -- if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { -- // On OS X CS_RESTRICT means the program was signed with entitlements -- if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) { -- 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; -- } -- } -+ 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 -+ } - } - -+// called by _dyld_register_driverkit_main() -+void setMainEntry(void (*main)()) -+{ -+ if ( sEntryOveride == nullptr ) -+ sEntryOveride = main; -+ else -+ halt("_dyld_register_driverkit_main() may only be called once"); -+} - - bool processIsRestricted() - { - #if __MAC_OS_X_VERSION_MIN_REQUIRED -- return gLinkContext.processIsRestricted; -+ return !gLinkContext.allowEnvVarsPath; - #else - return false; - #endif -@@ -5742,14 +5516,11 @@ void notifyKernelAboutDyld() - case LC_UUID: { - // Add dyld to the kernel image info - uuid_command* uc = (uuid_command*)cmd; -- dyld_kernel_image_info_t kernelInfo; -- memcpy(kernelInfo.uuid, uc->uuid, 16); -- kernelInfo.load_addr = (uint64_t)mh; -- kernelInfo.fsobjid.fid_objno = 0; -- kernelInfo.fsobjid.fid_generation = 0; -- kernelInfo.fsid.val[0] = 0; -- kernelInfo.fsid.val[1] = 0; -- task_register_dyld_image_infos(mach_task_self(), &kernelInfo, 1); -+ char path[MAXPATHLEN]; -+ if (fsgetpath(path, MAXPATHLEN, &fsid, fsobj_id_scalar) < 0) { -+ path[0] = 0; -+ } -+ dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, path, (const uuid_t *)&uc->uuid[0], fsobj_id, fsid, (const mach_header *)mh); - return; - } - } -@@ -5812,12 +5583,28 @@ static SyscallHelpers sSysCalls = { - // Added in version 6 - &abort_with_payload, - // Added in version 7 -- &task_register_dyld_image_infos, -- &task_unregister_dyld_image_infos, -- &task_get_dyld_image_infos, -- &task_register_dyld_shared_cache_image_info, -- &task_register_dyld_set_dyld_state, -- &task_register_dyld_get_process_state -+ &legacy_task_register_dyld_image_infos, -+ &legacy_task_unregister_dyld_image_infos, -+ &legacy_task_get_dyld_image_infos, -+ &legacy_task_register_dyld_shared_cache_image_info, -+ &legacy_task_register_dyld_set_dyld_state, -+ &legacy_task_register_dyld_get_process_state, -+ // Added in version 8 -+ &task_info, -+ &thread_info, -+ &kdebug_is_enabled, -+ &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)) -@@ -7100,32 +7806,6 @@ reloadAllImages: - #endif - - -- #if __MAC_OS_X_VERSION_MIN_REQUIRED -- // be less strict about old mach-o binaries -- uint32_t mainSDK = sMainExecutable->sdkVersion(); -- gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation; -- #else -- // simulators, iOS, tvOS, and watchOS are always strict -- gLinkContext.strictMachORequired = true; -- #endif -- -- // load shared cache -- checkSharedRegionDisable(); -- #if DYLD_SHARED_CACHE_SUPPORT -- if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) { -- mapSharedCache(); -- } else { -- dyld_kernel_image_info_t kernelCacheInfo; -- bzero(&kernelCacheInfo.uuid[0], sizeof(uuid_t)); -- kernelCacheInfo.load_addr = 0; -- kernelCacheInfo.fsobjid.fid_objno = 0; -- kernelCacheInfo.fsobjid.fid_generation = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- kernelCacheInfo.fsid.val[0] = 0; -- task_register_dyld_shared_cache_image_info(mach_task_self(), kernelCacheInfo, true, false); -- } -- #endif -- - #if SUPPORT_ACCELERATE_TABLES - sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT); - #else diff --git a/src/dyld2.h b/src/dyld2.h index cf82ca6..8c76393 100644 --- a/src/dyld2.h +++ b/src/dyld2.h @@ -123,8 +123,6 @@ namespace dyld { extern bool processIsRestricted(); extern const char* getStandardSharedCacheFilePath(); extern bool hasInsertedOrInterposingLibraries(); - extern int my_stat(const char* path, struct stat* buf); - extern int my_open(const char* path, int flag, int other); bool sandboxBlockedOpen(const char* path); bool sandboxBlockedMmap(const char* path); bool sandboxBlockedStat(const char* path); @@ -141,4 +139,7 @@ namespace dyld { bool isPathInCache(const char* path); const char* getPathFromIndex(unsigned cacheIndex); #endif +#if defined(__x86_64__) + bool isTranslated(); +#endif } diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index 2e074d6..adb0000 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.cpp @@ -109,10 +109,10 @@ 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 -#else +#if TARGET_OS_OSX #define DEPRECATED_APIS_SUPPORTED 1 +#else + #define DEPRECATED_APIS_SUPPORTED 0 #endif static bool sDynamicInterposing = false; @@ -123,12 +123,6 @@ static NSLinkEditErrors sLastErrorFileCode; static int sLastErrorNo; #endif -#ifdef DARLING -extern "C" int mach_driver_get_dyld_fd(void); -extern "C" void* elfcalls_get_pointer(void); -extern "C" void mach_driver_set_dyld_fd(int fd); -#endif - // In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib // Beginning in 10.4 the NSObjectFileImage API's are implemented in dyld and libSystem just forwards // This conditional keeps support for old libSystem's which needed some help implementing the API's @@ -158,7 +152,6 @@ static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* i #if DEPRECATED_APIS_SUPPORTED #endif - static void unimplemented() { dyld::halt("unimplemented dyld function\n"); @@ -186,6 +179,7 @@ static const struct dyld_func dyld_funcs[] = { {"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide }, {"__dyld_get_image_name", (void*)_dyld_get_image_name }, {"__dyld_get_image_slide", (void*)_dyld_get_image_slide }, + {"__dyld_get_prog_image_header", (void*)_dyld_get_prog_image_header }, {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, // SPIs @@ -214,7 +208,7 @@ static const struct dyld_func dyld_funcs[] = { {"__dyld_register_for_image_loads", (void*)_dyld_register_for_image_loads }, {"__dyld_register_for_bulk_image_loads", (void*)_dyld_register_for_bulk_image_loads }, {"__dyld_register_driverkit_main", (void*)_dyld_register_driverkit_main }, - + {"__dyld_halt", (void*)dyld::halt }, #if DEPRECATED_APIS_SUPPORTED #pragma clang diagnostic push @@ -260,9 +254,6 @@ static const struct dyld_func dyld_funcs[] = { #if OLD_LIBSYSTEM_SUPPORT {"__dyld_link_module", (void*)_dyld_link_module }, #endif -#ifdef DARLING - {"__dyld_get_elfcalls", (void*)elfcalls_get_pointer }, -#endif #pragma clang diagnostic pop #endif //DEPRECATED_APIS_SUPPORTED @@ -400,6 +391,14 @@ const char* _dyld_get_image_name(uint32_t image_index) return allImagesIndexedPath(image_index); } +const struct mach_header* _dyld_get_prog_image_header() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + + return dyld::mainExecutable()->machHeader(); +} + static const void *stripPointer(const void *ptr) { #if __has_feature(ptrauth_calls) return __builtin_ptrauth_strip(ptr, ptrauth_key_asia); @@ -785,6 +784,19 @@ void* NSAddressOfSymbol(NSSymbol symbol) ImageLoader* image = dyld::findImageContainingSymbol(symbol); if ( image != NULL ) result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); + +#if __has_feature(ptrauth_calls) + // Sign the pointer if it points to a function + if ( result ) { + 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 return result; } @@ -1381,7 +1393,7 @@ bool dlopen_preflight_internal(const char* path, void* callerAddress) const bool leafName = (strchr(path, '/') == NULL); const bool absolutePath = (path[0] == '/'); -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE char canonicalPath[PATH_MAX]; // dlopen() not opening frameworks from shared cache with // or ./ in path if ( !leafName ) { @@ -1412,7 +1424,7 @@ bool dlopen_preflight_internal(const char* path, void* callerAddress) if ( dyld::inSharedCache(path) ) return true; -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_OS_OSX // dlopen_preflight() on symlink to image in shared cache leaves it half loaded if ( strncmp(path, "/System/Library/", 16) == 0 ) { char canonicalPath[PATH_MAX]; @@ -1527,7 +1539,7 @@ void* dlopen_internal(const char* path, int mode, void* callerAddress) void* result = NULL; const bool leafName = (strchr(path, '/') == NULL); const bool absolutePath = (path[0] == '/'); -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if TARGET_OS_IPHONE char canonicalPath[PATH_MAX]; // dlopen() not opening frameworks from shared cache with // or ./ in path if ( !leafName ) { @@ -1840,7 +1852,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) // 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_ARM64E) ) { + if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { const ImageLoader* symbolImage = image; if (!symbolImage->containsAddress(result)) { symbolImage = dyld::findImageContainingAddress(result); @@ -1874,7 +1886,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) // 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_ARM64E) ) { + if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { const ImageLoader* symbolImage = image; if (!symbolImage->containsAddress(result)) { symbolImage = dyld::findImageContainingAddress(result); @@ -1920,7 +1932,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) // 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_ARM64E) ) { + if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { const ImageLoader* symbolImage = image; if (!symbolImage->containsAddress(result)) { symbolImage = dyld::findImageContainingAddress(result); @@ -1965,7 +1977,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) // 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_ARM64E) ) { + if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { const ImageLoader* symbolImage = image; if (!symbolImage->containsAddress(result)) { symbolImage = dyld::findImageContainingAddress(result); @@ -2016,7 +2028,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) // 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_ARM64E) ) { + if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) { const ImageLoader* symbolImage = image; if (!symbolImage->containsAddress(result)) { symbolImage = dyld::findImageContainingAddress(result); @@ -2134,6 +2146,9 @@ void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_inte ImageLoader* image = dyld::findImageByMachHeader(mh); if ( image == NULL ) return; + + // make the cache writable for this block + DyldSharedCache::DataConstScopedWriter patcher(dyld::gLinkContext.dyldCache, mach_task_self(), (dyld::gLinkContext.verboseMapping ? &dyld::log : nullptr)); // make pass at bound references in this image and update them dyld::gLinkContext.dynamicInterposeArray = array; @@ -2204,7 +2219,8 @@ const void* _dyld_get_shared_cache_range(size_t* length) const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader(); if ( cache != nullptr ) { const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset); - *length = (size_t)((mappings[2].address + mappings[2].size) - mappings[0].address); + const dyld_cache_mapping_info* lastMapping = &mappings[cache->header.mappingCount - 1]; + *length = (size_t)((lastMapping->address + lastMapping->size) - cache->unslidLoadAddress()); return cache; } return nullptr; diff --git a/src/dyldAPIs.cpp.orig b/src/dyldAPIs.cpp.orig deleted file mode 100644 index 9edcde1..0000000 --- a/src/dyldAPIs.cpp.orig +++ /dev/null @@ -1,2044 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-2009 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -// -// This file implements that API's in -// -// - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -#include -#include -#include -#include // for task_self_trap() - - -#include "mach-o/dyld_images.h" -#include "mach-o/dyld.h" -#include "mach-o/dyld_priv.h" - -#include "ImageLoader.h" -#include "ImageLoaderMachO.h" -#include "dyld.h" -#include "dyldLibSystemInterface.h" - -#undef _POSIX_C_SOURCE -#include "dlfcn.h" - -// from dyldExceptions.c -extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key); - -// from dyld_gdb.cpp -extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); -extern uint32_t allImagesCount(); -extern const mach_header* allImagesIndexedMachHeader(uint32_t index); -extern const char* allImagesIndexedPath(uint32_t index); - - -// deprecated APIs are still availble on Mac OS X, but not on iPhone OS -#if __IPHONE_OS_VERSION_MIN_REQUIRED - #define DEPRECATED_APIS_SUPPORTED 0 -#else - #define DEPRECATED_APIS_SUPPORTED 1 -#endif - -static bool sDynamicInterposing = false; - -#if DEPRECATED_APIS_SUPPORTED -static char sLastErrorFilePath[1024]; -static NSLinkEditErrors sLastErrorFileCode; -static int sLastErrorNo; -#endif - -#ifdef DARLING -extern "C" int mach_driver_get_dyld_fd(void); -extern "C" bool darling_am_i_ptraced(void); -#endif - -// In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib -// Beginning in 10.4 the NSObjectFileImage API's are implemented in dyld and libSystem just forwards -// This conditional keeps support for old libSystem's which needed some help implementing the API's -#define OLD_LIBSYSTEM_SUPPORT (__i386__) - -// The following functions have no prototype in any header. They are special cases -// where _dyld_func_lookup() is used directly. -static void _dyld_make_delayed_module_initializer_calls(); -static void registerThreadHelpers(const dyld::LibSystemHelpers*); -#if DEPRECATED_APIS_SUPPORTED -static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit); -#if OLD_LIBSYSTEM_SUPPORT -static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options); -#endif -static void _dyld_register_binding_handler(void * (*)(const char *, const char *, void *), ImageLoader::BindingOptions); -static bool NSMakePrivateModulePublic(NSModule module); -static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header); - -// The following functions are dyld API's, but since dyld links with a static copy of libc.a -// the public name cannot be used. -static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module); -static bool client_NSIsSymbolNameDefined(const char* symbolName); -#endif // DEPRECATED_APIS_SUPPORTED -#if SUPPORT_ZERO_COST_EXCEPTIONS -static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info); -#endif -#if DEPRECATED_APIS_SUPPORTED -#endif - - -static void unimplemented() -{ - dyld::halt("unimplemented dyld function\n"); -} - -struct dyld_func { - const char* name; - void* implementation; -}; - -static struct dyld_func dyld_funcs[] = { - {"__dyld_register_func_for_add_image", (void*)_dyld_register_func_for_add_image }, - {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image }, - {"__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_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_get_image_name", (void*)_dyld_get_image_name }, - {"__dyld_get_image_slide", (void*)_dyld_get_image_slide }, - {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, - - // SPIs - {"__dyld_register_thread_helpers", (void*)registerThreadHelpers }, - {"__dyld_fork_child", (void*)_dyld_fork_child }, - {"__dyld_make_delayed_module_initializer_calls", (void*)_dyld_make_delayed_module_initializer_calls }, - {"__dyld_get_all_image_infos", (void*)_dyld_get_all_image_infos }, -#if SUPPORT_ZERO_COST_EXCEPTIONS - {"__dyld_find_unwind_sections", (void*)client_dyld_find_unwind_sections }, -#endif -#if __i386__ || __x86_64__ || __arm__ || __arm64__ - {"__dyld_fast_stub_entry", (void*)dyld::fastBindLazySymbol }, -#endif - {"__dyld_image_path_containing_address", (void*)dyld_image_path_containing_address }, - {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, - {"__dyld_process_is_restricted", (void*)dyld::processIsRestricted }, - {"__dyld_dynamic_interpose", (void*)dyld_dynamic_interpose }, -#if DYLD_SHARED_CACHE_SUPPORT - {"__dyld_shared_cache_file_path", (void*)dyld::getStandardSharedCacheFilePath }, -#endif - {"__dyld_get_image_header_containing_address", (void*)dyld_image_header_containing_address }, - {"__dyld_is_memory_immutable", (void*)_dyld_is_memory_immutable }, - {"__dyld_objc_notify_register", (void*)_dyld_objc_notify_register }, - {"__dyld_get_shared_cache_uuid", (void*)_dyld_get_shared_cache_uuid }, - - - // deprecated -#if DEPRECATED_APIS_SUPPORTED - {"__dyld_lookup_and_bind", (void*)client_dyld_lookup_and_bind }, - {"__dyld_lookup_and_bind_with_hint", (void*)_dyld_lookup_and_bind_with_hint }, - {"__dyld_lookup_and_bind_fully", (void*)_dyld_lookup_and_bind_fully }, - {"__dyld_install_handlers", (void*)_dyld_install_handlers }, - {"__dyld_link_edit_error", (void*)NSLinkEditError }, - {"__dyld_unlink_module", (void*)NSUnLinkModule }, - {"__dyld_bind_objc_module", (void*)_dyld_bind_objc_module }, - {"__dyld_bind_fully_image_containing_address", (void*)_dyld_bind_fully_image_containing_address }, - {"__dyld_image_containing_address", (void*)_dyld_image_containing_address }, - {"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler }, - {"__dyld_NSNameOfSymbol", (void*)NSNameOfSymbol }, - {"__dyld_NSAddressOfSymbol", (void*)NSAddressOfSymbol }, - {"__dyld_NSModuleForSymbol", (void*)NSModuleForSymbol }, - {"__dyld_NSLookupAndBindSymbol", (void*)NSLookupAndBindSymbol }, - {"__dyld_NSLookupAndBindSymbolWithHint", (void*)NSLookupAndBindSymbolWithHint }, - {"__dyld_NSLookupSymbolInModule", (void*)NSLookupSymbolInModule}, - {"__dyld_NSLookupSymbolInImage", (void*)NSLookupSymbolInImage}, - {"__dyld_NSMakePrivateModulePublic", (void*)NSMakePrivateModulePublic}, - {"__dyld_NSIsSymbolNameDefined", (void*)client_NSIsSymbolNameDefined}, - {"__dyld_NSIsSymbolNameDefinedWithHint", (void*)NSIsSymbolNameDefinedWithHint }, - {"__dyld_NSIsSymbolNameDefinedInImage", (void*)NSIsSymbolNameDefinedInImage}, - {"__dyld_NSNameOfModule", (void*)NSNameOfModule }, - {"__dyld_NSLibraryNameForModule", (void*)NSLibraryNameForModule }, - {"__dyld_NSAddLibrary", (void*)NSAddLibrary }, - {"__dyld_NSAddLibraryWithSearching", (void*)NSAddLibraryWithSearching }, - {"__dyld_NSAddImage", (void*)NSAddImage }, - {"__dyld_launched_prebound", (void*)_dyld_launched_prebound }, - {"__dyld_all_twolevel_modules_prebound", (void*)_dyld_all_twolevel_modules_prebound }, - {"__dyld_call_module_initializers_for_dylib", (void*)_dyld_call_module_initializers_for_dylib }, - {"__dyld_NSCreateObjectFileImageFromFile", (void*)NSCreateObjectFileImageFromFile }, - {"__dyld_NSCreateObjectFileImageFromMemory", (void*)NSCreateObjectFileImageFromMemory }, - {"__dyld_NSDestroyObjectFileImage", (void*)NSDestroyObjectFileImage }, - {"__dyld_NSLinkModule", (void*)NSLinkModule }, - {"__dyld_NSHasModInitObjectFileImage", (void*)NSHasModInitObjectFileImage }, - {"__dyld_NSSymbolDefinitionCountInObjectFileImage", (void*)NSSymbolDefinitionCountInObjectFileImage }, - {"__dyld_NSSymbolDefinitionNameInObjectFileImage", (void*)NSSymbolDefinitionNameInObjectFileImage }, - {"__dyld_NSIsSymbolDefinedInObjectFileImage", (void*)NSIsSymbolDefinedInObjectFileImage }, - {"__dyld_NSSymbolReferenceNameInObjectFileImage", (void*)NSSymbolReferenceNameInObjectFileImage }, - {"__dyld_NSSymbolReferenceCountInObjectFileImage", (void*)NSSymbolReferenceCountInObjectFileImage }, - {"__dyld_NSGetSectionDataInObjectFileImage", (void*)NSGetSectionDataInObjectFileImage }, -#if OLD_LIBSYSTEM_SUPPORT - {"__dyld_link_module", (void*)_dyld_link_module }, -#endif -#ifdef DARLING - {"__dyld_get_mach_driver_fd", (void*)mach_driver_get_dyld_fd }, - {"__dyld_am_i_ptraced", (void*)darling_am_i_ptraced }, -#endif -#endif //DEPRECATED_APIS_SUPPORTED - - {NULL, 0} -}; - - - -#if DEPRECATED_APIS_SUPPORTED - -static void dyldAPIhalt(const char* apiName, const char* errorMsg) -{ - dyld::log("dyld: %s() error\n", apiName); - dyld::halt(errorMsg); -} - -// dyld's abstract type NSSymbol is implemented as const ImageLoader::Symbol* -inline NSSymbol SymbolToNSSymbol(const ImageLoader::Symbol* sym) -{ - return (NSSymbol)sym; -} -inline const ImageLoader::Symbol* NSSymbolToSymbol(NSSymbol sym) -{ - return (const ImageLoader::Symbol*)sym; -} - -// dyld's abstract type NSModule is implemented as ImageLoader* -inline NSModule ImageLoaderToNSModule(const ImageLoader* image) -{ - return (NSModule)image; -} -inline ImageLoader* NSModuleToImageLoader(NSModule module) -{ - ImageLoader* image = (ImageLoader*)module; - if ( dyld::validImage(image) ) - return image; - return NULL; -} - -// actual definition for opaque type -struct __NSObjectFileImage -{ - ImageLoader* image; - const void* imageBaseAddress; // not used with OFI created from files - size_t imageLength; // not used with OFI created from files -}; - - -VECTOR_NEVER_DESTRUCTED(NSObjectFileImage); -static std::vector sObjectFileImages; - - - -// -// __NSObjectFileImage are deleted in NSDestroyObjectFileImage() -// The contained image is delete in one of two places: -// NSUnLinkModule deletes the image if there is no __NSObjectFileImage with a reference to it -// NSDestroyObjectFileImage deletes the image if image is not in list of valid images -// - - - -static void setLastError(NSLinkEditErrors code, int errnum, const char* file, const char* message) -{ - dyld::setErrorMessage(message); - strncpy(sLastErrorFilePath, file, 1024); - sLastErrorFilePath[1023] = '\0'; - sLastErrorFileCode = code; - sLastErrorNo = errnum; -} - -#endif // DEPRECATED_APIS_SUPPORTED - -/* - *_dyld_NSGetExecutablePath is the dyld side of _NSGetExecutablePath which - * copies the path of the executable into the buffer and returns 0 if the path - * was successfully copied in the provided buffer. If the buffer is not large - * enough, -1 is returned and the expected buffer size is copied in *bufsize. - * Note that _NSGetExecutablePath will return "a path" to the executable not a - * "real path" to the executable. That is the path may be a symbolic link and - * not the real file. And with deep directories the total bufsize needed could - * be more than MAXPATHLEN. - */ -int _NSGetExecutablePath(char* buf, uint32_t *bufsize) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(...)\n", __func__); - const char* exePath = dyld::getExecutablePath(); - if(*bufsize < strlen(exePath) + 1){ - *bufsize = (uint32_t)(strlen(exePath) + 1); - return -1; - } - strcpy(buf, exePath); - return 0; -} - -uint32_t _dyld_image_count(void) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - return allImagesCount(); -} - -const struct mach_header* _dyld_get_image_header(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - return allImagesIndexedMachHeader(image_index); -} - -intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - const struct mach_header* mh = allImagesIndexedMachHeader(image_index); - if ( mh != NULL ) - return ImageLoaderMachO::computeSlide(mh); - else - return 0; -} - -intptr_t _dyld_get_image_slide(const struct mach_header* mh) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, mh); - return ImageLoaderMachO::computeSlide(mh); -} - - -const char* _dyld_get_image_name(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - return allImagesIndexedPath(image_index); -} - -const struct mach_header * dyld_image_header_containing_address(const void* address) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, address); -#if SUPPORT_ACCELERATE_TABLES - const mach_header* mh; - const char* path; - if ( dyld::addressInCache(address, &mh, &path) ) - return mh; -#endif - ImageLoader* image = dyld::findImageContainingAddress(address); - if ( image != NULL ) - return image->machHeader(); - return NULL; -} - - -void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)func); - dyld::registerAddCallback(func); -} - -void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)func); - dyld::registerRemoveCallback(func); -} - - - -// called by crt before main() by programs linked with 10.4 or earlier crt1.o -static void _dyld_make_delayed_module_initializer_calls() -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - -#if SUPPORT_OLD_CRT_INITIALIZATION - if ( dyld::gRunInitializersOldWay ) - dyld::initializeMainExecutable(); -#endif -} - - - -#if DEPRECATED_APIS_SUPPORTED - -// -// _dyld_call_module_initializers_for_dylib() is the dyld side of -// __initialize_Cplusplus() which is in dylib1.o. -// It is intended to only be called inside -init rouintes. -// -init routines are called before module initializers (what C++ -// initializers use). Calling __initialize_Cplusplus() in a -init -// routine causes the module initializers for an image to be called -// which then allows C++ to be used inside a -init routine -// -static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header) -{ - if ( dyld::gLogAPIs ) - dyld::log("__initialize_Cplusplus()\n"); - - // for now, do nothing... -} - - -void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", %p, %p)\n", __func__, symbolName, address, module); - ImageLoader* image; - const ImageLoader::Symbol* sym; - dyld::clearErrorMessage(); - if ( dyld::flatFindExportedSymbol(symbolName, &sym, (const ImageLoader**)&image) ) { - try { - image->bindAllLazyPointers(dyld::gLinkContext, true); - if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); - if ( module != NULL) - *module = ImageLoaderToNSModule(image); - } - catch (const char* msg) { - dyldAPIhalt(__func__, msg); - } - } - else { - // on failure to find symbol return NULLs - if ( address != NULL) - *address = NULL; - if ( module != NULL) - *module = NULL; - } -} - -// Note: This cannot have public name because dyld is built with a static copy of libc.a -// which calls dyld_lookup_and_bind() and expects to find dyld's symbols not host process -static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module) -{ - if ( dyld::gLogAPIs ) - dyld::log("_dyld_lookup_and_bind(\"%s\", %p, %p)\n", symbolName, address, module); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { - if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); - if ( module != NULL) - *module = ImageLoaderToNSModule(image); - } - else { - // on failure to find symbol return NULLs - if ( address != NULL) - *address = NULL; - if ( module != NULL) - *module = NULL; - } -} - -void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* library_name_hint, void** address, NSModule* module) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", \"%s\", %p, %p)\n", __func__, symbolName, library_name_hint, address, module); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - // Look for library whose path contains the hint. If that fails search everywhere - if ( dyld::flatFindExportedSymbolWithHint(symbolName, library_name_hint, &sym, &image) - || dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { - if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); - if ( module != NULL) - *module = ImageLoaderToNSModule(image); - } - else { - // on failure to find symbol return NULLs - if ( address != NULL) - *address = NULL; - if ( module != NULL) - *module = NULL; - } -} - - -NSSymbol NSLookupAndBindSymbol(const char *symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\")\n", __func__, symbolName); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { - return SymbolToNSSymbol(sym); - } - // return NULL on failure - return NULL; -} - -NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); - if ( ! found ) { - // hint failed, do slow search of all images - found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); - } - if ( found ) - return SymbolToNSSymbol(sym); - - // return NULL on failure and log - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", \"%s\") => NULL \n", __func__, symbolName, libraryNameHint); - return NULL; -} - - - - -static __attribute__((noinline)) -const struct mach_header* addImage(void* callerAddress, const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError) -{ - ImageLoader* image = NULL; - std::vector rpathsFromCallerImage; - try { - dyld::clearErrorMessage(); - ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - // like dlopen, use rpath from caller image and from main executable - if ( callerImage != NULL ) - callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); - if ( callerImage != dyld::mainExecutable() ) { - dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - } - dyld::LoadContext context; - context.useSearchPaths = search; - context.useFallbackPaths = search; - context.useLdLibraryPath = false; - context.implicitRPath = false; - context.matchByInstallName = matchInstallName; - context.dontLoad = dontLoad; - context.mustBeBundle = false; - context.mustBeDylib = true; - context.canBePIE = false; - context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = &callersRPaths; // rpaths from caller and main executable - - unsigned cacheIndex; - image = load(path, context, cacheIndex); - if ( image != NULL ) { - if ( context.matchByInstallName ) - image->setMatchInstallPath(true); - dyld::link(image, false, false, callersRPaths, cacheIndex); - dyld::runInitializers(image); - // images added with NSAddImage() can never be unloaded - image->setNeverUnload(); - } - } - catch (const char* msg) { - dyld::garbageCollectImages(); - if ( abortOnError) { - char pathMsg[strlen(msg)+strlen(path)+4]; - strcpy(pathMsg, msg); - strcat(pathMsg, " "); - strcat(pathMsg, path); - dyldAPIhalt("NSAddImage", pathMsg); - } - // not halting, so set error state for NSLinkEditError to find - setLastError(NSLinkEditOtherError, 0, path, msg); - free((void*)msg); // our free() will do nothing if msg is a string literal - image = NULL; - } - // free rpaths (getRPaths() malloc'ed each string) - for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { - const char* str = *it; - free((void*)str); - } - if ( image == NULL ) - return NULL; - else - return image->machHeader(); -} - - -const struct mach_header* NSAddImage(const char* path, uint32_t options) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", 0x%08X)\n", __func__, path, options); - const bool dontLoad = ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 ); - const bool search = ( (options & NSADDIMAGE_OPTION_WITH_SEARCHING) != 0 ); - const bool matchInstallName = ( (options & NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) != 0 ); - const bool abortOnError = ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ); - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - return addImage(callerAddress, path, search, dontLoad, matchInstallName, abortOnError); -} - -bool NSAddLibrary(const char* path) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\")\n", __func__, path); - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - return (addImage(callerAddress, path, false, false, false, false) != NULL); -} - -bool NSAddLibraryWithSearching(const char* path) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\")\n", __func__, path); - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - return (addImage(callerAddress, path, true, false, false, false) != NULL); -} - - - -//#define NSADDIMAGE_OPTION_NONE 0x0 -//#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 -//#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 - -bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, \"%s\")\n", __func__, (void *)mh, symbolName); - ImageLoader* image = dyld::findImageByMachHeader(mh); - if ( image != NULL ) { - if ( image->findExportedSymbol(symbolName, true, NULL) != NULL) - return true; - } - return false; -} - - -NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, mh, symbolName, options); - const ImageLoader::Symbol* symbol = NULL; - dyld::clearErrorMessage(); - ImageLoader* image = dyld::findImageByMachHeader(mh); - if ( image != NULL ) { - try { - if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY ) { - image->bindAllLazyPointers(dyld::gLinkContext, true); - } - else if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW ) { - image->bindAllLazyPointers(dyld::gLinkContext, false); - } - } - catch (const char* msg) { - if ( (options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) == 0 ) { - dyldAPIhalt(__func__, msg); - } - } - symbol = image->findExportedSymbol(symbolName, true, NULL); - } - if ( dyld::gLogAPIs && (symbol == NULL) ) - dyld::log("%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); - return SymbolToNSSymbol(symbol); -} - - -// Note: This cannot have public name because dyld is built with a static copy of libc.a -// which calls NSIsSymbolNameDefined() and expects to find dyld's symbols not host process -static bool client_NSIsSymbolNameDefined(const char* symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("NSIsSymbolNameDefined(\"%s\")\n", symbolName); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - return dyld::flatFindExportedSymbol(symbolName, &sym, &image); -} - -bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); - const ImageLoader* image; - const ImageLoader::Symbol* sym; - bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); - if ( ! found ) { - // hint failed, do slow search of all images - found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); - } - if ( !found && dyld::gLogAPIs ) - dyld::log("%s(\"%s\", \"%s\") => false \n", __func__, symbolName, libraryNameHint); - return found; -} - -const char* NSNameOfSymbol(NSSymbol symbol) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)symbol); - const char* result = NULL; - ImageLoader* image = dyld::findImageContainingSymbol(symbol); - if ( image != NULL ) - result = image->getExportedSymbolName(NSSymbolToSymbol(symbol)); - return result; -} - -void* NSAddressOfSymbol(NSSymbol symbol) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)symbol); - if ( symbol == NULL ) - return NULL; - void* result = NULL; - ImageLoader* image = dyld::findImageContainingSymbol(symbol); - if ( image != NULL ) - result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); - return result; -} - -NSModule NSModuleForSymbol(NSSymbol symbol) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)symbol); - NSModule result = NULL; - ImageLoader* image = dyld::findImageContainingSymbol(symbol); - if ( image != NULL ) - result = ImageLoaderToNSModule(image); - return result; -} - - - - -bool _dyld_all_twolevel_modules_prebound(void) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - return FALSE; -} - -void _dyld_bind_objc_module(const void *objc_module) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objc_module); - // do nothing, with new dyld everything already bound -} - - -bool _dyld_bind_fully_image_containing_address(const void* address) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, address); - dyld::clearErrorMessage(); - ImageLoader* image = dyld::findImageContainingAddress(address); - if ( image != NULL ) { - try { - image->bindAllLazyPointers(dyld::gLinkContext, true); - return true; - } - catch (const char* msg) { - dyldAPIhalt(__func__, msg); - } - } - return false; -} - -bool _dyld_image_containing_address(const void* address) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, address); - ImageLoader *imageLoader = dyld::findImageContainingAddress(address); - return (NULL != imageLoader); -} - -static NSObjectFileImage createObjectImageFile(ImageLoader* image, const void* address = NULL, size_t len=0) -{ - NSObjectFileImage result = new __NSObjectFileImage(); - result->image = image; - result->imageBaseAddress = address; - result->imageLength = len; - sObjectFileImages.push_back(result); - return result; -} - -NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(\"%s\", ...)\n", __func__, pathName); - try { - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - - dyld::LoadContext context; - context.useSearchPaths = false; - context.useFallbackPaths = false; - context.useLdLibraryPath = false; - context.implicitRPath = false; - context.matchByInstallName = false; - context.dontLoad = false; - context.mustBeBundle = true; - context.mustBeDylib = false; - context.canBePIE = false; - context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = NULL; // support not yet implemented - - unsigned cacheIndex; - ImageLoader* image = dyld::load(pathName, context, cacheIndex); - // Note: We DO NOT link the image! NSLinkModule will do that - if ( image != NULL ) { - if ( !image->isBundle() ) { - // the image must have been already loaded (since context.mustBeBundle will prevent it from being loaded) - return NSObjectFileImageInappropriateFile; - } - *objectFileImage = createObjectImageFile(image); - return NSObjectFileImageSuccess; - } - } - catch (const char* msg) { - //dyld::log("dyld: NSCreateObjectFileImageFromFile() error: %s\n", msg); - dyld::garbageCollectImages(); - free((void*)msg); - return NSObjectFileImageInappropriateFile; - } - return NSObjectFileImageFailure; -} - - -NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage *objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, %lu, %p)\n", __func__, address, size, objectFileImage); - - try { - ImageLoader* image = dyld::loadFromMemory((const uint8_t*)address, size, NULL); - if ( ! image->isBundle() ) { - // this API can only be used with bundles... - dyld::garbageCollectImages(); - return NSObjectFileImageInappropriateFile; - } - // Note: We DO NOT link the image! NSLinkModule will do that - if ( image != NULL ) { - *objectFileImage = createObjectImageFile(image, address, size); - return NSObjectFileImageSuccess; - } - } - catch (const char* msg) { - free((void*)msg); - dyld::garbageCollectImages(); - //dyld::log("dyld: NSCreateObjectFileImageFromMemory() error: %s\n", msg); - } - return NSObjectFileImageFailure; -} - -static bool validOFI(NSObjectFileImage objectFileImage) -{ - const int ofiCount = sObjectFileImages.size(); - for (int i=0; i < ofiCount; ++i) { - if ( sObjectFileImages[i] == objectFileImage ) - return true; - } - return false; -} - -bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objectFileImage); - - if ( validOFI(objectFileImage) ) { - // a failure during NSLinkModule will delete the image - if ( objectFileImage->image != NULL ) { - // if the image has never been linked or has been unlinked, the image is not in the list of valid images - // and we should delete it - bool linkedImage = dyld::validImage(objectFileImage->image); - if ( ! linkedImage ) { - ImageLoader::deleteImage(objectFileImage->image); - objectFileImage->image = NULL; - } - } - - // remove from list of ofi's - for (std::vector::iterator it=sObjectFileImages.begin(); it != sObjectFileImages.end(); it++) { - if ( *it == objectFileImage ) { - sObjectFileImages.erase(it); - break; - } - } - - // if object was created from a memory, release that memory - // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands over ownership of the memory to dyld - if ( objectFileImage->imageBaseAddress != NULL ) { - bool freed = false; - if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 6) ) { - size_t sz = (*dyld::gLibSystemHelpers->malloc_size)(objectFileImage->imageBaseAddress); - if ( sz != 0 ) { - (*dyld::gLibSystemHelpers->free)((void*)(objectFileImage->imageBaseAddress)); - freed = true; - } - } - if ( ! freed ) - vm_deallocate(mach_task_self(), (vm_address_t)objectFileImage->imageBaseAddress, objectFileImage->imageLength); - } - - // free ofi object - delete objectFileImage; - - return true; - } - return false; -} - -bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objectFileImage); - return objectFileImage->image->needsInitialization(); -} - -uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objectFileImage); - return objectFileImage->image->getExportedSymbolCount(); -} - -const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); - const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedExportedSymbol(ordinal); - return objectFileImage->image->getExportedSymbolName(sym); -} - -uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objectFileImage); - return objectFileImage->image->getImportedSymbolCount(); -} - -const char * NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, - bool* tentative_definition) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); - const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedImportedSymbol(ordinal); - if ( tentative_definition != NULL ) { - ImageLoader::ReferenceFlags flags = objectFileImage->image->getImportedSymbolInfo(sym); - if ( (flags & ImageLoader::kTentativeDefinition) != 0 ) - *tentative_definition = true; - else - *tentative_definition = false; - } - return objectFileImage->image->getImportedSymbolName(sym); -} - -void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, - const char* segmentName, const char* sectionName, unsigned long* size) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p,%s, %s)\n", __func__, objectFileImage, segmentName, sectionName); - - void* start; - size_t length; - if ( objectFileImage->image->getSectionContent(segmentName, sectionName, &start, &length) ) { - if ( size != NULL ) - *size = length; - return start; - } - return NULL; -} - - - -bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p,%s)\n", __func__, objectFileImage, symbolName); - const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, true, NULL); - return ( sym != NULL ); -} - - - -NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, objectFileImage, moduleName, options); - - dyld::clearErrorMessage(); - try { - if ( (options & NSLINKMODULE_OPTION_CAN_UNLOAD) != 0 ) - objectFileImage->image->setCanUnload(); - - // NSLinkModule allows a bundle to be link multpile times - // each link causes the bundle to be copied to a new address - if ( objectFileImage->image->isLinked() ) { - // already linked, so clone a new one and link it - objectFileImage->image = dyld::cloneImage(objectFileImage->image); - } - - // for memory based images, set moduleName as the name anyone calling _dyld_get_image_name() will see - if ( objectFileImage->image->getPath() == NULL ) { - objectFileImage->image->setPath(moduleName); - // dyld has NULL paths in image info array - dyld_image_info info; - info.imageLoadAddress = objectFileImage->image->machHeader(); - info.imageFilePath = moduleName; - info.imageFileModDate = 0; - addImagesToAllImages(1, &info); - } - - // support private bundles - if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) - objectFileImage->image->setHideExports(); - - // set up linking options - bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); - - // load libraries, rebase, bind, to make this image usable - dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); - - // bump reference count to keep this bundle from being garbage collected - objectFileImage->image->incrementDlopenReferenceCount(); - - // run initializers unless magic flag says not to - if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) - dyld::runInitializers(objectFileImage->image); - - return ImageLoaderToNSModule(objectFileImage->image); - } - catch (const char* msg) { - dyld::garbageCollectImages(); - if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) - dyldAPIhalt(__func__, msg); - // not halting, so set error state for NSLinkEditError to find - setLastError(NSLinkEditOtherError, 0, moduleName, msg); - // dyld::link() deleted the image so lose our reference - objectFileImage->image = NULL; - free((void*)msg); - return NULL; - } -} - - -#if OLD_LIBSYSTEM_SUPPORT -// This is for compatibility with old libSystems (libdyld.a) which process ObjectFileImages outside dyld -static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, \"%s\", 0x%08X)\n", "NSLinkModule", object_addr, moduleName, options); // note name/args translation - ImageLoader* image = NULL; - dyld::clearErrorMessage(); - try { - const char* imageName = moduleName; - image = dyld::loadFromMemory((const uint8_t*)object_addr, object_size, imageName); - - if ( image != NULL ) { - // support private bundles - if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) - image->setHideExports(); - - // set up linking options - bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); - - // load libraries, rebase, bind, to make this image usable - dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); - - // run initializers unless magic flag says not to - if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) - dyld::runInitializers(image); - } - } - catch (const char* msg) { - if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) - dyldAPIhalt("NSLinkModule", msg); - // not halting, so set error state for NSLinkEditError to find - setLastError(NSLinkEditOtherError, 0, moduleName, msg); - // if image was created for this bundle, destroy it - if ( image != NULL ) { - dyld::removeImage(image); - ImageLoader::deleteImage(image); - } - image = NULL; - free((void*)msg); - } - return ImageLoaderToNSModule(image); -} -#endif - -NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, \"%s\")\n", __func__, (void *)module, symbolName); - ImageLoader* image = NSModuleToImageLoader(module); - if ( image == NULL ) - return NULL; - return SymbolToNSSymbol(image->findExportedSymbol(symbolName, false, NULL)); -} - -const char* NSNameOfModule(NSModule module) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, module); - ImageLoader* image = NSModuleToImageLoader(module); - if ( image == NULL ) - return NULL; - return image->getPath(); -} - -const char* NSLibraryNameForModule(NSModule module) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, module); - ImageLoader* image = NSModuleToImageLoader(module); - if ( image == NULL ) - return NULL; - return image->getPath(); -} - -bool NSUnLinkModule(NSModule module, uint32_t options) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, 0x%08X)\n", __func__, module, options); - if ( module == NULL ) - return false; - ImageLoader* image = NSModuleToImageLoader(module); - if ( image == NULL ) - return false; - dyld::runImageStaticTerminators(image); - if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 13) ) { - __cxa_range_t ranges[image->segmentCount()]; - int rangeCount = 0; - for (unsigned int j=0; j < image->segmentCount(); ++j) { - if ( !image->segExecutable(j) ) - continue; - ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); - ranges[rangeCount].length = image->segSize(j); - ++rangeCount; - } - (*dyld::gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); - } - dyld::removeImage(image); - - if ( (options & NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) != 0 ) - image->setLeaveMapped(); - - // TODO: NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES - - // Only delete image if there is no ofi referencing it - // That means the ofi was destroyed after linking, so no one is left to delete this image - const int ofiCount = sObjectFileImages.size(); - bool found = false; - for (int i=0; i < ofiCount; ++i) { - NSObjectFileImage ofi = sObjectFileImages[i]; - if ( ofi->image == image ) - found = true; - } - if ( !found ) - ImageLoader::deleteImage(image); - - return true; -} - -// internal name and parameters do not match public name and parameters... -static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit) -{ - if ( dyld::gLogAPIs ) - dyld::log("NSLinkEditErrorHandlers()\n"); - - dyld::registerUndefinedHandler((dyld::UndefinedHandler)undefined); - // no support for multiple or linkedit handlers -} - - - - -void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString) -{ - // FIXME FIXME - *c = sLastErrorFileCode; - *errorNumber = sLastErrorNo; - *fileName = sLastErrorFilePath; - *errorString = dyld::getErrorMessage(); -} - - - -static void _dyld_register_binding_handler(void * (*bindingHandler)(const char *, const char *, void *), ImageLoader::BindingOptions bindingOptions) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - dyld::gLinkContext.bindingHandler = bindingHandler; - dyld::gLinkContext.bindingOptions = bindingOptions; -} - -#endif //DEPRECATED_APIS_SUPPORTED - - -// Call by fork() in libSystem after the kernel trap is done on the child side -void _dyld_fork_child() -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - // The implementation of fork() in libSystem knows to reset the variable mach_task_self_ - // in libSystem for the child of a fork. But dyld is built with a static copy - // of libc.a and has its own copy of mach_task_self_ which we reset here. - // - // In mach_init.h mach_task_self() is #defined to mach_task_self_ and - // in mach_init() mach_task_self_ is initialized to task_self_trap(). - // - extern mach_port_t mach_task_self_; - mach_task_self_ = task_self_trap(); - - // If dyld is sending load/unload notices to CoreSymbolication, the shared memory - // page is not copied on fork. - // NULL the CoreSymbolication shared memory pointer to prevent a crash. - dyld::gProcessInfo->coreSymbolicationShmPage = NULL; - // for safety, make sure child starts with clean systemOrderFlag - dyld::gProcessInfo->systemOrderFlag = 0; -} - - -#if DEPRECATED_APIS_SUPPORTED -// returns true if prebinding was used in main executable -bool _dyld_launched_prebound() -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - - // ����if we deprecate prebinding, we may want to consider always returning true or false here - return dyld::mainExecutablePrebound(); -} - - -// -// _dyld_NSMakePrivateModulePublic() is the dyld side of the hack -// NSMakePrivateModulePublic() needed for the dlopen() to turn it's -// RTLD_LOCAL handles into RTLD_GLOBAL. It just simply turns off the private -// flag on the image for this module. If the module was found and it was -// private then everything worked and TRUE is returned else FALSE is returned. -// -static bool NSMakePrivateModulePublic(NSModule module) -{ - ImageLoader* image = NSModuleToImageLoader(module); - if ( image != NULL ) { - if ( image->hasHiddenExports() ) { - image->setHideExports(false); - return true; - } - } - return false; -} - -#endif // DEPRECATED_APIS_SUPPORTED - -int _dyld_func_lookup(const char* name, void** address) -{ - for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) { - if ( strcmp(p->name, name) == 0 ) { - if( p->implementation == unimplemented ) - dyld::log("unimplemented dyld function: %s\n", p->name); - *address = p->implementation; - return true; - } - } - *address = 0; - return false; -} - - -static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) -{ - dyld::gLibSystemHelpers = helpers; - -#if !SUPPORT_ZERO_COST_EXCEPTIONS - if ( helpers->version >= 5 ) { - // create key use by dyld exception handling - pthread_key_t key; - int result = helpers->pthread_key_create(&key, NULL); - if ( result == 0 ) - __Unwind_SjLj_SetThreadKey(key); - } -#endif -} - - -static void dlerrorClear() -{ - if ( dyld::gLibSystemHelpers != NULL ) { - // dlerror buffer leak - // dlerrorClear() should not force allocation, but zero it if already allocated - if ( dyld::gLibSystemHelpers->version >= 10 ) { - if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) - return; - } - - // first char of buffer is flag whether string (starting at second char) is valid - char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); - buffer[0] = '\0'; - buffer[1] = '\0'; - } -} - -static void dlerrorSet(const char* msg) -{ - if ( dyld::gLibSystemHelpers != NULL ) { - // first char of buffer is flag whether string (starting at second char) is valid - char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(strlen(msg)+2); - buffer[0] = '\1'; - strcpy(&buffer[1], msg); - } -} - - -bool dlopen_preflight(const char* path) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%s)\n", __func__, path); - - dlerrorClear(); - - CRSetCrashLogMessage("dyld: in dlopen_preflight()"); - - const bool leafName = (strchr(path, '/') == NULL); - const bool absolutePath = (path[0] == '/'); -#if __IPHONE_OS_VERSION_MIN_REQUIRED - char canonicalPath[PATH_MAX]; - // dlopen() not opening frameworks from shared cache with // or ./ in path - if ( !leafName ) { - // make path canonical if it contains a // or ./ - if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) { - const char* lastSlash = strrchr(path, '/'); - char dirPath[PATH_MAX]; - if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) { - dirPath[lastSlash-path] = '\0'; - if ( realpath(dirPath, canonicalPath) ) { - strlcat(canonicalPath, "/", sizeof(canonicalPath)); - if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) { - // if all fit in buffer, use new canonical path - path = canonicalPath; - } - } - } - } - } -#endif -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::isPathInCache(path) ) - return true; -#endif - -#if DYLD_SHARED_CACHE_SUPPORT - // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized - // if requested path is to something in the dyld shared cache, always succeed - if ( dyld::inSharedCache(path) ) - return true; -#endif - - bool result = false; - std::vector 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 ) - callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); - if ( callerImage != dyld::mainExecutable() ) { - dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - } - - ImageLoader* image = NULL; - dyld::LoadContext context; - context.useSearchPaths = true; - context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths - context.useLdLibraryPath= leafName; // a leafname implies should search - context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching - context.matchByInstallName = true; - context.dontLoad = false; - context.mustBeBundle = false; - context.mustBeDylib = false; - context.canBePIE = true; - context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = &callersRPaths; // rpaths from caller and main executable - - unsigned cacheIndex; - image = load(path, context, cacheIndex); - if ( image != NULL ) { - dyld::preflight(image, callersRPaths, cacheIndex); // image object deleted by dyld::preflight() - result = true; - } - } - catch (const char* msg) { - const char* str = dyld::mkstringf("dlopen_preflight(%s): %s", path, msg); - dlerrorSet(str); - free((void*)str); - free((void*)msg); // our free() will do nothing if msg is a string literal - } - // free rpaths (getRPaths() malloc'ed each string) - for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { - const char* str = *it; - free((void*)str); - } - CRSetCrashLogMessage(NULL); - return result; -} - -#if SUPPORT_ACCELERATE_TABLES -bool static callerIsNonOSApp(void* callerAddress, const char** shortName) -{ - *shortName = NULL; - const mach_header* unusedMh; - const char* unusedPath; - unsigned unusedIndex; - // any address in shared cache is not from app - if ( dyld::addressInCache(callerAddress, &unusedMh, &unusedPath, &unusedIndex) ) - return false; - - ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - if ( callerImage == NULL ) - return false; - - *shortName = callerImage->getShortName(); - return ( strncmp(callerImage->getPath(), "/var/containers/", 16) == 0 ); -} -#endif - -void* dlopen(const char* path, int mode) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode); - -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::gLogAppAPIs ) { - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - const char* shortName; - if ( callerIsNonOSApp(callerAddress, &shortName) ) { - dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode); - } - } -#endif - - dlerrorClear(); - - // passing NULL for path means return magic object - if ( path == NULL ) { - // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images - if ( (mode & RTLD_FIRST) != 0 ) - return RTLD_MAIN_ONLY; - else - return RTLD_DEFAULT; - } - - // acquire global dyld lock (dlopen is special - libSystem glue does not do locking) - bool lockHeld = false; - if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) { - dyld::gLibSystemHelpers->acquireGlobalDyldLock(); - CRSetCrashLogMessage("dyld: in dlopen()"); - lockHeld = true; - } - - void* result = NULL; - const bool leafName = (strchr(path, '/') == NULL); - const bool absolutePath = (path[0] == '/'); -#if __IPHONE_OS_VERSION_MIN_REQUIRED - char canonicalPath[PATH_MAX]; - // dlopen() not opening frameworks from shared cache with // or ./ in path - if ( !leafName ) { - // make path canonical if it contains a // or ./ - if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) { - const char* lastSlash = strrchr(path, '/'); - char dirPath[PATH_MAX]; - if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) { - dirPath[lastSlash-path] = '\0'; - if ( realpath(dirPath, canonicalPath) ) { - strlcat(canonicalPath, "/", sizeof(canonicalPath)); - if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) { - // if all fit in buffer, use new canonical path - path = canonicalPath; - } - } - } - } - } -#endif -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::dlopenFromCache(path, mode, &result) ) { - // Note: dlopenFromCache() releases the lock - if ( dyld::gLogAPIs ) - dyld::log(" %s(%s) ==> %p\n", __func__, path, result); - return result; - } -#endif - - ImageLoader* image = NULL; - std::vector 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 - if ( callerImage != NULL ) - callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - if ( callerImage != dyld::mainExecutable() ) - dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); - } - - dyld::LoadContext context; - context.useSearchPaths = true; - context.useFallbackPaths= leafName; // a partial path means no fallback paths - context.useLdLibraryPath= leafName; // a leafname implies should search - context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching - context.matchByInstallName = true; - context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); - context.mustBeBundle = false; - context.mustBeDylib = false; - context.canBePIE = true; - context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = &callersRPaths; // rpaths from caller and main executable - - unsigned cacheIndex; - image = load(path, context, cacheIndex); -#if SUPPORT_ACCELERATE_TABLES - if ( (image != NULL) && (cacheIndex != UINT32_MAX) ) { - if ( dyld::makeCacheHandle(image, cacheIndex, mode, &result) ) { - if ( dyld::gLogAPIs ) - dyld::log(" %s(%s) ==> %p\n", __func__, path, result); - if ( lockHeld ) - dyld::gLibSystemHelpers->releaseGlobalDyldLock(); - return result; - } - } -#endif - if ( image != NULL ) { - // bump reference count. Do this before link() so that if an initializer calls dlopen and fails - // this image is not garbage collected - image->incrementDlopenReferenceCount(); - // link in all dependents - if ( (mode & RTLD_NOLOAD) == 0 ) { - bool alreadyLinked = image->isLinked(); - bool forceLazysBound = ( (mode & RTLD_NOW) != 0 ); - dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex); - if ( ! alreadyLinked ) { - // only hide exports if image is not already in use - if ( (mode & RTLD_LOCAL) != 0 ) - image->setHideExports(true); - } - } - - // RTLD_NODELETE means don't unmap image even after dlclosed. This is what dlcompat did on Mac OS X 10.3 - // On other *nix OS's, it means dlclose() should do nothing, but the handle should be invalidated. - // The subtle differences are: - // 1) if the image has any termination routines, whether they are run during dlclose or when the process terminates - // 2) If someone does a supsequent dlopen() on the same image, whether the same address should be used. - if ( (mode & RTLD_NODELETE) != 0 ) - image->setLeaveMapped(); - - // release global dyld lock early, this enables initializers to do threaded operations - if ( lockHeld ) { - CRSetCrashLogMessage(NULL); - dyld::gLibSystemHelpers->releaseGlobalDyldLock(); - lockHeld = false; - } - - // RTLD_NOLOAD means dlopen should fail unless path is already loaded. - // don't run initializers when RTLD_NOLOAD is set. This only matters if dlopen() is - // called from within an initializer because it can cause initializers to run - // out of order. Most uses of RTLD_NOLOAD are "probes". If they want initialzers - // to run, then don't use RTLD_NOLOAD. - if ( (mode & RTLD_NOLOAD) == 0 ) { - // run initializers - dyld::runInitializers(image); - } - - // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images - // this is tracked by setting the low bit of the handle, which is usually zero by malloc alignment - if ( (mode & RTLD_FIRST) != 0 ) - result = (void*)(((uintptr_t)image)|1); - else - result = image; - } - } - catch (const char* msg) { - if ( image != NULL ) { - // load() succeeded but, link() failed - // back down reference count and do GC - image->decrementDlopenReferenceCount(); - if ( image->dlopenCount() == 0 ) - dyld::garbageCollectImages(); - } - const char* str = dyld::mkstringf("dlopen(%s, %d): %s", path, mode, msg); - if ( dyld::gLogAPIs ) - dyld::log(" %s() failed, error: '%s'\n", __func__, str); - dlerrorSet(str); - free((void*)str); - free((void*)msg); // our free() will do nothing if msg is a string literal - result = NULL; - } - // free rpaths (getRPaths() malloc'ed each string) - for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { - const char* str = *it; - free((void*)str); - } - - // when context.dontLoad is set, load() returns NULL instead of throwing an exception - if ( (mode & RTLD_NOLOAD) && (result == NULL) ) { - dlerrorSet("image not already loaded"); - } - - if ( lockHeld ) { - CRSetCrashLogMessage(NULL); - dyld::gLibSystemHelpers->releaseGlobalDyldLock(); - } - if ( dyld::gLogAPIs && (result != NULL) ) - dyld::log(" %s(%s) ==> %p\n", __func__, path, result); - return result; -} - - - -int dlclose(void* handle) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, handle); - - // silently accept magic handles for main executable - if ( handle == RTLD_MAIN_ONLY ) - return 0; - if ( handle == RTLD_DEFAULT ) - return 0; - - ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits - if ( dyld::validImage(image) ) { - dlerrorClear(); - // decrement use count - if ( image->decrementDlopenReferenceCount() ) { - dlerrorSet("dlclose() called too many times"); - return -1; - } - // remove image if reference count went to zero - if ( image->dlopenCount() == 0 ) - dyld::garbageCollectImages(); - return 0; - } - else { - dlerrorSet("invalid handle passed to dlclose()"); - return -1; - } -} - - - -int dladdr(const void* address, Dl_info* info) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, %p)\n", __func__, address, info); - - CRSetCrashLogMessage("dyld: in dladdr()"); -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::dladdrFromCache(address, info) ) { - CRSetCrashLogMessage(NULL); - return 1; // success - } -#endif - - ImageLoader* image = dyld::findImageContainingAddress(address); - if ( image != NULL ) { - info->dli_fname = image->getRealPath(); - info->dli_fbase = (void*)image->machHeader(); - if ( address == info->dli_fbase ) { - // special case lookup of header - info->dli_sname = "__dso_handle"; - info->dli_saddr = info->dli_fbase; - CRSetCrashLogMessage(NULL); - return 1; // success - } - // find closest symbol in the image - info->dli_sname = image->findClosestSymbol(address, (const void**)&info->dli_saddr); - // never return the mach_header symbol - if ( info->dli_saddr == info->dli_fbase ) { - info->dli_sname = NULL; - info->dli_saddr = NULL; - CRSetCrashLogMessage(NULL); - return 1; // success - } - if ( info->dli_sname != NULL ) { - if ( info->dli_sname[0] == '_' ) - info->dli_sname = info->dli_sname +1; // strip off leading underscore - //dyld::log("dladdr(%p) => %p %s\n", address, info->dli_saddr, info->dli_sname); - CRSetCrashLogMessage(NULL); - return 1; // success - } - info->dli_sname = NULL; - info->dli_saddr = NULL; - CRSetCrashLogMessage(NULL); - return 1; // success - } - CRSetCrashLogMessage(NULL); - return 0; // failure -} - - -char* dlerror() -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - - if ( dyld::gLibSystemHelpers != NULL ) { - // if using newer libdyld.dylib and buffer if buffer not yet allocated, return NULL - if ( dyld::gLibSystemHelpers->version >= 10 ) { - if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) - return NULL; - } - - // first char of buffer is flag whether string (starting at second char) is valid - char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); - if ( buffer[0] != '\0' ) { // if valid buffer - buffer[0] = '\0'; // mark invalid, so next call to dlerror returns NULL - return &buffer[1]; // return message - } - } - return NULL; -} - -void* dlsym(void* handle, const char* symbolName) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, %s)\n", __func__, handle, symbolName); - -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::gLogAppAPIs ) { - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue - const char* shortName; - if ( callerIsNonOSApp(callerAddress, &shortName) ) { - dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName); - } - } -#endif - - CRSetCrashLogMessage("dyld: in dlsym()"); - dlerrorClear(); - - const ImageLoader* image; - const ImageLoader::Symbol* sym; - void* result; - - // dlsym() assumes symbolName passed in is same as in C source code - // dyld assumes all symbol names have an underscore prefix - char underscoredName[strlen(symbolName)+2]; - underscoredName[0] = '_'; - strcpy(&underscoredName[1], symbolName); - - // magic "search all" handle - if ( handle == RTLD_DEFAULT ) { - if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) { - CRSetCrashLogMessage(NULL); - result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result); - return result; - } - const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); - dlerrorSet(str); - free((void*)str); - CRSetCrashLogMessage(NULL); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_DEFAULT, %s) ==> NULL\n", __func__, symbolName); - return NULL; - } - - // magic "search only main executable" handle - else if ( handle == RTLD_MAIN_ONLY ) { - image = dyld::mainExecutable(); - sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way - if ( sym != NULL ) { - CRSetCrashLogMessage(NULL); - result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result); - return result; - } - const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); - dlerrorSet(str); - free((void*)str); - CRSetCrashLogMessage(NULL); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> NULL\n", __func__, symbolName); - return NULL; - } - - // magic "search what I would see" handle - else if ( handle == RTLD_NEXT ) { - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue -#if SUPPORT_ACCELERATE_TABLES - const mach_header* mh; - const char* path; - unsigned index; - if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { - // if dylib in cache is calling dlsym(RTLD_NEXT,xxx) handle search differently - result = dyld::dlsymFromCache(RTLD_NEXT, underscoredName, index); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); - return result; - } -#endif - ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against - if ( sym != NULL ) { - CRSetCrashLogMessage(NULL); - result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); - return result; - } - const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName); - dlerrorSet(str); - free((void*)str); - CRSetCrashLogMessage(NULL); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_NEXT, %s) ==> NULL\n", __func__, symbolName); - return NULL; - } - // magic "search me, then what I would see" handle - else if ( handle == RTLD_SELF ) { - void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue -#if SUPPORT_ACCELERATE_TABLES - const mach_header* mh; - const char* path; - unsigned index; - if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { - // if dylib in cache is calling dlsym(RTLD_SELF,xxx) handle search differently - result = dyld::dlsymFromCache(RTLD_SELF, underscoredName, index); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); - return result; - } -#endif - ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against - if ( sym != NULL ) { - CRSetCrashLogMessage(NULL); - result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); - return result; - } - const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName); - dlerrorSet(str); - free((void*)str); - CRSetCrashLogMessage(NULL); - if ( dyld::gLogAPIs ) - dyld::log(" %s(RTLD_SELF, %s) ==> NULL\n", __func__, symbolName); - return NULL; - } -#if SUPPORT_ACCELERATE_TABLES - // check for mega dylib handle - else if ( dyld::isCacheHandle(handle) ) { - result = dyld::dlsymFromCache(handle, underscoredName, 0); - if ( dyld::gLogAPIs ) - dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); - return result; - } -#endif - // real handle - image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits - if ( dyld::validImage(image) ) { - if ( (((uintptr_t)handle) & 1) != 0 ) - sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way - else - sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against - - if ( sym != NULL ) { - CRSetCrashLogMessage(NULL); - 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 ( dyld::gLogAPIs ) - dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); - return result; - } - const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); - dlerrorSet(str); - free((void*)str); - } - else { - dlerrorSet("invalid handle passed to dlsym()"); - } - CRSetCrashLogMessage(NULL); - if ( dyld::gLogAPIs ) - dyld::log(" %s(%p, %s) ==> NULL\n", __func__, handle, symbolName); - return NULL; -} - - - - - - - - - - -const struct dyld_all_image_infos* _dyld_get_all_image_infos() -{ - return dyld::gProcessInfo; -} - - -#if SUPPORT_ZERO_COST_EXCEPTIONS -static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) -{ - //if ( dyld::gLogAPIs ) - // dyld::log("%s(%p, %p)\n", __func__, addr, info); - -#if SUPPORT_ACCELERATE_TABLES - if ( dyld::findUnwindSections(addr, info) ) - return true; -#endif - ImageLoader* image = dyld::findImageContainingAddress(addr); - if ( image != NULL ) { - image->getUnwindInfo(info); - return true; - } - return false; -} -#endif - - -const char* dyld_image_path_containing_address(const void* address) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, address); - -#if SUPPORT_ACCELERATE_TABLES - const mach_header* mh; - const char* path; - if ( dyld::addressInCache(address, &mh, &path) ) - return path; -#endif - - ImageLoader* image = dyld::findImageContainingAddress(address); - if ( image != NULL ) - return image->getRealPath(); - return NULL; -} - - - -bool dyld_shared_cache_some_image_overridden() -{ - #if DYLD_SHARED_CACHE_SUPPORT - return dyld::gSharedCacheOverridden; - #else - return true; - #endif -} - - -void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) -{ - if ( mh == NULL ) - return; - if ( array == NULL ) - return; - if ( count == 0 ) - return; - ImageLoader* image = dyld::findImageByMachHeader(mh); - if ( image == NULL ) - return; - - // make pass at bound references in this image and update them - dyld::gLinkContext.dynamicInterposeArray = array; - dyld::gLinkContext.dynamicInterposeCount = count; - image->dynamicInterpose(dyld::gLinkContext); - dyld::gLinkContext.dynamicInterposeArray = NULL; - dyld::gLinkContext.dynamicInterposeCount = 0; - - // leave interposing info so any future (lazy) binding will get it too - image->addDynamicInterposingTuples(array, count); - - sDynamicInterposing = true; -} - - -bool _dyld_is_memory_immutable(const void* addr, size_t length) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, %ld)\n", __func__, addr, length); - - uintptr_t checkStart = (uintptr_t)addr; - uintptr_t checkEnd = checkStart + length; - -#if DYLD_SHARED_CACHE_SUPPORT - // quick check to see if in r/o region of shared cache. If so return true. - if ( dyld_shared_cache_ranges.sharedRegionsCount > 2 ) { - uintptr_t roStart = dyld_shared_cache_ranges.ranges[0].start; - uintptr_t roEnd = roStart + dyld_shared_cache_ranges.ranges[0].length; - if ( (roStart < checkStart) && (checkEnd < roEnd) ) - return true; - } -#endif - - // Otherwise find if addr is in a dyld loaded image - ImageLoader* image = dyld::findImageContainingAddress(addr); - if ( image != NULL ) { - // already checked for r/o portion of cache - if ( image->inSharedCache() ) - return false; - if ( !image->neverUnload() ) - return false; - for (unsigned i=0, e=image->segmentCount(); i < e; ++i) { - if ( (image->segActualLoadAddress(i) < checkStart) && (checkEnd < image->segActualEndAddress(i)) ) { - return !image->segWriteable(i); - } - } - } - return false; -} - - - -void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, - _dyld_objc_notify_init init, - _dyld_objc_notify_unmapped unmapped) -{ - dyld::registerObjCNotifiers(mapped, init, unmapped); -} - - -bool _dyld_get_shared_cache_uuid(uuid_t uuid) -{ - return dyld::sharedCacheUUID(uuid); -} diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index 6c02a59..07e20ac 100644 --- a/src/dyldAPIsInLibSystem.cpp +++ b/src/dyldAPIsInLibSystem.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -39,11 +40,14 @@ #include #include #include +#include #include #include #include +#include + #include "dyld_cache_format.h" #include "objc-shared-cache.h" @@ -71,6 +75,17 @@ extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int c // extern "C" int _dyld_func_lookup(const char* dyld_func_name, void **address); +template +static void dyld_func_lookup_and_resign(const char *dyld_func_name, T *__ptrauth_dyld_function_ptr* address) { + void *funcAsVoidPtr; + int res = _dyld_func_lookup(dyld_func_name, &funcAsVoidPtr); + (void)res; + + // If C function pointer discriminators are type-diverse this cast will be + // an authenticate and resign operation. + *address = reinterpret_cast(funcAsVoidPtr); +} + #if TARGET_OS_IOS && !TARGET_OS_SIMULATOR namespace dyld3 { extern int compatFuncLookup(const char* name, void** address) __API_AVAILABLE(ios(13.0)); @@ -79,13 +94,58 @@ extern "C" void setLookupFunc(void*); #endif -extern bool gUseDyld3; +extern void* __ptrauth_dyld_address_auth gUseDyld3; + + +// libdyld.dylib should use abort_with_payload() for asserts +VIS_HIDDEN +void abort_report_np(const char* format, ...) +{ + va_list list; + const char *str; + _SIMPLE_STRING s = _simple_salloc(); + if ( s != NULL ) { + va_start(list, format); + _simple_vsprintf(s, format, list); + va_end(list); + str = _simple_string(s); + } + else { + // _simple_salloc failed, but at least format may have useful info by itself + str = format; + } + if ( gUseDyld3 ) { + dyld3::halt(str); + } + else { + typedef void (*funcType)(const char* msg) __attribute__((__noreturn__)); + static funcType __ptrauth_dyld_function_ptr p = NULL; + dyld_func_lookup_and_resign("__dyld_halt", &p); + p(str); + } + // halt() doesn't return, so we can't call _simple_sfree +} + +// libc uses assert() +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-noreturn" +VIS_HIDDEN +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) +{ + if (func == NULL) { + abort_report_np("Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line); + } else { + abort_report_np("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + } +} +#pragma clang diagnostic pop + // deprecated APIs are still availble on Mac OS X, but not on iPhone OS -#if __IPHONE_OS_VERSION_MIN_REQUIRED || TARGET_OS_DRIVERKIT - #define DEPRECATED_APIS_SUPPORTED 0 -#else +#if TARGET_OS_OSX #define DEPRECATED_APIS_SUPPORTED 1 +#else + #define DEPRECATED_APIS_SUPPORTED 0 #endif /* @@ -154,10 +214,11 @@ const NSLinkEditErrorHandlers* handlers) typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler); typedef void (*lcallback_t)(NSLinkEditErrors c, int errorNumber, const char* fileName, const char* errorString); - static void (*p)(ucallback_t undefined, mcallback_t multiple, lcallback_t linkEdit) = NULL; + typedef void (*funcType)(ucallback_t undefined, mcallback_t multiple, lcallback_t linkEdit); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_install_handlers", (void**)&p); + dyld_func_lookup_and_resign("__dyld_install_handlers", &p); mcallback_t m = handlers->multiple; p(handlers->undefined, m, handlers->linkEdit); } @@ -170,10 +231,11 @@ NSModule module) return dyld3::NSNameOfModule(module); DYLD_LOCK_THIS_BLOCK; - static const char* (*p)(NSModule module) = NULL; + typedef const char* (*funcType)(NSModule module); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSNameOfModule", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSNameOfModule", &p); return(p(module)); } @@ -185,10 +247,11 @@ NSModule module) return dyld3::NSLibraryNameForModule(module); DYLD_LOCK_THIS_BLOCK; - static const char* (*p)(NSModule module) = NULL; + typedef const char* (*funcType)(NSModule module); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLibraryNameForModule", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLibraryNameForModule", &p); return(p(module)); } @@ -200,10 +263,11 @@ const char* symbolName) return dyld3::NSIsSymbolNameDefined(symbolName); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const char* symbolName) = NULL; + typedef bool (*funcType)(const char* symbolName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSIsSymbolNameDefined", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSIsSymbolNameDefined", &p); return(p(symbolName)); } @@ -216,11 +280,12 @@ const char* libraryNameHint) return dyld3::NSIsSymbolNameDefinedWithHint(symbolName, libraryNameHint); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const char* symbolName, - const char* libraryNameHint) = NULL; + typedef bool (*funcType)(const char* symbolName, + const char* libraryNameHint); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedWithHint", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSIsSymbolNameDefinedWithHint", &p); return(p(symbolName, libraryNameHint)); } @@ -233,11 +298,12 @@ const char* symbolName) return dyld3::NSIsSymbolNameDefinedInImage(image, symbolName); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const struct mach_header *image, - const char* symbolName) = NULL; + typedef bool (*funcType)(const struct mach_header *image, + const char* symbolName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSIsSymbolNameDefinedInImage", &p); return(p(image, symbolName)); } @@ -249,10 +315,11 @@ const char* symbolName) return dyld3::NSLookupAndBindSymbol(symbolName); DYLD_LOCK_THIS_BLOCK; - static NSSymbol (*p)(const char* symbolName) = NULL; + typedef NSSymbol (*funcType)(const char* symbolName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLookupAndBindSymbol", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLookupAndBindSymbol", &p); return(p(symbolName)); } @@ -265,11 +332,12 @@ const char* libraryNameHint) return dyld3::NSLookupAndBindSymbolWithHint(symbolName, libraryNameHint); DYLD_LOCK_THIS_BLOCK; - static NSSymbol (*p)(const char* symbolName, - const char* libraryNameHint) = NULL; + typedef NSSymbol (*funcType)(const char* symbolName, + const char* libraryNameHint); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLookupAndBindSymbolWithHint", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLookupAndBindSymbolWithHint", &p); return(p(symbolName, libraryNameHint)); } @@ -282,10 +350,11 @@ const char* symbolName) return dyld3::NSLookupSymbolInModule(module, symbolName); DYLD_LOCK_THIS_BLOCK; - static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL; + typedef NSSymbol (*funcType)(NSModule module, const char* symbolName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLookupSymbolInModule", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLookupSymbolInModule", &p); return(p(module, symbolName)); } @@ -299,12 +368,13 @@ uint32_t options) return dyld3::NSLookupSymbolInImage(image, symbolName, options); DYLD_LOCK_THIS_BLOCK; - static NSSymbol (*p)(const struct mach_header *image, - const char* symbolName, - uint32_t options) = NULL; + typedef NSSymbol (*funcType)(const struct mach_header *image, + const char* symbolName, + uint32_t options); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLookupSymbolInImage", &p); return(p(image, symbolName, options)); } @@ -316,10 +386,11 @@ NSSymbol symbol) return dyld3::NSNameOfSymbol(symbol); DYLD_LOCK_THIS_BLOCK; - static char * (*p)(NSSymbol symbol) = NULL; + typedef char * (*funcType)(NSSymbol symbol); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSNameOfSymbol",(void**)&p); + dyld_func_lookup_and_resign("__dyld_NSNameOfSymbol",&p); return(p(symbol)); } @@ -331,10 +402,11 @@ NSSymbol symbol) return dyld3::NSAddressOfSymbol(symbol); DYLD_LOCK_THIS_BLOCK; - static void * (*p)(NSSymbol symbol) = NULL; + typedef void * (*funcType)(NSSymbol symbol); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSAddressOfSymbol", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSAddressOfSymbol", &p); return(p(symbol)); } @@ -346,10 +418,11 @@ NSSymbol symbol) return dyld3::NSModuleForSymbol(symbol); DYLD_LOCK_THIS_BLOCK; - static NSModule (*p)(NSSymbol symbol) = NULL; + typedef NSModule (*funcType)(NSSymbol symbol); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSModuleForSymbol", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSModuleForSymbol", &p); return(p(symbol)); } @@ -361,10 +434,11 @@ const char* pathName) return dyld3::NSAddLibrary(pathName); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const char* pathName) = NULL; + typedef bool (*funcType)(const char* pathName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSAddLibrary", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSAddLibrary", &p); return(p(pathName)); } @@ -376,10 +450,11 @@ const char* pathName) return dyld3::NSAddLibrary(pathName); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const char* pathName) = NULL; + typedef bool (*funcType)(const char* pathName); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSAddLibraryWithSearching", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSAddLibraryWithSearching", &p); return(p(pathName)); } @@ -392,11 +467,12 @@ uint32_t options) return dyld3::NSAddImage(image_name, options); DYLD_LOCK_THIS_BLOCK; - static const struct mach_header * (*p)(const char* image_name, - uint32_t options) = NULL; + typedef const struct mach_header * (*funcType)(const char* image_name, + uint32_t options); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSAddImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSAddImage", &p); return(p(image_name, options)); } #endif // DEPRECATED_APIS_SUPPORTED @@ -626,15 +702,10 @@ bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid) } dyld_platform_t dyld_get_active_platform(void) { - if (gUseDyld3) { return dyld3::dyld_get_active_platform(); } - if (_dyld_get_all_image_infos()->version >= 16) { return (dyld_platform_t)_dyld_get_all_image_infos()->platform; } + if (gUseDyld3) + return dyld3::dyld_get_active_platform(); - __block dyld_platform_t result; - // FIXME: Remove this once we only care about version 16 or greater all image infos - dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) { - result = platform; - }); - return result; + return (dyld_platform_t)_dyld_get_all_image_infos()->platform; } dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) { @@ -654,11 +725,11 @@ bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t vers } bool dyld_program_sdk_at_least(dyld_build_version_t version) { - return dyld3::dyld_sdk_at_least((mach_header*)_NSGetMachExecuteHeader(),version); + return dyld3::dyld_program_sdk_at_least(version); } bool dyld_program_minos_at_least(dyld_build_version_t version) { - return dyld3::dyld_minos_at_least((mach_header*)_NSGetMachExecuteHeader(), version); + return dyld3::dyld_program_minos_at_least(version); } // Function that walks through the load commands and calls the internal block for every version found @@ -687,10 +758,11 @@ NSObjectFileImage *objectFileImage) return dyld3::NSCreateObjectFileImageFromFile(pathName, objectFileImage); DYLD_LOCK_THIS_BLOCK; - static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + typedef NSObjectFileImageReturnCode (*funcType)(const char*, NSObjectFileImage*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromFile", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSCreateObjectFileImageFromFile", &p); return p(pathName, objectFileImage); } @@ -708,7 +780,6 @@ const void* address, size_t size, NSObjectFileImage *objectFileImage) { -#ifndef DARLING // NSCreatObjectFileImageFromMemory fail opaquely if Hardened runtime is enabled uint32_t flags; if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { @@ -717,16 +788,16 @@ NSObjectFileImage *objectFileImage) return NSObjectFileImageAccess; } } -#endif if ( gUseDyld3 ) return dyld3::NSCreateObjectFileImageFromMemory(address, size, objectFileImage); DYLD_LOCK_THIS_BLOCK; - static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL; + typedef NSObjectFileImageReturnCode (*funcType)(const void*, size_t, NSObjectFileImage*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromMemory", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSCreateObjectFileImageFromMemory", &p); return p(address, size, objectFileImage); } @@ -743,10 +814,10 @@ const char* pathName, NSObjectFileImage *objectFileImage) { DYLD_LOCK_THIS_BLOCK; - static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + typedef NSObjectFileImageReturnCode (*funcType)(const char*, NSObjectFileImage*) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSCreateCoreFileImageFromFile", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSCreateCoreFileImageFromFile", &p); return p(pathName, objectFileImage); } #endif @@ -759,10 +830,11 @@ NSObjectFileImage objectFileImage) return dyld3::NSDestroyObjectFileImage(objectFileImage); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(NSObjectFileImage) = NULL; + typedef bool (*funcType)(NSObjectFileImage); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSDestroyObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSDestroyObjectFileImage", &p); return p(objectFileImage); } @@ -777,10 +849,11 @@ uint32_t options) return dyld3::NSLinkModule(objectFileImage, moduleName, options); DYLD_LOCK_THIS_BLOCK; - static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL; + typedef NSModule (*funcType)(NSObjectFileImage, const char*, unsigned long); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSLinkModule", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSLinkModule", &p); return p(objectFileImage, moduleName, options); } @@ -800,10 +873,11 @@ NSObjectFileImage objectFileImage) return dyld3::NSSymbolDefinitionCountInObjectFileImage(objectFileImage); DYLD_LOCK_THIS_BLOCK; - static uint32_t (*p)(NSObjectFileImage) = NULL; + typedef uint32_t (*funcType)(NSObjectFileImage); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSSymbolDefinitionCountInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSSymbolDefinitionCountInObjectFileImage", &p); return p(objectFileImage); } @@ -823,10 +897,11 @@ uint32_t ordinal) return dyld3::NSSymbolDefinitionNameInObjectFileImage(objectFileImage, ordinal); DYLD_LOCK_THIS_BLOCK; - static const char* (*p)(NSObjectFileImage, uint32_t) = NULL; + typedef const char* (*funcType)(NSObjectFileImage, uint32_t); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSSymbolDefinitionNameInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSSymbolDefinitionNameInObjectFileImage", &p); return p(objectFileImage, ordinal); } @@ -843,10 +918,11 @@ NSObjectFileImage objectFileImage) return dyld3::NSSymbolReferenceCountInObjectFileImage(objectFileImage); DYLD_LOCK_THIS_BLOCK; - static uint32_t (*p)(NSObjectFileImage) = NULL; + typedef uint32_t (*funcType)(NSObjectFileImage); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSSymbolReferenceCountInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSSymbolReferenceCountInObjectFileImage", &p); return p(objectFileImage); } @@ -867,10 +943,11 @@ bool *tentative_definition) /* can be NULL */ return dyld3::NSSymbolReferenceNameInObjectFileImage(objectFileImage, ordinal, tentative_definition); DYLD_LOCK_THIS_BLOCK; - static const char* (*p)(NSObjectFileImage, uint32_t, bool*) = NULL; + typedef const char* (*funcType)(NSObjectFileImage, uint32_t, bool*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSSymbolReferenceNameInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSSymbolReferenceNameInObjectFileImage", &p); return p(objectFileImage, ordinal, tentative_definition); } @@ -888,10 +965,11 @@ const char* symbolName) return dyld3::NSIsSymbolDefinedInObjectFileImage(objectFileImage, symbolName); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(NSObjectFileImage, const char*) = NULL; + typedef bool (*funcType)(NSObjectFileImage, const char*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSIsSymbolDefinedInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSIsSymbolDefinedInObjectFileImage", &p); return p(objectFileImage, symbolName); } @@ -914,10 +992,11 @@ unsigned long *size) /* can be NULL */ return dyld3::NSGetSectionDataInObjectFileImage(objectFileImage, segmentName, sectionName, size); DYLD_LOCK_THIS_BLOCK; - static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL; + typedef void* (*funcType)(NSObjectFileImage, const char*, const char*, unsigned long*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_NSGetSectionDataInObjectFileImage", (void**)&p); + dyld_func_lookup_and_resign("__dyld_NSGetSectionDataInObjectFileImage", &p); return p(objectFileImage, segmentName, sectionName, size); } @@ -934,13 +1013,14 @@ const char* *errorString) return dyld3::NSLinkEditError(c, errorNumber, fileName, errorString); DYLD_LOCK_THIS_BLOCK; - static void (*p)(NSLinkEditErrors *c, - int *errorNumber, - const char* *fileName, - const char* *errorString) = NULL; + typedef void (*funcType)(NSLinkEditErrors *c, + int *errorNumber, + const char* *fileName, + const char* *errorString); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_link_edit_error", (void**)&p); + dyld_func_lookup_and_resign("__dyld_link_edit_error", &p); if(p != NULL) p(c, errorNumber, fileName, errorString); } @@ -954,10 +1034,11 @@ uint32_t options) return dyld3::NSUnLinkModule(module, options); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(NSModule module, uint32_t options) = NULL; + typedef bool (*funcType)(NSModule module, uint32_t options); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_unlink_module", (void**)&p); + dyld_func_lookup_and_resign("__dyld_unlink_module", &p); return p(module, options); } @@ -994,10 +1075,11 @@ uint32_t *bufsize) return dyld3::_NSGetExecutablePath(buf, bufsize); DYLD_NO_LOCK_THIS_BLOCK; - static int (*p)(char *buf, uint32_t *bufsize) = NULL; + typedef int (*funcType)(char *buf, uint32_t *bufsize); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld__NSGetExecutablePath", (void**)&p); + dyld_func_lookup_and_resign("__dyld__NSGetExecutablePath", &p); return(p(buf, bufsize)); } @@ -1008,11 +1090,15 @@ const char* symbol_name, void** address, NSModule* module) { + if ( gUseDyld3 ) + return dyld3::_dyld_lookup_and_bind(symbol_name, address, module); + DYLD_LOCK_THIS_BLOCK; - static void (*p)(const char*, void** , NSModule*) = NULL; + typedef void (*funcType)(const char*, void** , NSModule*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_lookup_and_bind", (void**)&p); + dyld_func_lookup_and_resign("__dyld_lookup_and_bind", &p); p(symbol_name, address, module); } @@ -1024,10 +1110,11 @@ void** address, NSModule* module) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(const char*, const char*, void**, NSModule*) = NULL; + typedef void (*funcType)(const char*, const char*, void**, NSModule*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_lookup_and_bind_with_hint", (void**)&p); + dyld_func_lookup_and_resign("__dyld_lookup_and_bind_with_hint", &p); p(symbol_name, library_name_hint, address, module); } @@ -1039,10 +1126,10 @@ void** address, NSModule* module) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(const char* , void**, NSModule*) = NULL; + typedef void (*funcType)(const char* , void**, NSModule*) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_lookup_and_bind_objc", (void**)&p); + dyld_func_lookup_and_resign("__dyld_lookup_and_bind_objc", &p); p(symbol_name, address, module); } #endif @@ -1054,10 +1141,11 @@ void** address, NSModule* module) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(const char*, void**, NSModule*) = NULL; + typedef void (*funcType)(const char*, void**, NSModule*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_lookup_and_bind_fully", (void**)&p); + dyld_func_lookup_and_resign("__dyld_lookup_and_bind_fully", &p); p(symbol_name, address, module); } @@ -1066,10 +1154,11 @@ _dyld_bind_fully_image_containing_address( const void* address) { DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const void*) = NULL; + typedef bool (*funcType)(const void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_bind_fully_image_containing_address", (void**)&p); + dyld_func_lookup_and_resign("__dyld_bind_fully_image_containing_address", &p); return p(address); } #endif // DEPRECATED_APIS_SUPPORTED @@ -1089,12 +1178,15 @@ void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) return dyld3::_dyld_register_func_for_add_image(func); DYLD_LOCK_THIS_BLOCK; - typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); - static void (*p)(callback_t func) = NULL; + // Func must be a "void *" because dyld itself calls it. DriverKit + // libdyld.dylib uses diversified C function pointers but its dyld (the + // plain OS one) doesn't, so it must be resigned with 0 discriminator. + typedef void (*funcType)(void *func); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_func_for_add_image", (void**)&p); - p(func); + dyld_func_lookup_and_resign("__dyld_register_func_for_add_image", &p); + p(reinterpret_cast(func)); } /* @@ -1110,12 +1202,15 @@ void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) return dyld3::_dyld_register_func_for_remove_image(func); DYLD_LOCK_THIS_BLOCK; - typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); - static void (*p)(callback_t func) = NULL; + // Func must be a "void *" because dyld itself calls it. DriverKit + // libdyld.dylib uses diversified C function pointers but its dyld (the + // plain OS one) doesn't, so it must be resigned with 0 discriminator. + typedef void (*funcType)(void *func); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_func_for_remove_image", (void**)&p); - p(func); + dyld_func_lookup_and_resign("__dyld_register_func_for_remove_image", &p); + p(reinterpret_cast(func)); } #if OBSOLETE_DYLD_API @@ -1130,11 +1225,14 @@ _dyld_register_func_for_link_module( void (*func)(NSModule module)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*func)(NSModule module)) = NULL; + // Func must be a "void *" because dyld itself calls it. DriverKit + // libdyld.dylib uses diversified C function pointers but its dyld (the + // plain OS one) doesn't, so it must be resigned with 0 discriminator. + static void (*funcType)(void *func) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_func_for_link_module", (void**)&p); - p(func); + dyld_func_lookup_and_resign("__dyld_register_func_for_link_module", &p); + p(reinterpret_cast(func)); } /* @@ -1146,11 +1244,14 @@ _dyld_register_func_for_unlink_module( void (*func)(NSModule module)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*func)(NSModule module)) = NULL; + // Func must be a "void *" because dyld itself calls it. DriverKit + // libdyld.dylib uses diversified C function pointers but its dyld (the + // plain OS one) doesn't, so it must be resigned with 0 discriminator. + static void (*funcType)(void *func) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_func_for_unlink_module", (void**)&p); - p(func); + dyld_func_lookup_and_resign("__dyld_register_func_for_unlink_module", &p); + p(reinterpret_cast(func)); } /* @@ -1162,12 +1263,14 @@ _dyld_register_func_for_replace_module( void (*func)(NSModule oldmodule, NSModule newmodule)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*func)(NSModule oldmodule, - NSModule newmodule)) = NULL; + // Func must be a "void *" because dyld itself calls it. DriverKit + // libdyld.dylib uses diversified C function pointers but its dyld (the + // plain OS one) doesn't, so it must be resigned with 0 discriminator. + typedef void (*funcType)(void *func) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_func_for_replace_module", (void**)&p); - p(func); + dyld_func_lookup_and_resign("__dyld_register_func_for_replace_module", &p); + p(reinterpret_cast(func)); } @@ -1183,12 +1286,12 @@ void **objc_module, unsigned long *size) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(NSModule module, - void **objc_module, - unsigned long *size) = NULL; + typedef void (*funcType)(NSModule module, + void **objc_module, + unsigned long *size) = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_objc_module_sect_for_module", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_objc_module_sect_for_module", &p); p(module, objc_module, size); } @@ -1210,10 +1313,11 @@ _dyld_image_count(void) return dyld3::_dyld_image_count(); DYLD_NO_LOCK_THIS_BLOCK; - static uint32_t (*p)(void) = NULL; + typedef uint32_t (*funcType)(void); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_image_count", (void**)&p); + dyld_func_lookup_and_resign("__dyld_image_count", &p); return(p()); } @@ -1224,10 +1328,11 @@ _dyld_get_image_header(uint32_t image_index) return dyld3::_dyld_get_image_header(image_index); DYLD_NO_LOCK_THIS_BLOCK; - static struct mach_header * (*p)(uint32_t image_index) = NULL; + typedef struct mach_header * (*funcType)(uint32_t image_index); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_image_header", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_image_header", &p); return(p(image_index)); } @@ -1238,10 +1343,11 @@ _dyld_get_image_vmaddr_slide(uint32_t image_index) return dyld3::_dyld_get_image_vmaddr_slide(image_index); DYLD_NO_LOCK_THIS_BLOCK; - static unsigned long (*p)(uint32_t image_index) = NULL; + typedef unsigned long (*funcType)(uint32_t image_index); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_image_vmaddr_slide", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_image_vmaddr_slide", &p); return(p(image_index)); } @@ -1252,10 +1358,11 @@ _dyld_get_image_name(uint32_t image_index) return dyld3::_dyld_get_image_name(image_index); DYLD_NO_LOCK_THIS_BLOCK; - static const char* (*p)(uint32_t image_index) = NULL; + typedef const char* (*funcType)(uint32_t image_index); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_image_name", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_image_name", &p); return(p(image_index)); } @@ -1266,6 +1373,20 @@ intptr_t _dyld_get_image_slide(const struct mach_header* mh) return dyld3::_dyld_get_image_slide(mh); } +const struct mach_header * +_dyld_get_prog_image_header() +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_prog_image_header(); + + DYLD_LOCK_THIS_BLOCK; + typedef const struct mach_header * (*funcType)(void); + static funcType __ptrauth_dyld_function_ptr p = NULL; + + if(p == NULL) + dyld_func_lookup_and_resign("__dyld_get_prog_image_header", &p); + return p(); +} #if DEPRECATED_APIS_SUPPORTED bool @@ -1275,10 +1396,11 @@ _dyld_image_containing_address(const void* address) return dyld3::_dyld_image_containing_address(address); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const void*) = NULL; + typedef bool (*funcType)(const void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_image_containing_address", (void**)&p); + dyld_func_lookup_and_resign("__dyld_image_containing_address", &p); return(p(address)); } @@ -1290,36 +1412,39 @@ const void* address) return dyld3::_dyld_get_image_header_containing_address(address); DYLD_LOCK_THIS_BLOCK; - static const struct mach_header * (*p)(const void*) = NULL; + typedef const struct mach_header * (*funcType)(const void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_image_header_containing_address", &p); return p(address); } bool _dyld_launched_prebound(void) { DYLD_LOCK_THIS_BLOCK; - static bool (*p)(void) = NULL; + typedef bool (*funcType)(void); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_launched_prebound", (void**)&p); + dyld_func_lookup_and_resign("__dyld_launched_prebound", &p); return(p()); } bool _dyld_all_twolevel_modules_prebound(void) { DYLD_LOCK_THIS_BLOCK; - static bool (*p)(void) = NULL; + typedef bool (*funcType)(void); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_all_twolevel_modules_prebound", (void**)&p); + dyld_func_lookup_and_resign("__dyld_all_twolevel_modules_prebound", &p); return(p()); } #endif // DEPRECATED_APIS_SUPPORTED -#include +#include #include #include #include @@ -1412,23 +1537,26 @@ static void shared_cache_out_of_date() // leave until dyld's that might call this are rare } +// FIXME: This is a mess. Why can't Driverkit have its own dyld? +static int cxa_atexit_thunk(void (*func)(void *), void *arg, void *dso) +{ + // Func will have come from dyld and so be signed with 0 discriminator, + // resign it appropriately before passing to the real __cxa_atexit. + func = ptrauth_auth_and_resign(func, ptrauth_key_function_pointer, 0, + ptrauth_key_function_pointer, + ptrauth_function_pointer_type_discriminator(__typeof__(func))); + return __cxa_atexit(func, arg, dso); +} + +template static FTy *resign_for_dyld(FTy *func) { + return ptrauth_auth_and_resign(func, ptrauth_key_function_pointer, + ptrauth_function_pointer_type_discriminator(__typeof__(func)), + ptrauth_key_function_pointer, 0); +} + // the table passed to dyld containing thread helpers -static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, - &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, - &shared_cache_missing, &shared_cache_out_of_date, - NULL, NULL, - &pthread_key_create, &pthread_setspecific, - &malloc_size, - &pthread_getspecific, - &__cxa_finalize, - address_of_start, - &hasPerThreadBufferFor_dlerror, - &isLaunchdOwned, - &vm_allocate, - &mmap, - &__cxa_finalize_ranges - }; +static dyld::LibSystemHelpers sHelpers = { 13 }; static const objc_opt::objc_opt_t* gObjCOpt = nullptr; // @@ -1437,8 +1565,31 @@ static const objc_opt::objc_opt_t* gObjCOpt = nullptr; // extern "C" void tlv_initializer(); void _dyld_initializer() -{ - void (*p)(dyld::LibSystemHelpers*); +{ + sHelpers.acquireGlobalDyldLock = resign_for_dyld(&dyldGlobalLockAcquire); + sHelpers.releaseGlobalDyldLock = resign_for_dyld(&dyldGlobalLockRelease); + sHelpers.getThreadBufferFor_dlerror = resign_for_dyld(&getPerThreadBufferFor_dlerror); + sHelpers.malloc = resign_for_dyld(&malloc); + sHelpers.free = resign_for_dyld(&free); + sHelpers.cxa_atexit = resign_for_dyld(&cxa_atexit_thunk); + sHelpers.dyld_shared_cache_missing = resign_for_dyld(&shared_cache_missing); + sHelpers.dyld_shared_cache_out_of_date = resign_for_dyld(&shared_cache_out_of_date); + sHelpers.acquireDyldInitializerLock = NULL; + sHelpers.releaseDyldInitializerLock = NULL; + sHelpers.pthread_key_create = resign_for_dyld(&pthread_key_create); + sHelpers.pthread_setspecific = resign_for_dyld(&pthread_setspecific); + sHelpers.malloc_size = resign_for_dyld(&malloc_size); + sHelpers.pthread_getspecific = resign_for_dyld(&pthread_getspecific); + sHelpers.cxa_finalize = resign_for_dyld(&__cxa_finalize); + sHelpers.startGlueToCallExit = address_of_start; + sHelpers.hasPerThreadBufferFor_dlerror = resign_for_dyld(&hasPerThreadBufferFor_dlerror); + sHelpers.isLaunchdOwned = resign_for_dyld(&isLaunchdOwned); + sHelpers.vm_alloc = resign_for_dyld(&vm_allocate); + sHelpers.mmap = resign_for_dyld(&mmap); + sHelpers.cxa_finalize_ranges = resign_for_dyld(&__cxa_finalize_ranges); + + typedef void (*funcType)(dyld::LibSystemHelpers*); + static funcType __ptrauth_dyld_function_ptr p = NULL; // Get the optimized objc pointer now that the cache is loaded const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); @@ -1457,7 +1608,7 @@ void _dyld_initializer() #endif } else { - _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); + dyld_func_lookup_and_resign("__dyld_register_thread_helpers", &p); if(p != NULL) p(&sHelpers); } @@ -1473,10 +1624,11 @@ int dladdr(const void* addr, Dl_info* info) result = dyld3::dladdr(addr, info); } else { DYLD_LOCK_THIS_BLOCK; - static int (*p)(const void* , Dl_info*) = NULL; + typedef int (*funcType)(const void* , Dl_info*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dladdr", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dladdr", &p); result = p(addr, info); } timer.setData4(result); @@ -1492,10 +1644,11 @@ char* dlerror() return dyld3::dlerror(); DYLD_LOCK_THIS_BLOCK; - static char* (*p)() = NULL; + typedef char* (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dlerror", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dlerror", &p); return(p()); } @@ -1509,22 +1662,22 @@ int dlclose(void* handle) } DYLD_LOCK_THIS_BLOCK; - static int (*p)(void* handle) = NULL; + typedef int (*funcType)(void* handle); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dlclose", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dlclose", &p); result = p(handle); timer.setData4(result); return result; } -void* dlopen(const char* path, int mode) +static void* dlopen_internal(const char* path, int mode, void* callerAddress) { 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)); + result = dyld3::dlopen_internal(path, mode, callerAddress); timer.setData4(result); return result; } @@ -1532,11 +1685,12 @@ void* dlopen(const char* path, int 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, void*) = NULL; + typedef void* (*funcType)(const char* path, int, void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dlopen_internal", (void**)&p); - result = p(path, mode, __builtin_return_address(0)); + dyld_func_lookup_and_resign("__dyld_dlopen_internal", &p); + result = p(path, mode, callerAddress); // 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 // @@ -1546,6 +1700,31 @@ void* dlopen(const char* path, int mode) return result; } +void* dlopen(const char* path, int mode) +{ + void* result = dlopen_internal(path, mode, __builtin_return_address(0)); + if ( result ) + return result; + + + return nullptr; +} + +void* dlopen_from(const char* path, int mode, void* addressInCaller) +{ +#if __has_feature(ptrauth_calls) + addressInCaller = __builtin_ptrauth_strip(addressInCaller, ptrauth_key_asia); +#endif + return dlopen_internal(path, mode, addressInCaller); +} + +#if !__i386__ +void* dlopen_audited(const char* path, int mode) +{ + return dlopen(path, mode); +} +#endif // !__i386__ + bool dlopen_preflight(const char* path) { dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN_PREFLIGHT, path, 0, 0); @@ -1558,10 +1737,11 @@ bool dlopen_preflight(const char* path) } DYLD_LOCK_THIS_BLOCK; - static bool (*p)(const char* path, void* callerAddress) = NULL; + typedef bool (*funcType)(const char* path, void* callerAddress); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dlopen_preflight_internal", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dlopen_preflight_internal", &p); result = p(path, __builtin_return_address(0)); timer.setData4(result); return result; @@ -1579,10 +1759,11 @@ void* dlsym(void* handle, const char* symbol) } DYLD_LOCK_THIS_BLOCK; - static void* (*p)(void* handle, const char* symbol, void *callerAddress) = NULL; + typedef void* (*funcType)(void* handle, const char* symbol, void *callerAddress); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_dlsym_internal", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dlsym_internal", &p); result = p(handle, symbol, __builtin_return_address(0)); timer.setData4(result); return result; @@ -1596,10 +1777,11 @@ const struct dyld_all_image_infos* _dyld_get_all_image_infos() return dyld3::_dyld_get_all_image_infos(); DYLD_NO_LOCK_THIS_BLOCK; - static struct dyld_all_image_infos* (*p)() = NULL; + typedef struct dyld_all_image_infos* (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_all_image_infos", &p); return p(); } @@ -1610,10 +1792,11 @@ bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) return dyld3::_dyld_find_unwind_sections(addr, info); DYLD_NO_LOCK_THIS_BLOCK; - static void* (*p)(void*, dyld_unwind_sections*) = NULL; + typedef void* (*funcType)(void*, dyld_unwind_sections*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + dyld_func_lookup_and_resign("__dyld_find_unwind_sections", &p); return p(addr, info); } #endif @@ -1624,10 +1807,11 @@ __attribute__((visibility("hidden"))) void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) { DYLD_NO_LOCK_THIS_BLOCK; - static void* (*p)(void*, long) = NULL; + typedef void* (*funcType)(void*, long); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p); + dyld_func_lookup_and_resign("__dyld_fast_stub_entry", &p); return p(loadercache, lazyinfo); } #endif @@ -1639,10 +1823,11 @@ const char* dyld_image_path_containing_address(const void* addr) return dyld3::dyld_image_path_containing_address(addr); DYLD_NO_LOCK_THIS_BLOCK; - static const char* (*p)(const void*) = NULL; + typedef const char* (*funcType)(const void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_image_path_containing_address", (void**)&p); + dyld_func_lookup_and_resign("__dyld_image_path_containing_address", &p); return p(addr); } @@ -1652,10 +1837,11 @@ const struct mach_header* dyld_image_header_containing_address(const void* addr) return dyld3::dyld_image_header_containing_address(addr); DYLD_NO_LOCK_THIS_BLOCK; - static const mach_header* (*p)(const void*) = NULL; + typedef const mach_header* (*funcType)(const void*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_image_header_containing_address", &p); return p(addr); } @@ -1666,10 +1852,11 @@ bool dyld_shared_cache_some_image_overridden() return dyld3::dyld_shared_cache_some_image_overridden(); DYLD_NO_LOCK_THIS_BLOCK; - static bool (*p)() = NULL; + typedef bool (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_shared_cache_some_image_overridden", (void**)&p); + dyld_func_lookup_and_resign("__dyld_shared_cache_some_image_overridden", &p); return p(); } @@ -1679,10 +1866,11 @@ bool _dyld_get_shared_cache_uuid(uuid_t uuid) return dyld3::_dyld_get_shared_cache_uuid(uuid); DYLD_NO_LOCK_THIS_BLOCK; - static bool (*p)(uuid_t) = NULL; + typedef bool (*funcType)(uuid_t); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_shared_cache_uuid", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_shared_cache_uuid", &p); return p(uuid); } @@ -1692,10 +1880,11 @@ const void* _dyld_get_shared_cache_range(size_t* length) return dyld3::_dyld_get_shared_cache_range(length); DYLD_NO_LOCK_THIS_BLOCK; - static const void* (*p)(size_t*) = NULL; + typedef const void* (*funcType)(size_t*); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_get_shared_cache_range", (void**)&p); + dyld_func_lookup_and_resign("__dyld_get_shared_cache_range", &p); return p(length); } @@ -1727,16 +1916,43 @@ bool _dyld_shared_cache_is_locally_built() return false; } +const char* _dyld_shared_cache_real_path(const char* path) +{ + const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); + if ( allInfo != nullptr ) { + const DyldSharedCache* cache = (const DyldSharedCache*)(allInfo->sharedCacheBaseAddress); + if ( cache != nullptr ) + return cache->getCanonicalPath(path); + } + return nullptr; +} + +bool _dyld_shared_cache_contains_path(const char* path) +{ + return _dyld_shared_cache_real_path(path) != nullptr; +} + + +uint32_t _dyld_launch_mode() +{ + if ( gUseDyld3 ) + return dyld3::_dyld_launch_mode(); + + // in dyld2 mode all flag bits are zero + return 0; +} + 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; + typedef const void (*funcType)(unsigned, const void*[], struct dyld_image_uuid_offset[]); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_images_for_addresses", (void**)&p); + dyld_func_lookup_and_resign("__dyld_images_for_addresses", &p); return p(count, addresses, infos); } @@ -1746,10 +1962,11 @@ void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const ch 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; + typedef const void (*funcType)(void (*)(const mach_header* mh, const char* path, bool unloadable)); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_for_image_loads", (void**)&p); + dyld_func_lookup_and_resign("__dyld_register_for_image_loads", &p); return p(func); } @@ -1759,16 +1976,17 @@ void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const return dyld3::_dyld_register_for_bulk_image_loads(func); DYLD_NO_LOCK_THIS_BLOCK; - static const void (*p)(void (*)(unsigned imageCount, const mach_header* mhs[], const char* paths[])) = NULL; + typedef const void (*funcType)(void (*)(unsigned imageCount, const mach_header* mhs[], const char* paths[])); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_for_bulk_image_loads", (void**)&p); + dyld_func_lookup_and_resign("__dyld_register_for_bulk_image_loads", &p); return p(func); } -bool dyld_need_closure(const char* execPath, const char* tempDir) +bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir) { - return dyld3::dyld_need_closure(execPath, tempDir); + return dyld3::dyld_need_closure(execPath, dataContainerRootDir); } bool dyld_process_is_restricted() @@ -1777,10 +1995,11 @@ bool dyld_process_is_restricted() return dyld3::dyld_process_is_restricted(); DYLD_NO_LOCK_THIS_BLOCK; - static bool (*p)() = NULL; + typedef bool (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_process_is_restricted", (void**)&p); + dyld_func_lookup_and_resign("__dyld_process_is_restricted", &p); return p(); } @@ -1790,10 +2009,11 @@ const char* dyld_shared_cache_file_path() return dyld3::dyld_shared_cache_file_path(); DYLD_NO_LOCK_THIS_BLOCK; - static const char* (*p)() = NULL; + typedef const char* (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p); + dyld_func_lookup_and_resign("__dyld_shared_cache_file_path", &p); return p(); } @@ -1803,23 +2023,32 @@ bool dyld_has_inserted_or_interposing_libraries() return dyld3::dyld_has_inserted_or_interposing_libraries(); DYLD_NO_LOCK_THIS_BLOCK; - static bool (*p)() = NULL; + typedef bool (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if (p == NULL) - _dyld_func_lookup("__dyld_has_inserted_or_interposing_libraries", (void**)&p); + dyld_func_lookup_and_resign("__dyld_has_inserted_or_interposing_libraries", &p); return p(); } +bool _dyld_has_fix_for_radar(const char *rdar) { + // There is no point in shimming this to dyld3, actual functionality can exist purely in libSystem for + // both dyld2 and dyld3. + return false; +} + + void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) { if ( gUseDyld3 ) return dyld3::dyld_dynamic_interpose(mh, array, count); DYLD_LOCK_THIS_BLOCK; - static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL; + typedef void (*funcType)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count); + static funcType __ptrauth_dyld_function_ptr p = NULL; if (p == NULL) - _dyld_func_lookup("__dyld_dynamic_interpose", (void**)&p); + dyld_func_lookup_and_resign("__dyld_dynamic_interpose", &p); p(mh, array, count); } @@ -1844,10 +2073,11 @@ void _dyld_fork_child() return dyld3::_dyld_fork_child(); DYLD_NO_LOCK_THIS_BLOCK; - static void (*p)() = NULL; + typedef void (*funcType)(); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_fork_child", (void**)&p); + dyld_func_lookup_and_resign("__dyld_fork_child", &p); return p(); } @@ -1856,13 +2086,13 @@ void _dyld_fork_child() static void* mapStartOfCache(const char* path, size_t length) { struct stat statbuf; - if ( ::stat(path, &statbuf) == -1 ) + if ( dyld3::stat(path, &statbuf) == -1 ) return NULL; if ( (size_t)statbuf.st_size < length ) return NULL; - int cache_fd = ::open(path, O_RDONLY); + int cache_fd = dyld3::open(path, O_RDONLY, 0); if ( cache_fd < 0 ) return NULL; @@ -1895,7 +2125,7 @@ static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, con if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX ) continue; if ( const dyld_cache_header* cacheHeader = (dyld_cache_header*)mapStartOfCache(cachePath, 0x00100000) ) { - if ( ::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0 ) { + if ( (::memcmp(cacheHeader, "dyld_", 5) != 0) || (::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0) ) { // wrong uuid, unmap and keep looking ::munmap((void*)cacheHeader, 0x00100000); } @@ -1928,12 +2158,11 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr } else { // look first is default location for cache files - #if __IPHONE_OS_VERSION_MIN_REQUIRED - const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR; - #else - const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR; - #endif - cacheHeader = findCacheInDirAndMap(cacheUuid, defaultSearchDir); + #if TARGET_OS_IPHONE + cacheHeader = findCacheInDirAndMap(cacheUuid, IPHONE_DYLD_SHARED_CACHE_DIR); + #else + cacheHeader = findCacheInDirAndMap(cacheUuid, MACOSX_MRM_DYLD_SHARED_CACHE_DIR); + #endif // if not there, look in extra search locations if ( cacheHeader == NULL ) { for (const char** p = extraSearchDirs; *p != NULL; ++p) { @@ -1947,7 +2176,7 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr if ( cacheHeader == NULL ) return -1; - if ( cacheHeader->mappingOffset < sizeof(dyld_cache_header) ) { + if ( cacheHeader->mappingOffset <= __offsetof(dyld_cache_header, imagesTextOffset) ) { // old cache without imagesText array if ( needToUnmap ) ::munmap((void*)cacheHeader, 0x00100000); @@ -1992,10 +2221,11 @@ bool _dyld_is_memory_immutable(const void* addr, size_t length) return dyld3::_dyld_is_memory_immutable(addr, length); DYLD_NO_LOCK_THIS_BLOCK; - static bool (*p)(const void*, size_t) = NULL; + typedef bool (*funcType)(const void*, size_t); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_is_memory_immutable", (void**)&p); + dyld_func_lookup_and_resign("__dyld_is_memory_immutable", &p); return p(addr, length); } @@ -2008,10 +2238,11 @@ void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, return dyld3::_dyld_objc_notify_register(mapped, init, unmapped); DYLD_LOCK_THIS_BLOCK; - static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL; + typedef bool (*funcType)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_objc_notify_register", (void**)&p); + dyld_func_lookup_and_resign("__dyld_objc_notify_register", &p); p(mapped, init, unmapped); } @@ -2051,9 +2282,75 @@ void _dyld_for_each_objc_protocol(const char* protocolName, void _dyld_register_driverkit_main(void (*mainFunc)(void)) { - static bool (*p)(void (*mainFunc)(void)) = NULL; + if ( gUseDyld3 ) + return dyld3::_dyld_register_driverkit_main(mainFunc); + + typedef bool (*funcType)(void *); + static funcType __ptrauth_dyld_function_ptr p = NULL; if(p == NULL) - _dyld_func_lookup("__dyld_register_driverkit_main", (void**)&p); - p(mainFunc); + dyld_func_lookup_and_resign("__dyld_register_driverkit_main", &p); + p(reinterpret_cast(mainFunc)); +} + +// This is populated in the shared cache builder, so that the ranges are protected by __DATA_CONST +// If we have a root, we can find this range in the shared cache libdyld at runtime +typedef std::pair ObjCConstantRange; + +#if TARGET_OS_OSX +__attribute__((section(("__DATA, __objc_ranges")))) +#else +__attribute__((section(("__DATA_CONST, __objc_ranges")))) +#endif +__attribute__((used)) +static ObjCConstantRange gSharedCacheObjCConstantRanges[dyld_objc_string_kind + 1]; + +static std::pair getDyldCacheConstantRanges() { + const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); + if ( allInfo != nullptr ) { + const DyldSharedCache* cache = (const DyldSharedCache*)(allInfo->sharedCacheBaseAddress); + if ( cache != nullptr ) { + return cache->getObjCConstantRange(); + } + } + return { nullptr, 0 }; +} + +bool _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr) { + assert(kind <= dyld_objc_string_kind); + // The common case should be that the value is in range, as this is a security + // check, so first test against the values in the struct. If we have a root then + // we'll take the slow path later + if ( (addr >= gSharedCacheObjCConstantRanges[kind].first) && (addr < gSharedCacheObjCConstantRanges[kind].second) ) { + // Make sure that we are pointing at the start of a constant object, not in to the middle of it + uint64_t offset = (uint64_t)addr - (uint64_t)gSharedCacheObjCConstantRanges[kind].first; + return (offset % (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize) == 0; + } + + // If we are in the shared cache, then the above check was sufficient, so this really isn't a valid constant address + extern void* __dso_handle; + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)&__dso_handle; + if ( ma->inDyldCache() ) + return false; + + // We now know we are a root, so use the pointers in the shared cache libdyld version of gSharedCacheObjCConstantRanges + static std::pair sharedCacheRanges = { nullptr, ~0ULL }; + + // FIXME: Should we fold this in as an inititalizer above? + // That would mean we need to link against somewhere to get ___cxa_guard_acquire/___cxa_guard_release + if ( sharedCacheRanges.second == ~0ULL ) + sharedCacheRanges = getDyldCacheConstantRanges(); + + // We have the range of the section in libdyld in the shared cache, now get an array of ranges from it + uint64_t numRanges = sharedCacheRanges.second / sizeof(ObjCConstantRange); + if ( kind >= numRanges ) + return false; + + const ObjCConstantRange* rangeArrayBase = (const ObjCConstantRange*)sharedCacheRanges.first; + if ( (addr >= rangeArrayBase[kind].first) && (addr < rangeArrayBase[kind].second) ) { + // Make sure that we are pointing at the start of a constant object, not in to the middle of it + uint64_t offset = (uint64_t)addr - (uint64_t)rangeArrayBase[kind].first; + return (offset % (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize) == 0; + } + return false; } diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index 5a45d1e..76d6e61 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -36,9 +36,9 @@ #include "Tracing.h" // from libc.a -extern "C" void mach_init(const char* apple[]); +extern "C" void mach_init(); extern "C" void __guard_setup(const char* apple[]); - +extern "C" void _subsystem_init(const char* apple[]); // from dyld_debugger.cpp extern void syncProcessInfo(); @@ -84,7 +84,7 @@ static void runDyldInitializers(int argc, const char* argv[], const char* envp[] // On disk, all pointers in dyld's DATA segment are chained together. // They need to be fixed up to be real pointers to run. // -static void rebaseDyld(const dyld3::MachOLoaded* dyldMH, const char* apple[]) +static void rebaseDyld(const dyld3::MachOLoaded* dyldMH) { // walk all fixups chains and rebase dyld const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)dyldMH; @@ -97,7 +97,7 @@ static void rebaseDyld(const dyld3::MachOLoaded* dyldMH, const char* apple[]) diag.assertNoError(); // now that rebasing done, initialize mach/syscall layer - mach_init(apple); + mach_init(); // mark __DATA_CONST segment in dyld as read-only (once fixups are done) ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) { @@ -108,9 +108,6 @@ static void rebaseDyld(const dyld3::MachOLoaded* dyldMH, const char* apple[]) } -#ifdef DARLING -extern "C" void sigexc_setup(void); -#endif // // This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. @@ -123,19 +120,10 @@ uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* // Emit kdebug tracepoint to indicate dyld bootstrap has started dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0); - // kernel sets up env pointer to be just past end of agv array - const char** envp = &argv[argc+1]; - - // kernel sets up apple pointer to be just past end of envp array - const char** apple = envp; - while(*apple != NULL) { ++apple; } - ++apple; - // if kernel had to slide dyld, we need to fix up load sensitive locations // we have to do this before using any global variables - rebaseDyld(dyldsMachHeader, apple); + rebaseDyld(dyldsMachHeader); - /* // kernel sets up env pointer to be just past end of agv array const char** envp = &argv[argc+1]; @@ -143,19 +131,17 @@ uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* const char** apple = envp; while(*apple != NULL) { ++apple; } ++apple; - */ // set up random value for stack canary __guard_setup(apple); -#ifdef DARLING - sigexc_setup(); -#endif #if DYLD_INITIALIZER_SUPPORT // run all C++ initializers inside dyld runDyldInitializers(argc, argv, envp, apple); #endif + _subsystem_init(apple); + // now that we are done bootstrapping dyld, call dyld's main uintptr_t appsSlide = appsMachHeader->getSlide(); return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); diff --git a/src/dyldInitialization.cpp.orig b/src/dyldInitialization.cpp.orig deleted file mode 100644 index 3bbe799..0000000 --- a/src/dyldInitialization.cpp.orig +++ /dev/null @@ -1,287 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if __x86_64__ - #include -#endif -#include "dyld.h" -#include "dyldSyscallInterface.h" - -// from dyld_gdb.cpp -extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); -extern void syncProcessInfo(); - -#ifndef MH_PIE - #define MH_PIE 0x200000 -#endif - -// currently dyld has no initializers, but if some come back, set this to non-zero -#define DYLD_INITIALIZER_SUPPORT 0 - -#if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 - #define macho_segment_command segment_command_64 - #define macho_section section_64 - #define RELOC_SIZE 3 -#else - #define LC_SEGMENT_COMMAND LC_SEGMENT - #define macho_segment_command segment_command - #define macho_section section - #define RELOC_SIZE 2 -#endif - -#if __x86_64__ - #define POINTER_RELOC X86_64_RELOC_UNSIGNED -#else - #define POINTER_RELOC GENERIC_RELOC_VANILLA -#endif - - -#if TARGET_IPHONE_SIMULATOR -const dyld::SyscallHelpers* gSyscallHelpers = NULL; -#endif - - -// -// Code to bootstrap dyld into a runnable state -// -// - -namespace dyldbootstrap { - - - -#if DYLD_INITIALIZER_SUPPORT - -typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[]); - -extern const Initializer inits_start __asm("section$start$__DATA$__mod_init_func"); -extern const Initializer inits_end __asm("section$end$__DATA$__mod_init_func"); - -// -// For a regular executable, the crt code calls dyld to run the executables initializers. -// For a static executable, crt directly runs the initializers. -// dyld (should be static) but is a dynamic executable and needs this hack to run its own initializers. -// We pass argc, argv, etc in case libc.a uses those arguments -// -static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, int argc, const char* argv[], const char* envp[], const char* apple[]) -{ - for (const Initializer* p = &inits_start; p < &inits_end; ++p) { - (*p)(argc, argv, envp, apple); - } -} -#endif // DYLD_INITIALIZER_SUPPORT - - -// -// The kernel may have slid a Position Independent Executable -// -static uintptr_t slideOfMainExecutable(const struct macho_header* mh) -{ - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; - if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0)) { - return (uintptr_t)mh - segCmd->vmaddr; - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return 0; -} - - -// -// If the kernel does not load dyld at its preferred address, we need to apply -// fixups to various initialized parts of the __DATA segment -// -static void rebaseDyld(const struct macho_header* mh, intptr_t slide) -{ - // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) - // and get interesting pointers into dyld - 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; -#if __x86_64__ - const struct macho_segment_command* firstWritableSeg = NULL; -#endif - const struct dysymtab_command* dynamicSymbolTable = 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; - 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) { - const uint8_t type = sect->flags & SECTION_TYPE; - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { - // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) - const uint32_t pointerCount = (uint32_t)(sect->size / sizeof(uintptr_t)); - uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); - for (uint32_t j=0; j < pointerCount; ++j) { - symbolPointers[j] += slide; - } - } - } -#if __x86_64__ - if ( (firstWritableSeg == NULL) && (seg->initprot & VM_PROT_WRITE) ) - firstWritableSeg = seg; -#endif - } - break; - case LC_DYSYMTAB: - dynamicSymbolTable = (struct dysymtab_command *)cmd; - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // use reloc's to rebase all random data pointers -#if __x86_64__ - const uintptr_t relocBase = firstWritableSeg->vmaddr + slide; -#else - const uintptr_t relocBase = (uintptr_t)mh; -#endif - const relocation_info* const relocsStart = (struct relocation_info*)(linkEditSeg->vmaddr + slide + dynamicSymbolTable->locreloff - linkEditSeg->fileoff); - const relocation_info* const relocsEnd = &relocsStart[dynamicSymbolTable->nlocrel]; - for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - if ( reloc->r_length != RELOC_SIZE ) - throw "relocation in dyld has wrong size"; - - if ( reloc->r_type != POINTER_RELOC ) - throw "relocation in dyld has wrong type"; - - // update pointer by amount dyld slid - *((uintptr_t*)(reloc->r_address + relocBase)) += slide; - } -} - - -extern "C" void mach_init(const char* apple[]); -extern "C" void __guard_setup(const char* apple[]); -extern "C" void sigexc_setup(); - - -// -// This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. -// In dyld we have to do this manually. -// -uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], - intptr_t slide, const struct macho_header* dyldsMachHeader, - uintptr_t* startGlue) -{ - // 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); - } - - // kernel sets up env pointer to be just past end of agv array - const char** envp = &argv[argc+1]; - - // kernel sets up apple pointer to be just past end of envp array - const char** apple = envp; - while(*apple != NULL) { ++apple; } - ++apple; - - // allow dyld to use mach messaging - mach_init(apple); - - // set up random value for stack canary - __guard_setup(apple); - -#if DYLD_INITIALIZER_SUPPORT - // run all C++ initializers inside dyld - runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); -#endif - -#ifdef DARLING - sigexc_setup(); -#endif - - // now that we are done bootstrapping dyld, call dyld's main - uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); - return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); -} - - -#if TARGET_IPHONE_SIMULATOR - -extern "C" uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], - const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, - const dyld::SyscallHelpers*, uintptr_t* startGlue); - - -uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], - const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, - const dyld::SyscallHelpers* sc, uintptr_t* startGlue) -{ - // if simulator dyld loaded slid, it needs to rebase itself - // we have to do this before using any global variables - if ( dyldSlide != 0 ) { - rebaseDyld(dyldMH, dyldSlide); - } - - // save table of syscall pointers - gSyscallHelpers = sc; - - // allow dyld to use mach messaging - mach_init(); - - // set up random value for stack canary - __guard_setup(apple); - - // setup gProcessInfo to point to host dyld's struct - dyld::gProcessInfo = (struct dyld_all_image_infos*)(sc->getProcessInfo()); - syncProcessInfo(); - - // now that we are done bootstrapping dyld, call dyld's main - uintptr_t appsSlide = slideOfMainExecutable(mainExecutableMH); - return dyld::_main(mainExecutableMH, appsSlide, argc, argv, envp, apple, startGlue); -} -#endif - - -} // end of namespace - - - - diff --git a/src/dyldInitialization.cpp.rej b/src/dyldInitialization.cpp.rej deleted file mode 100644 index 3ca5690..0000000 --- a/src/dyldInitialization.cpp.rej +++ /dev/null @@ -1,197 +0,0 @@ ---- src/dyldInitialization.cpp -+++ src/dyldInitialization.cpp -@@ -81,116 +57,48 @@ static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, i - - - // --// The kernel may have slid a Position Independent Executable -+// On disk, all pointers in dyld's DATA segment are chained together. -+// They need to be fixed up to be real pointers to run. - // --static uintptr_t slideOfMainExecutable(const struct macho_header* mh) -+static void rebaseDyld(const dyld3::MachOLoaded* dyldMH) - { -- const uint32_t cmd_count = mh->ncmds; -- const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); -- const struct load_command* cmd = cmds; -- for (uint32_t i = 0; i < cmd_count; ++i) { -- if ( cmd->cmd == LC_SEGMENT_COMMAND ) { -- const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; -- if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0)) { -- return (uintptr_t)mh - segCmd->vmaddr; -- } -- } -- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); -- } -- return 0; -+ // walk all fixups chains and rebase dyld -+ const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)dyldMH; -+ assert(ma->hasChainedFixups()); -+ uintptr_t slide = (long)ma; // all fixup chain based images have a base address of zero, so slide == load address -+ __block Diagnostics diag; -+ ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) { -+ ma->fixupAllChainedFixups(diag, starts, slide, dyld3::Array(), nullptr); -+ }); -+ diag.assertNoError(); -+ -+ // now that rebasing done, initialize mach/syscall layer -+ mach_init(); -+ -+ // mark __DATA_CONST segment in dyld as read-only (once fixups are done) -+ ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) { -+ if ( info.readOnlyData ) { -+ ::mprotect(((uint8_t*)(dyldMH))+info.vmAddr, (size_t)info.vmSize, VM_PROT_READ); -+ } -+ }); - } - - --// --// If the kernel does not load dyld at its preferred address, we need to apply --// fixups to various initialized parts of the __DATA segment --// --static void rebaseDyld(const struct macho_header* mh, intptr_t slide) --{ -- // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) -- // and get interesting pointers into dyld -- 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; --#if __x86_64__ -- const struct macho_segment_command* firstWritableSeg = NULL; --#endif -- const struct dysymtab_command* dynamicSymbolTable = 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; -- 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) { -- const uint8_t type = sect->flags & SECTION_TYPE; -- if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { -- // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) -- const uint32_t pointerCount = (uint32_t)(sect->size / sizeof(uintptr_t)); -- uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); -- for (uint32_t j=0; j < pointerCount; ++j) { -- symbolPointers[j] += slide; -- } -- } -- } --#if __x86_64__ -- if ( (firstWritableSeg == NULL) && (seg->initprot & VM_PROT_WRITE) ) -- firstWritableSeg = seg; --#endif -- } -- break; -- case LC_DYSYMTAB: -- dynamicSymbolTable = (struct dysymtab_command *)cmd; -- break; -- } -- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); -- } -- -- // use reloc's to rebase all random data pointers --#if __x86_64__ -- const uintptr_t relocBase = firstWritableSeg->vmaddr + slide; --#else -- const uintptr_t relocBase = (uintptr_t)mh; --#endif -- const relocation_info* const relocsStart = (struct relocation_info*)(linkEditSeg->vmaddr + slide + dynamicSymbolTable->locreloff - linkEditSeg->fileoff); -- const relocation_info* const relocsEnd = &relocsStart[dynamicSymbolTable->nlocrel]; -- for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { -- if ( reloc->r_length != RELOC_SIZE ) -- throw "relocation in dyld has wrong size"; -- -- if ( reloc->r_type != POINTER_RELOC ) -- throw "relocation in dyld has wrong type"; -- -- // update pointer by amount dyld slid -- *((uintptr_t*)(reloc->r_address + relocBase)) += slide; -- } --} -- -- --extern "C" void mach_init(); --extern "C" void __guard_setup(const char* apple[]); -- - - // - // This is code to bootstrap dyld. This work in normally done for a program by dyld and crt. - // In dyld we have to do this manually. - // --uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], -- intptr_t slide, const struct macho_header* dyldsMachHeader, -- uintptr_t* startGlue) -+uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[], -+ const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue) - { -+ -+ // Emit kdebug tracepoint to indicate dyld bootstrap has started -+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0); -+ - // 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); -- } -- -- // allow dyld to use mach messaging -- mach_init(); -+ rebaseDyld(dyldsMachHeader); - - // kernel sets up env pointer to be just past end of agv array - const char** envp = &argv[argc+1]; -@@ -205,37 +113,31 @@ uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* - - #if DYLD_INITIALIZER_SUPPORT - // run all C++ initializers inside dyld -- runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); -+ runDyldInitializers(argc, argv, envp, apple); - #endif - - // now that we are done bootstrapping dyld, call dyld's main -- uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); -- return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); -+ uintptr_t appsSlide = appsMachHeader->getSlide(); -+ return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); - } - - --#if TARGET_IPHONE_SIMULATOR -+#if TARGET_OS_SIMULATOR - - extern "C" uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], -- const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, -+ const dyld3::MachOLoaded* mainExecutableMH, const dyld3::MachOLoaded* dyldMH, uintptr_t dyldSlide, - const dyld::SyscallHelpers*, uintptr_t* startGlue); - -- -+ - uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], -- const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, -+ const dyld3::MachOLoaded* mainExecutableMH, const dyld3::MachOLoaded* dyldSimMH, uintptr_t dyldSlide, - const dyld::SyscallHelpers* sc, uintptr_t* startGlue) - { -- // if simulator dyld loaded slid, it needs to rebase itself -- // we have to do this before using any global variables -- if ( dyldSlide != 0 ) { -- rebaseDyld(dyldMH, dyldSlide); -- } -+ // save table of syscall pointers -+ gSyscallHelpers = sc; - -- // save table of syscall pointers -- gSyscallHelpers = sc; -- -- // allow dyld to use mach messaging -- mach_init(); -+ // dyld_sim uses chained rebases, so it always need to be fixed up -+ rebaseDyld(dyldSimMH); - - // set up random value for stack canary - __guard_setup(apple); diff --git a/src/dyldLibSystemGlue.c b/src/dyldLibSystemGlue.c index 486bde3..bcdb1e0 100644 --- a/src/dyldLibSystemGlue.c +++ b/src/dyldLibSystemGlue.c @@ -48,7 +48,7 @@ const char* __progname = NULL; // struct __DATA__dyld { long lazy; - int (*lookup)(const char*, void**); + void *lookup; // ProgramVars const void* mh; int* NXArgcPtr; @@ -60,7 +60,7 @@ struct __DATA__dyld { static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, 0, NULL, &NXArgc, &NXArgv, &environ, &__progname }; -#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED +#if __arm__ && TARGET_OS_OSX // // For historical reasons, gcc and llvm-gcc added -ldylib1.o to the link line of armv6 // dylibs when targeting MacOSX (but not iOS). clang cleans up that mistake, but doing @@ -68,10 +68,13 @@ static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__D // dylib1.o is used, it overrides this, otherwise this implementation is used. __attribute__((weak)) #endif -//__attribute__((visibility("hidden"))) +__attribute__((visibility("hidden"))) int _dyld_func_lookup(const char* dyld_func_name, void **address) { - return (*myDyldSection.lookup)(dyld_func_name, address); + // Cast lookup function here to resign from dyld's 0-discriminator to a real + // function pointer if needed. + int (*lookupFn)(const char*, void**) = myDyldSection.lookup; + return lookupFn(dyld_func_name, address); } #if TARGET_OS_IOS && !TARGET_OS_SIMULATOR diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index b2331e9..2bdafba 100644 --- a/src/dyldNew.cpp +++ b/src/dyldNew.cpp @@ -51,7 +51,10 @@ struct dyld_static_pool { dyld_static_pool* previousPool; uint8_t* current; uint8_t* end; - uint8_t pool[1]; + + // libunwind, and probably others, need the pool to be 16-byte aligned as malloc guarantees that + __attribute__((__aligned__(16))) + uint8_t pool[1]; }; // allocate initial pool independently of pool header to take less space on disk @@ -68,6 +71,9 @@ void* malloc(size_t size) return p; } else { + // keep allocations 16-byte aligned + size = ((size + 15) & -16); + if ( size > DYLD_POOL_CHUNK_SIZE ) { dyld::log("dyld malloc overflow: size=%lu\n", size); dyld::halt("dyld malloc overflow\n"); @@ -147,10 +153,7 @@ void* calloc(size_t count, size_t size) void* realloc(void *ptr, size_t size) { void* result = malloc(size); -#ifdef DARLING - if (ptr) -#endif - memcpy(result, ptr, size); + memcpy(result, ptr, size); return result; } diff --git a/src/dyldStartup.S b/src/dyldStartup.s similarity index 98% rename from src/dyldStartup.S rename to src/dyldStartup.s index b1083f3..8b9b6cc 100644 --- a/src/dyldStartup.S +++ b/src/dyldStartup.s @@ -212,7 +212,7 @@ Lapple: ldr r4, [r3] -#if __arm64__ +#if __arm64__ && !TARGET_OS_SIMULATOR .text .align 2 .globl __dyld_start @@ -287,7 +287,7 @@ Lapple: ldr w4, [x3] br x16 #endif -#endif // __arm64__ +#endif // __arm64__ && !TARGET_OS_SIMULATOR // When iOS 10.0 simulator runs on 10.11, abort_with_payload() does not exist, @@ -297,11 +297,11 @@ Lapple: ldr w4, [x3] .align 2 .globl _dyld_fatal_error _dyld_fatal_error: +#if __arm64__ || __arm64e__ + brk #3 +#else int3 +#endif nop #endif - - - - diff --git a/src/dyldSyscallInterface.h b/src/dyldSyscallInterface.h index 18aec04..0dc540f 100644 --- a/src/dyldSyscallInterface.h +++ b/src/dyldSyscallInterface.h @@ -26,14 +26,36 @@ #ifndef __DYLD_SYSCALL_HELPERS__ #define __DYLD_SYSCALL_HELPERS__ +#include + #include +#if __has_include() +#include +#else +__BEGIN_DECLS +extern int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags); +__END_DECLS +#endif +#include +#include +#include +#include #include -#include "../dyld_kernel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DYLD_SYSCALL_VTABLE_ENTRY(x) __typeof__ (x) *x #if __cplusplus namespace dyld { #endif - // // This file contains the table of function pointers the host dyld supplies // to the iOS simulator dyld. @@ -41,76 +63,89 @@ namespace dyld { struct SyscallHelpers { uintptr_t version; - int (*open)(const char* path, int oflag, int extra); - int (*close)(int fd); - ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset); - ssize_t (*write)(int fd, const void* buf, size_t nbyte); - void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); - int (*munmap)(void* addr, size_t len); - int (*madvise)(void* addr, size_t len, int advice); - int (*stat)(const char* path, struct stat* buf); - int (*fcntl)(int fildes, int cmd, void* result); - int (*ioctl)(int fildes, unsigned long request, void* result); - int (*issetugid)(void); - char* (*getcwd)(char* buf, size_t size); - char* (*realpath)(const char* file_name, char* resolved_name); - kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags); - kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size); - kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot); + DYLD_SYSCALL_VTABLE_ENTRY(open); + DYLD_SYSCALL_VTABLE_ENTRY(close); + DYLD_SYSCALL_VTABLE_ENTRY(pread); + DYLD_SYSCALL_VTABLE_ENTRY(write); + DYLD_SYSCALL_VTABLE_ENTRY(mmap); + DYLD_SYSCALL_VTABLE_ENTRY(munmap); + DYLD_SYSCALL_VTABLE_ENTRY(madvise); + DYLD_SYSCALL_VTABLE_ENTRY(stat); + DYLD_SYSCALL_VTABLE_ENTRY(fcntl); + DYLD_SYSCALL_VTABLE_ENTRY(ioctl); + DYLD_SYSCALL_VTABLE_ENTRY(issetugid); + DYLD_SYSCALL_VTABLE_ENTRY(getcwd); + DYLD_SYSCALL_VTABLE_ENTRY(realpath); + DYLD_SYSCALL_VTABLE_ENTRY(vm_allocate); + DYLD_SYSCALL_VTABLE_ENTRY(vm_deallocate); + DYLD_SYSCALL_VTABLE_ENTRY(vm_protect); void (*vlog)(const char* format, va_list list); void (*vwarn)(const char* format, va_list list); - int (*pthread_mutex_lock)(pthread_mutex_t* m); - int (*pthread_mutex_unlock)(pthread_mutex_t* m); - mach_port_t (*mach_thread_self)(void); - kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name); - mach_port_name_t(*task_self_trap)(void); - kern_return_t (*mach_timebase_info)(mach_timebase_info_t info); + DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_lock); + DYLD_SYSCALL_VTABLE_ENTRY(pthread_mutex_unlock); + DYLD_SYSCALL_VTABLE_ENTRY(mach_thread_self); + DYLD_SYSCALL_VTABLE_ENTRY(mach_port_deallocate); + DYLD_SYSCALL_VTABLE_ENTRY(task_self_trap); + DYLD_SYSCALL_VTABLE_ENTRY(mach_timebase_info); +#if OSATOMIC_USE_INLINED bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value); void (*OSMemoryBarrier)(void); +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" + DYLD_SYSCALL_VTABLE_ENTRY(OSAtomicCompareAndSwapPtrBarrier); + DYLD_SYSCALL_VTABLE_ENTRY(OSMemoryBarrier); +#pragma clang diagnostic pop +#endif void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; int* (*errnoAddress)(void); - uint64_t (*mach_absolute_time)(void); + DYLD_SYSCALL_VTABLE_ENTRY(mach_absolute_time); // Added in version 2 - kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); + DYLD_SYSCALL_VTABLE_ENTRY(thread_switch); // Added in version 3 - DIR* (*opendir)(const char* path); - int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); - int (*closedir)(DIR* dirp); + DYLD_SYSCALL_VTABLE_ENTRY(opendir); + DYLD_SYSCALL_VTABLE_ENTRY(readdir_r); + DYLD_SYSCALL_VTABLE_ENTRY(closedir); // Added in version 4 void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); // Added in version 5 - int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize); - int (*getpid)(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); + DYLD_SYSCALL_VTABLE_ENTRY(proc_regionfilename); + DYLD_SYSCALL_VTABLE_ENTRY(getpid); + DYLD_SYSCALL_VTABLE_ENTRY(mach_port_insert_right); + DYLD_SYSCALL_VTABLE_ENTRY(mach_port_allocate); + DYLD_SYSCALL_VTABLE_ENTRY(mach_msg); // Added in version 6 - void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags); + DYLD_SYSCALL_VTABLE_ENTRY(abort_with_payload); // Add in version 7 - kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); - kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); - kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt); - kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache); - kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state); - kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state); - 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); - }; + DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_image_infos); + DYLD_SYSCALL_VTABLE_ENTRY(task_unregister_dyld_image_infos); + DYLD_SYSCALL_VTABLE_ENTRY(task_get_dyld_image_infos); + DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_shared_cache_image_info); + DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_set_dyld_state); + DYLD_SYSCALL_VTABLE_ENTRY(task_register_dyld_get_process_state); + DYLD_SYSCALL_VTABLE_ENTRY(task_info); + DYLD_SYSCALL_VTABLE_ENTRY(thread_info); + // Add in version 8 + DYLD_SYSCALL_VTABLE_ENTRY(kdebug_is_enabled); + DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace); + // Add in version 9 + DYLD_SYSCALL_VTABLE_ENTRY(kdebug_trace_string); + // Add in version 10 + DYLD_SYSCALL_VTABLE_ENTRY(amfi_check_dyld_policy_self); + // 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 + DYLD_SYSCALL_VTABLE_ENTRY(mach_msg_destroy); + DYLD_SYSCALL_VTABLE_ENTRY(mach_port_construct); + DYLD_SYSCALL_VTABLE_ENTRY(mach_port_destruct); + // Add in version 13 + DYLD_SYSCALL_VTABLE_ENTRY(fstat); + DYLD_SYSCALL_VTABLE_ENTRY(vm_copy); + // Add in version 14 + DYLD_SYSCALL_VTABLE_ENTRY(task_dyld_process_info_notify_get); + }; extern const struct SyscallHelpers* gSyscallHelpers; diff --git a/src/dyldSyscallInterface.h.orig b/src/dyldSyscallInterface.h.orig deleted file mode 100644 index b67004a..0000000 --- a/src/dyldSyscallInterface.h.orig +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004-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 __DYLD_SYSCALL_HELPERS__ -#define __DYLD_SYSCALL_HELPERS__ - -#include -#include -#include "../dyld_kernel.h" - -#if __cplusplus -namespace dyld { -#endif - - // - // This file contains the table of function pointers the host dyld supplies - // to the iOS simulator dyld. - // - struct SyscallHelpers - { - uintptr_t version; - int (*open)(const char* path, int oflag, int extra); - int (*close)(int fd); - ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset); - ssize_t (*write)(int fd, const void* buf, size_t nbyte); - void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); - int (*munmap)(void* addr, size_t len); - int (*madvise)(void* addr, size_t len, int advice); - int (*stat)(const char* path, struct stat* buf); - int (*fcntl)(int fildes, int cmd, void* result); - int (*ioctl)(int fildes, unsigned long request, void* result); - int (*issetugid)(void); - char* (*getcwd)(char* buf, size_t size); - char* (*realpath)(const char* file_name, char* resolved_name); - kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags); - kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size); - kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot); - void (*vlog)(const char* format, va_list list); - void (*vwarn)(const char* format, va_list list); - int (*pthread_mutex_lock)(pthread_mutex_t* m); - int (*pthread_mutex_unlock)(pthread_mutex_t* m); - mach_port_t (*mach_thread_self)(void); - kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name); - mach_port_name_t(*task_self_trap)(void); - kern_return_t (*mach_timebase_info)(mach_timebase_info_t info); - 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)(); - // Added in version 2 - kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); - // Added in version 3 - DIR* (*opendir)(const char* path); - int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); - int (*closedir)(DIR* dirp); - // Added in version 4 - void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); - void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); - // Added in version 5 - int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize); - int (*getpid)(); - kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly); - kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); - kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); - // Added in version 6 - void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags); - // Add in version 7 - kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); - kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); - kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt); - kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache); - kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state); - kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state); - }; - - extern const struct SyscallHelpers* gSyscallHelpers; - - -#if __cplusplus -} -#endif - -#endif diff --git a/src/dyld_debugger.cpp b/src/dyld_debugger.cpp index bb24ecc..072c4b6 100644 --- a/src/dyld_debugger.cpp +++ b/src/dyld_debugger.cpp @@ -38,10 +38,10 @@ extern "C" void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]); -#if __IPHONE_OS_VERSION_MIN_REQUIRED - #define INITIAL_UUID_IMAGE_COUNT 4 -#else +#if TARGET_OS_OSX #define INITIAL_UUID_IMAGE_COUNT 32 +#else + #define INITIAL_UUID_IMAGE_COUNT 4 #endif VECTOR_NEVER_DESTRUCTED(dyld_image_info); @@ -50,6 +50,10 @@ VECTOR_NEVER_DESTRUCTED(dyld_uuid_info); static std::vector sImageInfos; static std::vector sImageUUIDs; +#if __x86_64__ +static std::vector sAotImageInfos; +#endif + size_t allImagesCount() { return sImageInfos.size(); @@ -92,6 +96,26 @@ void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) dyld::gProcessInfo->infoArray = &sImageInfos[0]; } +#if __x86_64__ +void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[]) +{ + if (sAotImageInfos.size() == 0) { + sAotImageInfos.reserve(INITIAL_IMAGE_COUNT); + } + // set aotInfoArray to NULL to denote it is in-use + dyld::gProcessInfo->aotInfoArray = NULL; + + for (uint32_t i = 0; i < aotInfoCount; ++i) { + sAotImageInfos.push_back(aotInfo[i]); + } + dyld::gProcessInfo->aotInfoCount = (uint32_t)sAotImageInfos.size(); + dyld::gProcessInfo->aotInfoArrayChangeTimestamp = mach_absolute_time(); + + // set aotInfoArray back to base address of vector (other process can now read) + dyld::gProcessInfo->aotInfoArray = &sAotImageInfos[0]; +} +#endif + #if TARGET_OS_SIMULATOR // called once in dyld_sim start up to copy image list from host dyld to sImageInfos void syncProcessInfo() @@ -229,10 +253,10 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) = { - 16, 0, {NULL}, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, + 17, 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, {0}, "/usr/lib/dyld", {0}, {0}, 0, 0, NULL, 0 }; struct dyld_shared_cache_ranges dyld_shared_cache_ranges; diff --git a/src/dyld_process_info.cpp b/src/dyld_process_info.cpp index b195336..437d6ac 100644 --- a/src/dyld_process_info.cpp +++ b/src/dyld_process_info.cpp @@ -22,6 +22,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include #include @@ -48,46 +49,48 @@ RemoteBuffer& RemoteBuffer::operator=(RemoteBuffer&& other) { std::swap(_localAddress, other._localAddress); std::swap(_size, other._size); std::swap(_kr, other._kr); - std::swap(_shared, other._shared); return *this; } -RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0), _kr(KERN_SUCCESS), _shared(false) {} -RemoteBuffer::RemoteBuffer(std::tuple T) - : _localAddress(std::get<0>(T)), _size(std::get<1>(T)), _kr(std::get<2>(T)), _shared(std::get<3>(T)) {} +RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0), _kr(KERN_SUCCESS) {} +RemoteBuffer::RemoteBuffer(std::tuple T) + : _localAddress(std::get<0>(T)), _size(std::get<1>(T)), _kr(std::get<2>(T)) {} -RemoteBuffer::RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation) -: RemoteBuffer(RemoteBuffer::create(task, remote_address, remote_size, shared, allow_truncation)) {}; +RemoteBuffer::RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool allow_truncation) +: RemoteBuffer(RemoteBuffer::create(task, remote_address, remote_size, allow_truncation)) {}; std::pair -RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, vm_size_t size, bool shared) { +RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, vm_size_t size) { + static kern_return_t (*mvrn)(vm_map_t, mach_vm_address_t*, mach_vm_size_t, mach_vm_offset_t, int, vm_map_read_t, mach_vm_address_t, + boolean_t, vm_prot_t*, vm_prot_t*, vm_inherit_t) = nullptr; vm_prot_t cur_protection = VM_PROT_NONE; - vm_prot_t max_protection = VM_PROT_NONE; - int flags; + vm_prot_t max_protection = VM_PROT_READ; if (size == 0) { return std::make_pair(MACH_VM_MIN_ADDRESS, KERN_INVALID_ARGUMENT); } - if (shared) { - flags = VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR; - } else { - // - // Since we are getting rid of the flag probing we have to make sure that simulator libdyld's do not use VM_FLAGS_RESILIENT_MEDIA - // FIXME: Remove this when simulator builds do not support back deployment to 10.14 -#if TARGET_OS_SIMULATOR - flags = VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN; -#else - flags = VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN | VM_FLAGS_RESILIENT_MEDIA; -#endif - } mach_vm_address_t localAddress = 0; - auto kr = mach_vm_remap(mach_task_self(), +#if TARGET_OS_SIMULATOR + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mvrn = (kern_return_t (*)(vm_map_t, mach_vm_address_t*, mach_vm_size_t, mach_vm_offset_t, int, vm_map_read_t, mach_vm_address_t, + boolean_t, vm_prot_t*, vm_prot_t*, vm_inherit_t))dlsym(RTLD_DEFAULT, "mach_vm_remap_new"); + if (mvrn == nullptr) { + // We are running on a system that does not support task_read ports, use the old call + mvrn = (kern_return_t (*)(vm_map_t, mach_vm_address_t*, mach_vm_size_t, mach_vm_offset_t, int, vm_map_read_t, mach_vm_address_t, + boolean_t, vm_prot_t*, vm_prot_t*, vm_inherit_t))dlsym(RTLD_DEFAULT, "mach_vm_remap"); + } + }); +#else + mvrn = &mach_vm_remap_new; +#endif + auto kr = mvrn(mach_task_self(), &localAddress, size, 0, // mask - flags, + VM_FLAGS_ANYWHERE | VM_FLAGS_RESILIENT_CODESIGN | VM_FLAGS_RESILIENT_MEDIA, task, remote_address, - !shared, + true, &cur_protection, &max_protection, VM_INHERIT_NONE); @@ -101,68 +104,51 @@ RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, vm_size_t size, // we are copying some memory in the middle of a mach-o that is on a USB drive that is disconnected after we perform // the mapping). Once we copy them into a local buffer the memory will be handled by the default pager instead of // potentially being backed by the mmap pager, and thus will be guaranteed not to mutate out from under us. - if (!shared) { - void* buffer = malloc(size); - if (buffer == nullptr) { - (void)vm_deallocate(mach_task_self(), localAddress, size); - return std::make_pair(MACH_VM_MIN_ADDRESS, kr); - } - memcpy(buffer, (void *)localAddress, size); - (void)vm_deallocate(mach_task_self(), localAddress, size); - return std::make_pair((vm_address_t)buffer, KERN_SUCCESS); + void* buffer = malloc(size); + if (buffer == nullptr) { + (void)vm_deallocate(mach_task_self(), (vm_address_t)localAddress, size); + return std::make_pair(MACH_VM_MIN_ADDRESS, KERN_NO_SPACE); } - // A shared buffer was requested, if the permissions are not correct deallocate the region and return failure - if (cur_protection != (VM_PROT_READ|VM_PROT_WRITE)) { - if (localAddress != 0) { - (void)vm_deallocate(mach_task_self(), (size_t)localAddress, size); - } - return std::make_pair(MACH_VM_MIN_ADDRESS, KERN_PROTECTION_FAILURE); - } - // We have a successfully created shared buffer with the correct permissions, return it - return std::make_pair(localAddress, KERN_SUCCESS); + memcpy(buffer, (void *)localAddress, size); + (void)vm_deallocate(mach_task_self(), (vm_address_t)localAddress, size); + return std::make_pair((vm_address_t)buffer, KERN_SUCCESS); } -std::tuple RemoteBuffer::create(task_t task, +std::tuple RemoteBuffer::create(task_t task, mach_vm_address_t remote_address, size_t size, - bool shared, bool allow_truncation) { mach_vm_address_t localAddress; kern_return_t kr; // Try the initial map - std::tie(localAddress, kr) = map(task, remote_address, size, shared); - if (kr == KERN_SUCCESS) return std::make_tuple(localAddress, size, kr, shared); + std::tie(localAddress, kr) = map(task, remote_address, size); + if (kr == KERN_SUCCESS) return std::make_tuple(localAddress, size, kr); // The first attempt failed, truncate if possible and try again. We only need to try once since the largest // truncatable buffer we map is less than a single page. To be more general we would need to try repeatedly in a // loop. if (allow_truncation) { size = PAGE_SIZE - remote_address%PAGE_SIZE; - std::tie(localAddress, kr) = map(task, remote_address, size, shared); - if (kr == KERN_SUCCESS) return std::make_tuple(localAddress, size, kr, shared); + std::tie(localAddress, kr) = map(task, remote_address, size); + if (kr == KERN_SUCCESS) return std::make_tuple(localAddress, size, kr); } // If we reach this then the mapping completely failed - return std::make_tuple(MACH_VM_MIN_ADDRESS, 0, kr, shared); + return std::make_tuple(MACH_VM_MIN_ADDRESS, 0, kr); } RemoteBuffer::~RemoteBuffer() { if (!_localAddress) { return; } - - if (_shared) { - (void)vm_deallocate(mach_task_self(), (vm_address_t)_localAddress, _size); - } else { - free((void*)_localAddress); - } + free((void*)_localAddress); } void *RemoteBuffer::getLocalAddress() const { return (void *)_localAddress; } size_t RemoteBuffer::getSize() const { return _size; } kern_return_t RemoteBuffer::getKernelReturn() const { 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)) { +void withRemoteBuffer(task_t task, mach_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 krSink = KERN_SUCCESS; if (kr == nullptr) { kr = &krSink; } - RemoteBuffer buffer(task, remote_address, remote_size, shared, allow_truncation); + RemoteBuffer buffer(task, remote_address, remote_size, allow_truncation); *kr = buffer.getKernelReturn(); if (*kr == KERN_SUCCESS) { block(buffer.getLocalAddress(), buffer.getSize()); @@ -195,13 +181,15 @@ struct __attribute__((visibility("hidden"))) dyld_process_info_base { template static dyld_process_info_ptr makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr); - std::atomic& 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); } - dyld_platform_t platform() const { return _platform; } + std::atomic& retainCount() const { return _retainCount; } + dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); } + dyld_process_aot_cache_info* aotCacheInfo() const { return (dyld_process_aot_cache_info*)(((char*)this) + _aotCacheInfoOffset); } + dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); } + dyld_platform_t platform() const { return _platform; } - 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 forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const; + void forEachAotImage(bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) const; + void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const; bool reserveSpace(size_t space) { if (_freeSpace < space) { return false; } @@ -238,16 +226,14 @@ private: uint64_t size; }; - dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, size_t totalSize); + dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, unsigned aotImageCount, size_t totalSize); void* operator new (size_t, void* buf) { return buf; } -#ifndef DARLING static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); } -#else - static bool inCache(uint64_t addr) { return false; } -#endif bool addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); + bool addAotImage(dyld_aot_image_info_64 aotImageInfo); + kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath); bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } @@ -263,13 +249,17 @@ private: mutable std::atomic _retainCount; const uint32_t _cacheInfoOffset; + const uint32_t _aotCacheInfoOffset; const uint32_t _stateInfoOffset; const uint32_t _imageInfosOffset; + const uint32_t _aotImageInfosOffset; const uint32_t _segmentInfosOffset; size_t _freeSpace; dyld_platform_t _platform; ImageInfo* const _firstImage; ImageInfo* _curImage; + dyld_aot_image_info_64* const _firstAotImage; + dyld_aot_image_info_64* _curAotImage; SegmentInfo* const _firstSegment; SegmentInfo* _curSegment; uint32_t _curSegmentIndex; @@ -282,14 +272,18 @@ private: // char stringPool[] }; -dyld_process_info_base::dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, size_t totalSize) +dyld_process_info_base::dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, unsigned aotImageCount, size_t totalSize) : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)), - _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)), - _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)), - _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)), + _aotCacheInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)), + _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info)), + _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info)), + _aotImageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)), + _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo) + aotImageCount*sizeof(dyld_aot_image_info_64)), _freeSpace(totalSize), _platform(platform), _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), + _firstAotImage((dyld_aot_image_info_64*)(((uint8_t*)this) + _aotImageInfosOffset)), + _curAotImage((dyld_aot_image_info_64*)(((uint8_t*)this) + _aotImageInfosOffset)), _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), _curSegmentIndex(0), @@ -315,13 +309,11 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma 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 - } } + usleep(1000 * 50); // 50ms + // Not exactly correct, but conveys that operation may succeed in the future + *kr = KERN_RESOURCE_SHORTAGE; + return nullptr; } // Test to see if there are no changes and we can exit early @@ -330,125 +322,138 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma return nullptr; } - for (uint32_t j=0; j < 10; ++j) { - 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; - } + uint64_t currentTimestamp = allImageInfo.infoArrayChangeTimestamp; + mach_vm_address_t infoArray = allImageInfo.infoArray; + if (infoArray == 0) { + usleep(1000 * 50); // 50ms + // Not exactly correct, but conveys that operation may succeed in the future + *kr = KERN_RESOURCE_SHORTAGE; + return nullptr; + }; - // 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); - // 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); - if (storage == nullptr) { - *kr = KERN_NO_SPACE; - result = nullptr; - return; - } - auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(allImageInfo.platform, imageCountWithDyld, allocationSize), deleter); - (void)info->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info)); - - // 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; - } - } - - 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; - - 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 + withRemoteBuffer(task, infoArray, imageArraySize, 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 (!info->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL)) { - result = nullptr; - return; - } + if ( !inCache(imageArray[i].imageFilePath) ) + ++countOfPathsNeedingCopying; } - // sanity check internal data did not overflow - if ( info->invalid() ) { + } + 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_aot_cache_info) + + sizeof(dyld_process_state_info) + + sizeof(ImageInfo)*(imageCountWithDyld) + + sizeof(dyld_aot_image_info_64)*(allImageInfo.aotInfoCount) // add the size necessary for aot info to this buffer + + sizeof(SegmentInfo)*imageCountWithDyld*10 + + countOfPathsNeedingCopying*PATH_MAX; + void* storage = malloc(allocationSize); + if (storage == nullptr) { + *kr = KERN_NO_SPACE; + result = nullptr; + return; + } + auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(allImageInfo.platform, imageCountWithDyld, allImageInfo.aotInfoCount, allocationSize), deleter); + (void)info->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info)+sizeof(dyld_process_aot_cache_info)); + (void)info->reserveSpace(sizeof(ImageInfo)*imageCountWithDyld); + + // 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 aot shared cache info + dyld_process_aot_cache_info* aotCacheInfo = info->aotCacheInfo(); + memcpy(aotCacheInfo->cacheUUID, &allImageInfo.aotSharedCacheUUID[0], 16); + aotCacheInfo->cacheBaseAddress = allImageInfo.aotSharedCacheBaseAddress; + + 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; + + 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))) { *kr = KERN_FAILURE; result = nullptr; return; } + } + // fill in info for each image + for (uint32_t i=0; i < imageCount; ++i) { + if (!info->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL)) { + *kr = KERN_FAILURE; + result = nullptr; + return; + } + } + // sanity check internal data did not overflow + if ( info->invalid() ) { + *kr = KERN_FAILURE; + result = nullptr; + return; + } - result = std::move(info); + result = std::move(info); + }); + + mach_vm_address_t aotImageArray = allImageInfo.aotInfoArray; + // shortcircuit this code path if aotImageArray == 0 (32 vs 64 bit struct difference) + // and if result == nullptr, since we need to append aot image infos to the process info struct + if (aotImageArray != 0 && result != nullptr) { + uint32_t aotImageCount = allImageInfo.aotInfoCount; + size_t aotImageArraySize = aotImageCount * sizeof(dyld_aot_image_info_64); + + withRemoteBuffer(task, aotImageArray, aotImageArraySize, false, kr, ^(void *buffer, size_t size) { + dyld_aot_image_info_64* imageArray = (dyld_aot_image_info_64*)buffer; + for (uint32_t i = 0; i < aotImageCount; i++) { + if (!result->addAotImage(imageArray[i])) { + *kr = KERN_FAILURE; + result = nullptr; + return; + } + } }); - - if (result) break; } - return std::move(result); } @@ -491,7 +496,7 @@ dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T 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, false, NULL, ^(mach_header_64 mhBuffer){ + withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){ if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) ) return; // now know the region is the start of a mach-o file @@ -517,20 +522,25 @@ dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer); //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer); + // explicitly set aot image count to 0 in the suspended case + unsigned aotImageCount = 0; + // allocate result object size_t allocationSize = sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info) + sizeof(ImageInfo)*(imageCount) - + sizeof(SegmentInfo)*imageCount*5 + + sizeof(dyld_aot_image_info_64)*aotImageCount // this should always be 0, but including it here to be explicit + + sizeof(SegmentInfo)*imageCount*10 + imageCount*PATH_MAX; void* storage = malloc(allocationSize); if (storage == nullptr) { *kr = KERN_NO_SPACE; return nullptr; } - auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base((dyld_platform_t)platformID, imageCount, allocationSize), deleter); - (void)obj->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info)); + auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base((dyld_platform_t)platformID, imageCount, aotImageCount, allocationSize), deleter); + (void)obj->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_aot_cache_info)+sizeof(dyld_process_state_info)); // fill in base info dyld_process_cache_info* cacheInfo = obj->cacheInfo(); bzero(cacheInfo->cacheUUID, 16); @@ -538,6 +548,11 @@ dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T cacheInfo->noCache = true; cacheInfo->privateCache = false; + // zero out aot cache info + dyld_process_aot_cache_info* aotCacheInfo = obj->aotCacheInfo(); + bzero(aotCacheInfo->cacheUUID, 16); + aotCacheInfo->cacheBaseAddress = 0; + dyld_process_state_info* stateInfo = obj->stateInfo(); stateInfo->timestamp = 0; stateInfo->imageCount = imageCount; @@ -590,7 +605,7 @@ const char* dyld_process_info_base::addString(const char* str, size_t maxlen) const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask) { __block const char* retval = ""; - withRemoteBuffer(task, stringAddressInTask, PATH_MAX, false, true, nullptr, ^(void *buffer, size_t size) { + withRemoteBuffer(task, stringAddressInTask, PATH_MAX, true, nullptr, ^(void *buffer, size_t size) { retval = addString(static_cast(buffer), size); }); return retval; @@ -598,7 +613,6 @@ const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddress bool dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal) { - if (!reserveSpace(sizeof(ImageInfo))) { return false; } _curImage->loadAddress = imageAddress; _curImage->segmentStartIndex = _curSegmentIndex; if ( imagePathLocal != NULL ) { @@ -622,6 +636,18 @@ bool dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, return true; } +bool dyld_process_info_base::addAotImage(dyld_aot_image_info_64 aotImageInfo) { + if (!reserveSpace(sizeof(dyld_aot_image_info_64))) { + return false; + } + _curAotImage->x86LoadAddress = aotImageInfo.x86LoadAddress; + _curAotImage->aotLoadAddress = aotImageInfo.aotLoadAddress; + _curAotImage->aotImageSize = aotImageInfo.aotImageSize; + memcpy(_curAotImage->aotImageKey, aotImageInfo.aotImageKey, sizeof(aotImageInfo.aotImageKey)); + + _curAotImage++; + return true; +} kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH) { __block kern_return_t kr = KERN_SUCCESS; @@ -629,7 +655,7 @@ kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, __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) { + withRemoteBuffer(task, remoteMH, PAGE_SIZE, 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) { @@ -643,7 +669,7 @@ kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, if (kr != KERN_SUCCESS) { return kr; } - withRemoteBuffer(task, remoteMH, headerPagesSize, false, false, &kr, ^(void * buffer, size_t size) { + withRemoteBuffer(task, remoteMH, headerPagesSize, false, &kr, ^(void * buffer, size_t size) { addInfoFromLoadCommands((mach_header*)buffer, remoteMH, size); }); } @@ -653,11 +679,6 @@ kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath) { - if (!reserveSpace(sizeof(ImageInfo))) { - // If we don't have ebnough spacee the data will be truncated, but well formed. Return success so - // symbolicators can try and use it - return KERN_SUCCESS; - } __block kern_return_t kr = KERN_SUCCESS; _curImage->loadAddress = dyldAddress; _curImage->segmentStartIndex = _curSegmentIndex; @@ -726,7 +747,13 @@ void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint const char* dyld_process_info_base::copySegmentName(const char* name) { // don't copy names of standard segments into string pool - static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL }; + static const char* stdSegNames[] = { + "__TEXT", "__DATA", "__LINKEDIT", + "__DATA_DIRTY", "__DATA_CONST", + "__OBJC", "__OBJC_CONST", + "__AUTH", "__AUTH_CONST", + NULL + }; for (const char** s=stdSegNames; *s != NULL; ++s) { if ( strcmp(name, *s) == 0 ) return *s; @@ -742,6 +769,18 @@ void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAd } } + +#if TARGET_OS_OSX +void dyld_process_info_base::forEachAotImage(bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) const +{ + for (const dyld_aot_image_info_64* p = _firstAotImage; p < _curAotImage; ++p) { + if (!callback(p->x86LoadAddress, p->aotLoadAddress, p->aotImageSize, (uint8_t*)p->aotImageKey, sizeof(p->aotImageKey))) { + break; + } + } +} +#endif + void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const { for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { @@ -780,26 +819,30 @@ dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, ker } //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) + if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) { + *kr = KERN_FAILURE; 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, (size_t)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(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(task, *info, timestamp, kr); - } - if (base) { - result = base.release(); - } - }); + for (auto i = 0; i < 10; ++i) { + withRemoteBuffer(task, task_dyld_info.all_image_info_addr, (size_t)task_dyld_info.all_image_info_size, 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(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(task, *info, timestamp, kr); + } + if (base) { + if (result) { + free((void*)result); + } + result = base.release(); + } + }); + if (kr == KERN_SUCCESS) { break; } + } return result; } @@ -813,6 +856,11 @@ void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_inf *cacheInfo = *info->cacheInfo(); } +void _dyld_process_info_get_aot_cache(dyld_process_info info, dyld_process_aot_cache_info* aotCacheInfo) +{ + *aotCacheInfo = *info->aotCacheInfo(); +} + void _dyld_process_info_retain(dyld_process_info object) { const_cast(object)->retain(); @@ -832,6 +880,12 @@ void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)( info->forEachImage(callback); } +#if TARGET_OS_OSX +void _dyld_process_info_for_each_aot_image(dyld_process_info info, bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) +{ + info->forEachAotImage(callback); +} +#endif void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) { diff --git a/src/dyld_process_info.cpp.orig b/src/dyld_process_info.cpp.orig deleted file mode 100644 index 03bd55c..0000000 --- a/src/dyld_process_info.cpp.orig +++ /dev/null @@ -1,652 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2016 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dyld_process_info.h" -#include "dyld_process_info_internal.h" -#include "dyld_images.h" -#include "dyld_priv.h" - - -// -// Opaque object returned by _dyld_process_info_create() -// - -struct __attribute__((visibility("hidden"))) dyld_process_info_base { - static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr); - static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr); - - uint32_t& retainCount() const { return _retainCount; } - dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); } - dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); } - void forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const; - void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const; - -private: - struct ImageInfo { - uuid_t uuid; - uint64_t loadAddress; - const char* path; - uint32_t segmentStartIndex; - uint32_t segmentsCount; - }; - - struct SegmentInfo { - const char* name; - uint64_t addr; - uint64_t size; - }; - - dyld_process_info_base(unsigned imageCount, size_t totalSize); - void* operator new (size_t, void* buf) { return buf; } - -#ifndef DARLING - static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); } -#else - static bool inCache(uint64_t addr) { return false; } -#endif - kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); - kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath); - - bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } - const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr); - const char* addString(const char*); - const char* copySegmentName(const char*); - - void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size); - - void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func); - kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func); - - mutable uint32_t _retainCount; - const uint32_t _cacheInfoOffset; - const uint32_t _stateInfoOffset; - const uint32_t _imageInfosOffset; - const uint32_t _segmentInfosOffset; - ImageInfo* const _firstImage; - ImageInfo* _curImage; - SegmentInfo* const _firstSegment; - SegmentInfo* _curSegment; - uint32_t _curSegmentIndex; - char* _stringRevBumpPtr; - - // dyld_process_cache_info cacheInfo; - // dyld_process_state_info stateInfo; - // ImageInfo images[]; - // SegmentInfo segments[]; - // char stringPool[] -}; - -dyld_process_info_base::dyld_process_info_base(unsigned imageCount, size_t totalSize) - : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)), - _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)), - _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)), - _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)), - _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), - _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), - _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), - _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), - _curSegmentIndex(0), - _stringRevBumpPtr((char*)(this)+totalSize) -{ -} - - -dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr) -{ - // figure out how many path strings will need to be copied and their size - const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos(); - bool sameCacheAsThisProcess = ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0) && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide)); - unsigned countOfPathsNeedingCopying = 0; - if ( sameCacheAsThisProcess ) { - for (int i=0; i < allImageInfo.infoArrayCount; ++i) { - if ( !inCache(imageArray[i].imageFilePath) ) - ++countOfPathsNeedingCopying; - } - } - else { - countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1; - } - unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1; - - // allocate result object - size_t allocationSize = sizeof(dyld_process_info_base) - + sizeof(dyld_process_cache_info) - + sizeof(dyld_process_state_info) - + sizeof(ImageInfo)*(imageCountWithDyld) - + sizeof(SegmentInfo)*imageCountWithDyld*5 - + countOfPathsNeedingCopying*PATH_MAX; - void* storage = malloc(allocationSize); - dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new() - - // fill in base info - dyld_process_cache_info* cacheInfo = obj->cacheInfo(); - memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16); - cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress; - cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion; - // if no cache is used, allImageInfo has all zeros for cache UUID - cacheInfo->noCache = true; - for (int i=0; i < 16; ++i) { - if ( cacheInfo->cacheUUID[i] != 0 ) { - cacheInfo->noCache = false; - } - } - - dyld_process_state_info* stateInfo = obj->stateInfo(); - stateInfo->timestamp = allImageInfo.infoArrayChangeTimestamp; - stateInfo->imageCount = imageCountWithDyld; - stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1); - if ( allImageInfo.infoArray != 0 ) - stateInfo->dyldState = dyld_process_state_dyld_initialized; - if ( allImageInfo.libSystemInitialized != 0 ) { - stateInfo->dyldState = dyld_process_state_libSystem_initialized; - if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount ) - stateInfo->dyldState = dyld_process_state_program_running; - } - if ( allImageInfo.errorMessage != 0 ) - stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated; - - // fill in info for dyld - if ( allImageInfo.dyldPath != 0 ) { - if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) { - if ( kr != NULL ) - *kr = r; - goto fail; - } - } - - // fill in info for each image - for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) { - if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) { - if ( kr != NULL ) - *kr = r; - goto fail; - } - } - - // sanity check internal data did not overflow - if ( obj->invalid() ) - goto fail; - - return obj; - -fail: - free(obj); - return NULL; -} - -dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr) -{ - pid_t pid; - kern_return_t result = pid_for_task(task, &pid); - if ( result != KERN_SUCCESS ) { - if ( kr != NULL ) - *kr = result; - return NULL; - } - - unsigned imageCount = 0; // main executable and dyld - uint64_t mainExecutableAddress = 0; - uint64_t dyldAddress = 0; - char dyldPathBuffer[PATH_MAX+1]; - char mainExecutablePathBuffer[PATH_MAX+1]; - mach_vm_size_t size; - for (mach_vm_address_t address = 0; ; address += size) { - vm_region_basic_info_data_64_t info; - mach_port_t objectName; - unsigned int infoCount = VM_REGION_BASIC_INFO_COUNT_64; - result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO, - (vm_region_info_t)&info, &infoCount, &objectName); - if ( result != KERN_SUCCESS ) - break; - if ( info.protection == (VM_PROT_READ|VM_PROT_EXECUTE) ) { - if ( mainExecutableAddress == 0 ) { - mainExecutableAddress = address; - int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePathBuffer, PATH_MAX); - if ( len != 0 ) - mainExecutablePathBuffer[len] = '\0'; - ++imageCount; - } - else if ( dyldAddress == 0 ) { - dyldAddress = address; - int len = proc_regionfilename(pid, dyldAddress, dyldPathBuffer, PATH_MAX); - if ( len != 0 ) - dyldPathBuffer[len] = '\0'; - ++imageCount; - } - //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection); - } - } - //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer); - //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer); - - // allocate result object - size_t allocationSize = sizeof(dyld_process_info_base) - + sizeof(dyld_process_cache_info) - + sizeof(dyld_process_state_info) - + sizeof(ImageInfo)*(imageCount) - + sizeof(SegmentInfo)*imageCount*5 - + imageCount*PATH_MAX; - void* storage = malloc(allocationSize); - dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new() - - // fill in base info - dyld_process_cache_info* cacheInfo = obj->cacheInfo(); - bzero(cacheInfo->cacheUUID, 16); - cacheInfo->cacheBaseAddress = 0; - cacheInfo->noCache = true; - cacheInfo->privateCache = false; - - dyld_process_state_info* stateInfo = obj->stateInfo(); - stateInfo->timestamp = 0; - stateInfo->imageCount = imageCount; - stateInfo->initialImageCount = imageCount; - stateInfo->dyldState = dyld_process_state_not_started; - - // fill in info for dyld - if ( dyldAddress != 0 ) { - if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPathBuffer) ) { - if ( kr != NULL ) - *kr = r; - free(obj); - return NULL; - } - } - - // fill in info for each image - if ( mainExecutableAddress != 0 ) { - if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePathBuffer) ) { - if ( kr != NULL ) - *kr = r; - free(obj); - return NULL; - } - } - - return obj; -} - - - -const char* dyld_process_info_base::addString(const char* str) -{ - size_t len = strlen(str) + 1; - _stringRevBumpPtr -= len; - strcpy(_stringRevBumpPtr, str); - return _stringRevBumpPtr; -} - -const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr) -{ - char temp[PATH_MAX+8]; // +8 is to allow '\0' at temp[PATH_MAX] - mach_vm_size_t readSize = PATH_MAX; - if ( ((stringAddressInTask & 0xFFF) + PATH_MAX) < 4096 ) { - // string fits within page, only one vm_read needed - if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, PATH_MAX, (vm_address_t)&temp, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - } - else { - // string may cross page boundary, split into two reads - size_t firstLen = 4096 - (stringAddressInTask & 0xFFF); - readSize = firstLen; - if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask, firstLen, (vm_address_t)&temp, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - temp[firstLen] = '\0'; - if ( strlen(temp) >= firstLen ) { - readSize = PATH_MAX-firstLen; - if ( kern_return_t r = mach_vm_read_overwrite(task, stringAddressInTask+firstLen, PATH_MAX-firstLen, (vm_address_t)&temp+firstLen, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - temp[PATH_MAX] = '\0'; // truncate any string that is too long - } - } - } - if ( kr != NULL ) - *kr = KERN_SUCCESS; - return addString(temp); -} - - -kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal) -{ - _curImage->loadAddress = imageAddress; - _curImage->segmentStartIndex = _curSegmentIndex; - if ( imagePathLocal != NULL ) { - _curImage->path = addString(imagePathLocal); - } - else if ( sameCacheAsThisProcess && inCache(imagePath) ) { - _curImage->path = (const char*)imagePath; - } - else { - kern_return_t kr; - _curImage->path = copyPath(task, imagePath, &kr); - if ( kr ) - return kr; - } - if ( sameCacheAsThisProcess && inCache(imageAddress) ) { - addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024); - } - else { - mach_vm_size_t readSize = sizeof(mach_header_64); - mach_header_64 mhBuffer; - if ( kern_return_t r = mach_vm_read_overwrite(task, imageAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) { - return r; - } - size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096); - vm_address_t localCopyBuffer; - unsigned int localCopyBufferSize; - if ( kern_return_t r = mach_vm_read(task, imageAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) { - return r; - } - addInfoFromLoadCommands((mach_header*)localCopyBuffer, imageAddress, localCopyBufferSize); - vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize); - } - _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; - _curImage++; - return KERN_SUCCESS; -} - - -kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath) -{ - kern_return_t kr; - _curImage->loadAddress = dyldAddress; - _curImage->segmentStartIndex = _curSegmentIndex; - if ( localPath != NULL ) { - _curImage->path = addString(localPath); - } - else { - _curImage->path = copyPath(task, dyldPathAddress, &kr); - if ( kr ) - return kr; - } - - mach_vm_size_t readSize = sizeof(mach_header_64); - mach_header_64 mhBuffer; - if ( kern_return_t r = mach_vm_read_overwrite(task, dyldAddress, sizeof(mach_header_64), (vm_address_t)&mhBuffer, &readSize) ) { - return r; - } - size_t headerPagesSize = (sizeof(mach_header_64) + mhBuffer.sizeofcmds + 4095) & (-4096); - vm_address_t localCopyBuffer; - unsigned int localCopyBufferSize; - if ( kern_return_t r = mach_vm_read(task, dyldAddress, headerPagesSize, &localCopyBuffer, &localCopyBufferSize) ) { - return r; - } - addInfoFromLoadCommands((mach_header*)localCopyBuffer, dyldAddress, localCopyBufferSize); - vm_deallocate(mach_task_self(), localCopyBuffer, localCopyBufferSize); - _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; - _curImage++; - return KERN_SUCCESS; -} - - -void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size) -{ - const load_command* startCmds = NULL; - if ( mh->magic == MH_MAGIC_64 ) - startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); - else if ( mh->magic == MH_MAGIC ) - startCmds = (load_command*)((char *)mh + sizeof(mach_header)); - else - return; // not a mach-o file, or wrong endianness - - const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); - const load_command* cmd = startCmds; - for(uint32_t i = 0; i < mh->ncmds; ++i) { - const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); - if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { - return; // malformed load command - } - if ( cmd->cmd == LC_UUID ) { - const uuid_command* uuidCmd = (uuid_command*)cmd; - memcpy(_curImage->uuid, uuidCmd->uuid, 16); - } - else if ( cmd->cmd == LC_SEGMENT ) { - const segment_command* segCmd = (segment_command*)cmd; - _curSegment->name = copySegmentName(segCmd->segname); - _curSegment->addr = segCmd->vmaddr; - _curSegment->size = segCmd->vmsize; - _curSegment++; - _curSegmentIndex++; - } - else if ( cmd->cmd == LC_SEGMENT_64 ) { - const segment_command_64* segCmd = (segment_command_64*)cmd; - _curSegment->name = copySegmentName(segCmd->segname); - _curSegment->addr = segCmd->vmaddr; - _curSegment->size = segCmd->vmsize; - _curSegment++; - _curSegmentIndex++; - } - cmd = nextCmd; - } -} - -const char* dyld_process_info_base::copySegmentName(const char* name) -{ - // don't copy names of standard segments into string pool - static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL }; - for (const char** s=stdSegNames; *s != NULL; ++s) { - if ( strcmp(name, *s) == 0 ) - return *s; - } - // copy custom segment names into string pool - return addString(name); -} - -void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const -{ - for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { - callback(p->loadAddress, p->uuid, p->path); - } -} - -void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const -{ - for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { - if ( p->loadAddress == machHeaderAddress ) { - uint64_t slide = 0; - for (int i=0; i < p->segmentsCount; ++i) { - const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; - if ( strcmp(seg->name, "__TEXT") == 0 ) { - slide = machHeaderAddress - seg->addr; - break; - } - } - for (int i=0; i < p->segmentsCount; ++i) { - const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; - callback(seg->addr + slide, seg->size, seg->name); - } - break; - } - } -} - - - - - -// Implementation that works with existing dyld data structures -dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr) -{ - if ( kr != NULL ) - *kr = KERN_SUCCESS; - - task_dyld_info_data_t task_dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - - //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded - if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) - return NULL; - - if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) ) - return NULL; - - // read all_image_infos struct - dyld_all_image_infos_64 allImageInfo64; - mach_vm_size_t readSize = task_dyld_info.all_image_info_size; - if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - if ( allImageInfo64.infoArrayCount == 0 ) { - // could be task was launch suspended or still launching, wait a moment to see - usleep(1000 * 50); // 50ms - if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - // if infoArrayCount is still zero, then target was most likely launched suspended - if ( allImageInfo64.infoArrayCount == 0 ) - return dyld_process_info_base::makeSuspended(task, kr); - } - - // bail out of dyld is too old - if ( allImageInfo64.version < 15 ) { - if ( kr != NULL ) - *kr = KERN_INVALID_HOST; - return NULL; - } - - // normalize by expanding 32-bit all_image_infos into 64-bit one - uint32_t imageCount = allImageInfo64.infoArrayCount; - size_t imageArraySize = imageCount * sizeof(dyld_image_info_64); - if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { - const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64; - dyld_all_image_infos_64 info64; - bzero(&info64, sizeof(info64)); - info64.version = allImageInfo32->version; - info64.infoArrayCount = allImageInfo32->infoArrayCount; - info64.infoArray = allImageInfo32->infoArray; - info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion; - info64.libSystemInitialized = allImageInfo32->libSystemInitialized; - info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress; - info64.initialImageCount = allImageInfo32->initialImageCount; - info64.uuidArrayCount = allImageInfo32->uuidArrayCount; - info64.uuidArray = allImageInfo32->uuidArray; - info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress; - info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide; - info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp; - info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress; - info64.dyldPath = allImageInfo32->dyldPath; - memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16); - allImageInfo64 = info64; - imageCount = allImageInfo64.infoArrayCount; - imageArraySize = imageCount * sizeof(dyld_image_info_32); - } - - // don't do any (more) work if target process's dyld timestamp has not changed since previous query - if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) { - if ( kr != NULL ) - *kr = KERN_SUCCESS; - return NULL; - } - - // For the moment we are going to truncate any image list longer than 8192 because some programs do - // terrible things that corrupt their own image lists and we need to stop clients from crashing - // reading them. We can try to do something more advanced in the future. rdar://27446361 - imageCount = MIN(imageCount, 8192); - - // read image array - dyld_image_info_64 imageArray64[imageCount]; - if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) { - if ( kr != NULL ) - *kr = r; - return NULL; - } - // normalize by expanding 32-bit image_infos into 64-bit ones - if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { - const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64; - dyld_image_info_64 tempArray[imageCount]; - for (uint32_t i=0; i < imageCount; ++i) { - tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress; - tempArray[i].imageFilePath = imageArray32[i].imageFilePath; - tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate; - } - memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount); - } - - // create object based on local copy of all image infos and image array - return dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr); -} - - -void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo) -{ - *stateInfo = *info->stateInfo(); -} - -void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo) -{ - *cacheInfo = *info->cacheInfo(); -} - -void _dyld_process_info_retain(dyld_process_info info) -{ - info->retainCount() += 1; -} - -void _dyld_process_info_release(dyld_process_info info) -{ - info->retainCount() -= 1; - if ( info->retainCount() == 0 ) - free((void*)info); -} - -void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) -{ - info->forEachImage(callback); -} - - -void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) -{ - info->forEachSegment(machHeaderAddress, callback); -} - - - diff --git a/src/dyld_process_info.cpp.rej b/src/dyld_process_info.cpp.rej deleted file mode 100644 index b2c707f..0000000 --- a/src/dyld_process_info.cpp.rej +++ /dev/null @@ -1,40 +0,0 @@ ---- src/dyld_process_info.cpp -+++ src/dyld_process_info.cpp -@@ -238,28 +408,32 @@ private: - uint64_t size; - }; - -- dyld_process_info_base(unsigned imageCount, size_t totalSize); -+ dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, size_t totalSize); - void* operator new (size_t, void* buf) { return buf; } - - static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); } -- kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); -+ bool addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); -+ - kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath); - - bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } -- const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr); -- const char* addString(const char*); -+ const char* copyPath(task_t task, uint64_t pathAddr); -+ const char* addString(const char*, size_t); - 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 _retainCount; - const uint32_t _cacheInfoOffset; - const uint32_t _stateInfoOffset; - const uint32_t _imageInfosOffset; - const uint32_t _segmentInfosOffset; -+ size_t _freeSpace; -+ dyld_platform_t _platform; - ImageInfo* const _firstImage; - ImageInfo* _curImage; - SegmentInfo* const _firstSegment; diff --git a/src/dyld_process_info_internal.h b/src/dyld_process_info_internal.h index df3d963..43ce333 100644 --- a/src/dyld_process_info_internal.h +++ b/src/dyld_process_info_internal.h @@ -67,6 +67,12 @@ struct dyld_all_image_infos_32 { uint32_t compact_dyld_image_info_addr; uint32_t compact_dyld_image_info_size; uint32_t platform; + // the aot fields below will not be set in the 32 bit case + uint32_t aotInfoCount; + std::atomic aotInfoArray; + uint64_t aotInfoArrayChangeTimestamp; + uint64_t aotSharedCacheBaseAddress; + std::array aotSharedCacheUUID[16]; }; struct dyld_all_image_infos_64 { @@ -102,6 +108,11 @@ struct dyld_all_image_infos_64 { uint64_t compact_dyld_image_info_addr; uint64_t compact_dyld_image_info_size; uint32_t platform; + uint32_t aotInfoCount; + std::atomic aotInfoArray; + uint64_t aotInfoArrayChangeTimestamp; + uint64_t aotSharedCacheBaseAddress; + std::array aotSharedCacheUUID[16]; }; struct dyld_image_info_32 { @@ -115,6 +126,14 @@ struct dyld_image_info_64 { uint64_t imageFileModDate; }; +#define DYLD_AOT_IMAGE_KEY_SIZE 32 +struct dyld_aot_image_info_64 { + uint64_t x86LoadAddress; + uint64_t aotLoadAddress; + uint64_t aotImageSize; + uint8_t aotImageKey[DYLD_AOT_IMAGE_KEY_SIZE]; +}; + #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 @@ -140,26 +159,23 @@ struct dyld_process_info_notify_header { //FIXME: Refactor this out into a seperate file 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(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool allow_truncation); RemoteBuffer& operator=(RemoteBuffer&& other); ~RemoteBuffer(); void *getLocalAddress() const; kern_return_t getKernelReturn() const; size_t getSize() const; private: - static std::pair map( task_t task, mach_vm_address_t remote_address, - vm_size_t _size, bool shared); - static std::tuplecreate( task_t task, + static std::pair map( task_t task, mach_vm_address_t remote_address, vm_size_t _size); + static std::tuplecreate( task_t task, mach_vm_address_t remote_address, size_t remote_size, - bool shared, bool allow_truncation); - RemoteBuffer(std::tuple T); + RemoteBuffer(std::tuple T); mach_vm_address_t _localAddress; vm_size_t _size; kern_return_t _kr; - bool _shared; }; // only called during libdyld set up @@ -168,12 +184,12 @@ 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"))); +void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) __attribute__((visibility("hidden"))); template -VIS_HIDDEN void withRemoteObject(task_t task, mach_vm_address_t remote_address, bool shared, kern_return_t *kr, void (^block)(T t)) +VIS_HIDDEN void withRemoteObject(task_t task, mach_vm_address_t remote_address, kern_return_t *kr, void (^block)(T t)) { - withRemoteBuffer(task, remote_address, sizeof(T), shared, false, kr, ^(void *buffer, size_t size) { + withRemoteBuffer(task, remote_address, sizeof(T), false, kr, ^(void *buffer, size_t size) { block(*reinterpret_cast(buffer)); }); } diff --git a/src/dyld_process_info_notify.cpp b/src/dyld_process_info_notify.cpp index de739e6..c5317d7 100644 --- a/src/dyld_process_info_notify.cpp +++ b/src/dyld_process_info_notify.cpp @@ -33,11 +33,13 @@ #include #include #include - +#include +#include #include "dyld_process_info_internal.h" #include "Loading.h" +#include "Tracing.h" #include "AllImages.h" extern "C" int _dyld_func_lookup(const char* name, void** address); @@ -57,7 +59,11 @@ struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base void retain(); void release(); - void setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; } + void setNotifyMain(NotifyMain notifyMain) const { + if (_notifyMain == notifyMain) { return; } + Block_release(_notifyMain); + _notifyMain = Block_copy(notifyMain); + } // override new and delete so we don't need to link with libc++ static void* operator new(size_t sz) { return malloc(sz); } @@ -65,189 +71,217 @@ struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base private: void handleEvent(); - void teardown(); + void disconnect(); + void teardownMachPorts(); void replyToMonitoredProcess(mach_msg_header_t& header); + kern_return_t task_dyld_process_info_notify_register(task_read_t target_task, mach_port_t notify); + kern_return_t task_dyld_process_info_notify_deregister(task_read_t target_task, mach_port_t notify); + RemoteBuffer _remoteAllImageInfoBuffer; - uint32_t* _notifyMachPorts; - uint32_t _notifySlot; - mutable std::atomic _retainCount; + mutable std::atomic _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 _disabled; + mutable Notify _notify; + mutable NotifyExit _notifyExit; + mutable NotifyMain _notifyMain; + dispatch_source_t _machSource; + task_t _task; + mach_port_t _port; // monitor is process being notified of image loading/unloading + std::atomic _connected; +#if TARGET_OS_SIMULATOR + uint32_t _portInTarget; +#endif }; +#if TARGET_OS_SIMULATOR + +template +kern_return_t withRemotePortArray(task_t target_task, F f) { + // Get the all image info + task_dyld_info_data_t taskDyldInfo; + mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT; + auto kr = task_info(target_task, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount); + if (kr != KERN_SUCCESS) { + return kr; + } + + vm_prot_t cur_protection = VM_PROT_NONE; + vm_prot_t max_protection = VM_PROT_NONE; + mach_vm_address_t localAddress = 0; + mach_vm_size_t size = sizeof(dyld_all_image_infos_64); + if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + size = sizeof(dyld_all_image_infos_32); + } + kr = mach_vm_remap(mach_task_self(), + &localAddress, + size, + 0, // mask + VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR| VM_FLAGS_RESILIENT_CODESIGN | VM_FLAGS_RESILIENT_MEDIA, + target_task, + taskDyldInfo.all_image_info_addr, + false, + &cur_protection, + &max_protection, + VM_INHERIT_NONE); + + static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits"); + uint32_t* notifyMachPorts; + if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + notifyMachPorts = (uint32_t *)((uint8_t *)localAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts)); + } else { + notifyMachPorts = (uint32_t *)((uint8_t *)localAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts)); + } + kr = f(notifyMachPorts); + (void)vm_deallocate(target_task, localAddress, size); + return kr; +} + +#endif + +kern_return_t dyld_process_info_notify_base::task_dyld_process_info_notify_register(task_t target_task, mach_port_t notify) { +#if TARGET_OS_SIMULATOR + static dispatch_once_t onceToken; + static kern_return_t (*tdpinr)(task_t, mach_port_t) = nullptr; + dispatch_once(&onceToken, ^{ + tdpinr = (kern_return_t (*)(task_t, mach_port_t))dlsym(RTLD_DEFAULT, "task_dyld_process_info_notify_register"); + }); + if (tdpinr) { + return tdpinr(target_task, notify); + } + // Our libsystem does not have task_dyld_process_info_notify_register, emulate + return withRemotePortArray(target_task, [this,target_task,notify](uint32_t* portArray){ + mach_port_t portInTarget = MACH_PORT_NULL; + // Insert the right + kern_return_t kr = KERN_NAME_EXISTS; + while (kr == KERN_NAME_EXISTS) { + portInTarget = MACH_PORT_NULL; + kr = mach_port_allocate(target_task, MACH_PORT_RIGHT_DEAD_NAME, &portInTarget); + if (kr != KERN_SUCCESS) { + return kr; + } + (void)mach_port_deallocate(target_task, portInTarget); + kr = mach_port_insert_right(target_task, portInTarget, notify, MACH_MSG_TYPE_MAKE_SEND); + } + // The call is not succesfull return + if (kr != KERN_SUCCESS) { + (void)mach_port_deallocate(target_task, portInTarget); + return kr; + } + // Find a slot for the right + for (uint8_t notifySlot=0; notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++notifySlot) { + if (OSAtomicCompareAndSwap32(0, portInTarget, (volatile int32_t*)&portArray[notifySlot])) { + _portInTarget = portInTarget; + return KERN_SUCCESS; + } + } + // The array was full, we need to fail + (void)mach_port_deallocate(target_task, portInTarget); + return KERN_UREFS_OVERFLOW; + }); +#else + return ::task_dyld_process_info_notify_register(target_task, notify); +#endif +} + +kern_return_t dyld_process_info_notify_base::task_dyld_process_info_notify_deregister(task_t target_task, mach_port_t notify) { +#if TARGET_OS_SIMULATOR + static dispatch_once_t onceToken; + static kern_return_t (*tdpind)(task_t, mach_port_t) = nullptr; + dispatch_once(&onceToken, ^{ + tdpind = (kern_return_t (*)(task_t, mach_port_t))dlsym(RTLD_DEFAULT, "task_dyld_process_info_notify_deregister"); + }); + if (tdpind) { + return tdpind(target_task, notify); + } + // Our libsystem does not have task_dyld_process_info_notify_deregister, emulate + return withRemotePortArray(target_task, [this](uint32_t* portArray){ + // Find a slot for the right + for (uint8_t notifySlot=0; notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++notifySlot) { + if (OSAtomicCompareAndSwap32(0, _portInTarget, (volatile int32_t*)&portArray[notifySlot])) { + return KERN_SUCCESS; + } + } + return KERN_FAILURE; + }); +#else + // Our libsystem does not have task_dyld_process_info_notify_deregister, emulate + return ::task_dyld_process_info_notify_deregister(target_task, notify); +#endif +} 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) + _retainCount(0), _queue(queue), _notify(Block_copy(notify)), _notifyExit(Block_copy(notifyExit)), + _notifyMain(nullptr), _machSource(nullptr), _task(task), _port(MACH_PORT_NULL), _connected(false) +#if TARGET_OS_SIMULATOR + , _portInTarget(0) +#endif { - assert(_disabled == false); + assert(kr != NULL); dispatch_retain(_queue); // 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(); + mach_port_options_t options = { .flags = MPO_IMPORTANCE_RECEIVER | MPO_CONTEXT_AS_GUARD | MPO_STRICT, .mpl = { MACH_PORT_QLIMIT_DEFAULT }}; + *kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_port); + if (*kr != KERN_SUCCESS) { + teardownMachPorts(); 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; - } + mach_port_t previous = MACH_PORT_NULL; + *kr = mach_port_request_notification(mach_task_self(), _port, MACH_NOTIFY_NO_SENDERS, 1, _port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + if ((*kr != KERN_SUCCESS) || previous != MACH_PORT_NULL) { + teardownMachPorts(); + return; + } + //FIXME: Should we retry here if we fail? + *kr = task_dyld_process_info_notify_register(_task, _port); + dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TASK_NOTIFY_REGISTER, (uint64_t)_task, (uint64_t)_port, *kr, 0); + if (*kr != KERN_SUCCESS) { + teardownMachPorts(); + return; } // Setup the event handler for the port - _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue); + _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _port, 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(); + teardownMachPorts(); 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_source_set_event_handler(_machSource, ^{ handleEvent(); }); + dispatch_source_set_cancel_handler(_machSource, ^{ teardownMachPorts(); }); dispatch_activate(_machSource); - - // 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, (size_t)taskDyldInfo.all_image_info_size, true, false); - *kr = _remoteAllImageInfoBuffer.getKernelReturn(); - if (*kr) { - (void)mach_port_deallocate(_targetTask, _sendPortInTarget); - teardown(); - return; - } - - 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 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 (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) { - (void)mach_port_deallocate(_targetTask, _sendPortInTarget); - teardown(); - *kr = KERN_UREFS_OVERFLOW; - return; - } - - *kr = KERN_SUCCESS; + _connected = true; } 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"); - } + if (_connected) { fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still connected\n"); } + Block_release(_notify); + Block_release(_notifyMain); + Block_release(_notifyExit); dispatch_release(_queue); } -void dyld_process_info_notify_base::teardown() { - if (!_disabled) { - _disabled = true; +void dyld_process_info_notify_base::teardownMachPorts() { + if ( _port != 0 ) { + kern_return_t kr = task_dyld_process_info_notify_deregister(_task, _port); + dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TASK_NOTIFY_DEREGISTER, (uint64_t)_task, (uint64_t)_port, kr, 0); + (void)mach_port_destruct(mach_task_self(), _port, 0, (mach_port_context_t)this); + _port = 0; + } +} + +void dyld_process_info_notify_base::disconnect() { + if (_connected) { + _connected = false; // 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); - } - } - _sendPortInTarget = 0; if ( _machSource ) { dispatch_source_cancel(_machSource); dispatch_release(_machSource); _machSource = NULL; - } + } if (_notifyExit) { dispatch_async(_queue, ^{ + // There was a not a mach source, so if we have any ports they will not get torn down by its cancel handler _notifyExit(); }); } @@ -256,24 +290,23 @@ void dyld_process_info_notify_base::teardown() { bool dyld_process_info_notify_base::enabled() const { - return !_disabled; + return _connected; } void dyld_process_info_notify_base::retain() { - _retainCount++; + _retainCount.fetch_add(1, std::memory_order_relaxed); } void dyld_process_info_notify_base::release() { - uint32_t newCount = --_retainCount; - - if ( newCount == 0 ) { - teardown(); + if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 0) { + // When we subtracted the ref count was 0, which means it was the last reference + disconnect(); + dispatch_async(_queue, ^{ + delete this; + }); } - dispatch_async(_queue, ^{ - delete this; - }); } void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& header) { @@ -288,22 +321,21 @@ void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& h if (r == KERN_SUCCESS) { header.msgh_remote_port = MACH_PORT_NULL; } else { - teardown(); + disconnect(); } } 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; - } + if (!_connected) { 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); + 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), _port, 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); @@ -322,39 +354,39 @@ void dyld_process_info_notify_base::handleEvent() { //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(); + disconnect(); break; } } // reply to dyld, so it can continue replyToMonitoredProcess(*h); } else { - teardown(); + disconnect(); } } else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) { if (h->msgh_size != sizeof(mach_msg_header_t)) { - teardown(); + disconnect(); } 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; + } else if ( h->msgh_id == MACH_NOTIFY_NO_SENDERS ) { // 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(); + && audit_tlr->msgh_audit.val[5] == 0) { + disconnect(); } } else { fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); } + } else { + fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); } mach_msg_destroy(h); } diff --git a/src/dyld_stub_binder.S b/src/dyld_stub_binder.s similarity index 100% rename from src/dyld_stub_binder.S rename to src/dyld_stub_binder.s diff --git a/src/dyld_usage.cpp b/src/dyld_usage.cpp index 2ee9385..a2e2a4f 100644 --- a/src/dyld_usage.cpp +++ b/src/dyld_usage.cpp @@ -73,7 +73,7 @@ exit_usage(void) 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"); + fprintf(stderr, "dyld_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n"); exit(1); } @@ -346,11 +346,11 @@ public: if (auto dlopenNode = dynamic_cast(node.get())) { sstr << std::hex; sstr << "{\"type\":\"dlopen\",\"path\":\"" << dlopenNode->path << "\",\"flags\":\"0x" << dlopenNode->flags << "\""; - sstr << ",\"result\":\"" << dlopenNode->result << "\""; + sstr << ",\"result\":\"0x" << dlopenNode->result << "\""; } else if (auto dlopenPreflightNode = dynamic_cast(node.get())) { sstr << std::hex; sstr << "{\"type\":\"dlopen_preflight\",\"path\":\"" << dlopenPreflightNode->path << "\""; - sstr << ",\"result\":\"" << dlopenPreflightNode->result << "\""; + sstr << ",\"result\":\"0x" << dlopenPreflightNode->result << "\""; } else if (auto dlsymNode = dynamic_cast(node.get())) { sstr << std::hex << "{\"type\":\"dlsym\",\"symbol\":\"" << dlsymNode->symbol << "\",\"handle\":\"0x"; sstr << dlsymNode->handle << "\",\"result\":\"0x" << dlsymNode->result << "\""; @@ -729,7 +729,7 @@ main(int argc, char *argv[]) s = ktrace_session_create(); assert(s); - while ((ch = getopt(argc, argv, "jJeR:t:")) != -1) { + while ((ch = getopt(argc, argv, "hjJeR:t:")) != -1) { switch (ch) { case 'j': JSON_flag = true; @@ -756,6 +756,7 @@ main(int argc, char *argv[]) exit(1); } break; + case 'h': default: exit_usage(); } @@ -778,7 +779,7 @@ main(int argc, char *argv[]) if (!RAW_flag) { if (geteuid() != 0) { - fprintf(stderr, "'dyld_usage' must be run as root...\n"); + fprintf(stderr, "'dyld_usage' must be run as root\n"); exit(1); } @@ -879,6 +880,7 @@ main(int argc, char *argv[]) exit(1); } + setvbuf(stdout, (char *)NULL, _IONBF, 0); dispatch_main(); return 0; diff --git a/src/glue.c b/src/glue.c index 92875de..9bebbe8 100644 --- a/src/glue.c +++ b/src/glue.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -400,29 +401,6 @@ void* memset(void* b, int c, size_t len) } -// wrap calls to stat() with check for EAGAIN -int _ZN4dyld7my_statEPKcP4stat(const char* path, struct stat* buf) -{ - int result; - do { - result = stat(path, buf); - } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR))); - - return result; -} - -// dyld should retry open() if it gets an EGAIN -int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) -{ - int result; - do { - result = open(path, flag, other); - } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR))); - - return result; -} - - // // The dyld in the iOS simulator cannot do syscalls, so it calls back to // host dyld. @@ -430,9 +408,15 @@ int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) #if TARGET_OS_SIMULATOR -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); +int open(const char* path, int oflag, ...) { + int retval; + + va_list args; + va_start(args, oflag); + retval = gSyscallHelpers->open(path, oflag, va_arg(args, int)); + va_end(args); + + return retval; } int close(int fd) { @@ -463,14 +447,26 @@ int stat(const char* path, struct stat* buf) { return gSyscallHelpers->stat(path, buf); } -int myfcntl(int fd, int cmd, void* result) __asm("_fcntl"); -int myfcntl(int fd, int cmd, void* result) { - return gSyscallHelpers->fcntl(fd, cmd, result); +int fcntl(int fd, int cmd, ...) { + int retval; + + va_list args; + va_start(args, cmd); + retval = gSyscallHelpers->fcntl(fd, cmd, va_arg(args, void *)); + va_end(args); + + return retval; } -int myioctl(int fd, unsigned long request, void* result) __asm("_ioctl"); -int myioctl(int fd, unsigned long request, void* result) { - return gSyscallHelpers->ioctl(fd, request, result); +int ioctl(int fd, unsigned long request, ...) { + int retval; + + va_list args; + va_start(args, request); + retval = gSyscallHelpers->ioctl(fd, request, va_arg(args, void *)); + va_end(args); + + return retval; } int issetugid() { @@ -552,11 +548,13 @@ kern_return_t mach_timebase_info(mach_timebase_info_t info) { return gSyscallHelpers->mach_timebase_info(info); } -bool OSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) { +bool myOSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) __asm("_OSAtomicCompareAndSwapPtrBarrier"); +bool myOSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) { return gSyscallHelpers->OSAtomicCompareAndSwapPtrBarrier(old, new, value); } -void OSMemoryBarrier() { +void myOSMemoryBarrier(void) __asm("_OSMemoryBarrier"); +void myOSMemoryBarrier() { return gSyscallHelpers->OSMemoryBarrier(); } @@ -894,6 +892,21 @@ kern_return_t mach_port_destruct(ipc_space_t task, mach_port_name_t name, mach_p return KERN_NOT_SUPPORTED; } +kern_return_t task_dyld_process_info_notify_get( mach_port_name_array_t names_addr, mach_msg_type_number_t *names_count_addr) { + if ( gSyscallHelpers->version >= 14 ) { + return gSyscallHelpers->task_dyld_process_info_notify_get(names_addr, names_count_addr); + } + 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 ) { + // Bump the refs + (void)mach_port_mod_refs(mach_task_self(), imageInfo->notifyPorts[slot], MACH_PORT_RIGHT_SEND, 1); + } + } + + 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) { if ( gSyscallHelpers->version >= 6 ) @@ -931,7 +944,7 @@ uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str) return 0; } -uint64_t amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags) +int amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags) { if ( gSyscallHelpers->version >= 10 ) return gSyscallHelpers->amfi_check_dyld_policy_self(inFlags, outFlags); @@ -1076,12 +1089,6 @@ void _ZN4dyld20notifyMonitoringDyldEbjPPK11mach_headerPPKc(bool unloading, unsig gSyscallHelpers->notifyMonitoringDyld(unloading, imageCount, loadAddresses, imagePaths); return; } -#if SUPPORT_HOST_10_11 - findHostFunctions(); - for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { - notifyMonitoringDyld(unloading, slot, imageCount, loadAddresses, imagePaths); - } -#endif } int* __error(void) { @@ -1108,6 +1115,8 @@ vm_size_t vm_page_size = 0x1000; #if ! TARGET_OS_SIMULATOR #include + // dyld should mark _dyld_debugger_notification `noinline` + __attribute__ ((noinline)) void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]) { // Do nothing. This exists for the debugger to set a break point on to see what images have been loaded or unloaded. @@ -1161,3 +1170,6 @@ void uuid_unparse_upper(const uuid_t uu, uuid_string_t out) uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); } + + + diff --git a/src/start_glue.S b/src/start_glue.s similarity index 100% rename from src/start_glue.S rename to src/start_glue.s diff --git a/src/stub_binding_helper.S b/src/stub_binding_helper.s similarity index 100% rename from src/stub_binding_helper.S rename to src/stub_binding_helper.s diff --git a/src/threadLocalHelpers.S b/src/threadLocalHelpers.s similarity index 98% rename from src/threadLocalHelpers.S rename to src/threadLocalHelpers.s index 1e9aa80..dfcf614 100644 --- a/src/threadLocalHelpers.S +++ b/src/threadLocalHelpers.s @@ -250,6 +250,9 @@ _tlv_get_addr: ret lr LlazyAllocate: +#if __has_feature(ptrauth_returns) + pacibsp +#endif stp fp, lr, [sp, #-16]! mov fp, sp sub sp, sp, #288 @@ -292,7 +295,11 @@ LlazyAllocate: mov sp, fp ldp fp, lr, [sp], #16 - ret lr +#if __has_feature(ptrauth_returns) + retab +#else + ret +#endif #endif diff --git a/testing/README.txt b/testing/README.txt index bb821c9..351bcc5 100755 --- a/testing/README.txt +++ b/testing/README.txt @@ -10,38 +10,45 @@ Example, main.c may contain: // RUN: ./example.exe int main() { return 0; } +It is possible to restrict build and run lines to specific platforms using the BUILD(): syntax, for example + + // BUILD(macos): $CC main.c -o $BUILD_DIR/example.exe + // BUILD(ios): $CC main.c -DIOS=1 -o $BUILD_DIR/example.exe + // RUN(ios,macos): ./example.exe + int main() { return 0; } + +will build example.exe with distinct options for macOS and iOS, and will invoke example.exe on both macOS and iOS (but not tvOS, +watchOS, or bridgeOS). Valid platforms are "macos", ios", tvos", "watchos", "bridgeos". + When build lines are executed, the current directory is set to the test case's .dtest dir. Build lines may contain the follow variables: - $BUILD_DIR - expands to the directory in $DSTROOT where this test case binaries are installed - $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries will be run - $TEMP_DIR - expands to a temporary directory that will be delete after this test case is built - $CC - expands to the C compiler command line for the current platform. It includes - the min-os-version option, then SDK option, and the architectures. - $CXX - expands to the C++ compiler plus standard options + $BUILD_DIR - expands to the directory in $DSTROOT where this test case binaries are installed + $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries will be run + $TEMP_DIR - expands to a temporary directory that will be delete after this test case is built + $CC - expands to the C compiler command line for the current platform. It includes + the min-os-version option, then SDK option, and the architectures. + $CXX - expands to the C++ compiler plus standard options + $DEPENDS_ON - adds a build time dependency to the next argument on the commandline + $SKIP_INSTALL - prevents the built binary from being installed + $SYM_LINK - creates a symlink + $CP - copies a file When run lines are executed, the current directory is set to what $RUN_DIR was during build. Run lines may contain the follow variables: $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries are installed - $REQUIRE_CRASH - expands to the 'nocr' tool which is used when a program is expected to "crash" in order to pass -When a test program runs, it should initially print out the name of the test case. Ex: - [BEGIN] dlfoo-thread-safe -Then it should print a pass or fail message with the same test name. Ex: - [PASS] dlfoo-thread-safe +The file "test_support.h" provides support functions to correctly write tests. The primariy interfaces provided are +three printf like functions (technially they are implemented as macros in order to capture line info for logging): + void PASS(const char* format, ...); + void FAIL(const char* format, ...); + void LOG(const char* format, ...); -To support tests that dyld is supposed to terminate, use $REQUIRE_CRASH on the RUN: line -along with setting env var NOCR_TEST_NAME to the name of the test case. When that env -var is set, the nocr tool wil print out the [BEGIN], then [PASS] if the test crashes -otherwise [FAIL]. Ex: - // RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe - - -To support tests that are platform specific, add the BUILD_ONLY: line which specifies the platform. -Valid platforms are: MacOSX, iOS, watchOS, and tvOS. When a specific platform is specified, a -new min OS version can also be specified via the BUILD_MIN_OS option. For instance: - // BUILD_ONLY: MacOSX - // BUILD_MIN_OS: 10.5 - +PASS() and FAIL() will take care of appropriately formatting the messages for the various test environments that dyld_tests +run in. LOG() will capture messages in a per image queue. By default these logs are emitted if a test fails, and they are +ignored if a test succeeds. While debugging tests logs can be emitted even during success by setting the LOG_ON_SUCCESS +environment variable. This allows us to leave logging statements in production dyld_tests.Fdtra +Note, to run the tests as root, you need to first set "defaults write com.apple.dt.Xcode EnableRootTesting YES", +and then check the "Debug process as root" box in the Test scheme on the ContainerizedTestRunner scheme. diff --git a/testing/build_ninja.py b/testing/build_ninja.py new file mode 100755 index 0000000..8fcf40f --- /dev/null +++ b/testing/build_ninja.py @@ -0,0 +1,642 @@ +#!/usr/bin/python2.7 + +import plistlib +import string +import argparse +import sys +import os +import tempfile +import shutil +import subprocess +import re +import hashlib +import textwrap +from string import Template + +class BufferedFile: + def __init__(self, fileName): + self.data = "" + self.fileName = os.path.abspath(fileName) + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + if os.path.exists(self.fileName): + with open(self.fileName, "r") as file: + fileData = file.read() + if fileData == self.data: return + else: + dir = os.path.dirname(self.fileName) + if not os.path.exists(dir): + os.makedirs(dir) + with open(self.fileName, "w") as file: + file.write(self.data) + def write(self, str): + self.data += str + +class NinjaFile: + class Variable: + def __init__(self, name, value): + self.name = name + self.value = value + def __lt__(self, other): + return self.name.__lt__(other.name) + def __str__(self): + return NinjaFile.lineWrap("{} = {}".format(self.name, self.value)) + + class Rule: + def __init__(self, name, command, depfile): + self.name = name + self.command = command + self.depfile = depfile + def __lt__(self, other): + return self.name.__lt__(other.name) + def __str__(self): + result = NinjaFile.lineWrap("rule {}".format(self.name)) + if self.command: result += ("\n"+ NinjaFile.lineWrap(" command = {}".format(self.command))) + if self.depfile: + result += ("\n" + NinjaFile.lineWrap(" deps = gcc")) + result += ("\n" + NinjaFile.lineWrap(" depfile = {}".format(self.depfile))) + return result + class Target: + def __init__(self, rule): + self.rule = rule + self.output = "" + self.inputs = [] + self.variables = [] + self.dependencies = [] + def __lt__(self, other): + return self.output.__lt__(other.output) + def __str__(self): + self.inputs.sort() + self.variables.sort() + self.dependencies.sort() + buildLine = "build {}: {}".format(self.output, self.rule) + if self.inputs: buildLine += " {}".format(" ".join(self.inputs)) + if self.dependencies: buildLine += " | {}".format(" ".join(self.dependencies)) + result = NinjaFile.lineWrap(buildLine) + for variable in self.variables: result += ("\n" + NinjaFile.lineWrap(" " + str(variable))) + return result + def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value)) + def addDependency(self, dependency): + if isinstance(dependency, str): + self.dependencies.append(dependency) + elif isinstance(dependency, NinjaFile.Target): + self.dependencies.append(dependency.output) + else: + raise ValueError("dependency must be a string or NinjaFile.Target") + def addInput(self, input): + if isinstance(input, str): + self.inputs.append(input) + elif isinstance(input, NinjaFile.Target): + self.inputs.append(input.output) + else: + raise ValueError("input must be a string or NinjaFile.Target") + class Include: + def __init__(self, file): + self.file = file + def __lt__(self, other): + return self.file.__lt__(other.file) + def __str__(self): + return NinjaFile.lineWrap("include {}".format(self.file)) + + def __init__(self, fileName): + self.fileName = os.path.abspath(fileName) + self.rules = [] + self.variables = [] + self.targets = [] + self.includes = [] + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + with BufferedFile(self.fileName) as file: + file.write(str(self)) + def addRule(self, name, command, deps): self.rules.append(NinjaFile.Rule(name, command, deps)) + def addVariable(self, name, value): self.variables.append(NinjaFile.Variable(name, value)) + def addInclude(self, file): self.includes.append(NinjaFile.Include(file)) + def newTarget(self, type, name): + target = NinjaFile.Target(type) + target.output = name + self.targets.append(target) + return target + def findTarget(self, name): + #PERF If this gets to be significant we can sort the array and binary search it + for target in self.targets: + if target.output == name: return target + raise ValueError("Target \"{}\" not found".format(name)) + def deleteTarget(self, name): self.targets.remove(self.findTarget(name)) + def __str__(self): + self.variables.sort() + self.rules.sort() + self.targets.sort() + self.includes.sort() + subs = { + "VARIABLES": "\n".join(map(str, self.variables)), + "RULES": "\n\n".join(map(str, self.rules)), + "TARGETS": "\n\n".join(map(str, self.targets)), + "INCLUDES": "\n\n".join(map(str, self.includes)) + } + return string.Template( +"""ninja_required_version = 1.6 + +$INCLUDES + +$VARIABLES + +$RULES + +$TARGETS + +""").safe_substitute(subs) +# wrapper = textwrap.TextWrapper(width = 130, subsequent_indent = " ", break_long_words = False) + @classmethod + def lineWrap(cls, line): + if len(line) <= 132: return line + result = "" + currentIdx = 0 + wrappedLineLeadingSpace = " " + firstLineIndent = 0 + if line[0].isspace(): + firstLineIndent = 4 + result = " " + wrappedLineLeadingSpace = " " + trailer = " $" + wrappedLineLeadingSpaceLen = len(wrappedLineLeadingSpace) + lineSpaceAvailable = 132-(firstLineIndent+wrappedLineLeadingSpaceLen) + words = line.split() + wordsCount = len(words)-1 + for idx, word in enumerate(words): + wordLen = len(word) + if (wordLen <= lineSpaceAvailable and idx == wordsCount): + result += word + elif wordLen <= lineSpaceAvailable+2: + result += "{} ".format(word) + lineSpaceAvailable -= (wordLen) + else: + result += "$\n{}{} ".format(wrappedLineLeadingSpace, word) + lineSpaceAvailable = 132-(wrappedLineLeadingSpaceLen+wordLen) + return result + +def processBuildLines(ninja, buildLines, testName, platform, osFlag, forceArchs, testDstDir, testSrcDir): + testInstallTarget = ninja.newTarget("phony", "install-{}".format(testName)) + testTarget = ninja.newTarget("phony", testName) + ninja.findTarget("all").addInput(testTarget) + ninja.findTarget("install").addInput(testInstallTarget) + for buildLine in buildLines: + minOS = None + args = buildLine.split() + if args[0] == "$DTRACE": + target = None + for idx, arg in enumerate(args): + if arg == "-o": target = ninja.newTarget("dtrace", args[idx+1]) + for idx, arg in enumerate(args): + if arg == "-s": target.addInput(testSrcDir + "/" + args[idx+1]) + elif args[0] == "$CP": + target = ninja.newTarget("cp", args[2]) + target.addInput(testSrcDir + "/" + args[1]) + testTarget.addInput(target) + installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:])) + installTarget.addInput(target.output) + installTarget.addVariable("mode", "0644") + testInstallTarget.addInput(installTarget) + elif args[0] == "$SYMLINK": + target = ninja.newTarget("symlink", args[2]) + target.addVariable("source", args[1]) + testTarget.addInput(target) + installTarget = ninja.newTarget("symlink", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:])) + installTarget.addVariable("source", args[1]) + testInstallTarget.addInput(installTarget) + elif args[0] == "$STRIP": + target = ninja.findTarget(args[1]) + target.addVariable("extraCmds", "&& strip {}".format(target.output)) + elif args[0] == "$SKIP_INSTALL": + target = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[1][9:]) + ninja.deleteTarget(target) + testInstallTarget.inputs.remove(target) + elif args[0] == "$DYLD_ENV_VARS_ENABLE": + if platform != "macos": + target = ninja.findTarget(args[1]) + target.addVariable("entitlements", "--entitlements $SRCROOT/testing/get_task_allow_entitlement.plist") + elif args[0] == "$TASK_FOR_PID_ENABLE": + target = ninja.findTarget(args[1]) + target.addVariable("entitlements", "--entitlements $SRCROOT/testing/task_read_for_pid_entitlement.plist") + elif args[0] in ["$CC", "$CXX"]: + tool = args[0][1:].lower() + sources = [] + cflags = [] + ldflags = [] + dependencies = [] + skipCount = 0 + linkTarget = None + linkTestSupport = True + platformVersion = None + targetNames = [target.output for target in ninja.targets] + args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]] + #First find the target + for idx, arg in enumerate(args): + if arg == "-o": + linkTarget = ninja.newTarget("{}-link".format(tool), args[idx+1]) + linkTarget.addDependency("$BUILT_PRODUCTS_DIR/libtest_support.a") + testTarget.addInput(linkTarget); + break + skipCount = 0 + for idx, arg in enumerate(args): + if skipCount: skipCount -= 1 + elif arg == "-o": + skipCount = 1 + elif arg == "$DEPENDS_ON": + skipCount = 1 + dependencies.append(args[idx+1]) + elif arg in ["-arch"]: + skipCount = 1 + nextArg = args[idx+1] + ldflags.append(arg) + ldflags.append(nextArg) + cflags.append(arg) + cflags.append(nextArg) + elif arg in ["-target"]: + skipCount = 1 + nextArg = args[idx+1] + ldflags.append(arg) + ldflags.append(nextArg) + cflags.append(arg) + cflags.append(nextArg) + elif arg in ["-install_name","-framework", "-rpath","-compatibility_version","-sub_library", "-undefined", "-current_version"]: + skipCount = 1 + nextArg = args[idx+1] + ldflags.append(arg) + ldflags.append(nextArg) + elif arg == "-sectcreate": + skipCount = 3 + ldflags.append(arg) + ldflags.append(args[idx+1]) + ldflags.append(args[idx+2]) + ldflags.append(args[idx+3]) + elif arg[:2] == "-L": ldflags.append(arg) + elif arg[:2] == "-F": ldflags.append(arg) + elif arg == "-nostdlib": + ldflags.append(arg) + # Kernel tests pass -nostdlib so don't link test support + linkTestSupport = False + elif arg == "-flat_namespace": + ldflags.append(arg) + elif arg in ["-dynamiclib","-bundle"]: + ldflags.append(arg) + linkTestSupport = False + elif arg.endswith((".s", ".c", ".cpp", ".cxx", ".m", ".mm")): + if not arg.startswith("$SRCROOT"): sources.append(testSrcDir + "/" + arg) + else: sources.append(arg) + elif arg in targetNames: + linkTarget.addInput(arg) + elif osFlag in arg: + minOS = arg[arg.find('=')+1:] + elif arg[:4] == "-Wl,": + linkerArgs = arg.split(",") + if linkerArgs[1] == "-platform_version": + minOS = linkerArgs[3] + platformVersion = arg + else: + for linkerArg in linkerArgs[1:]: + if linkerArg in targetNames: linkTarget.addDependency(linkerArg) + ldflags.append(arg) + elif arg[:2] == "-l": + candidate = "{}/lib{}.dylib".format(testDstDir, arg[2:]) + if candidate in targetNames: linkTarget.addDependency(candidate) + ldflags.append(arg) + elif arg[:7] == "-weak-l": + candidate = "{}/lib{}.dylib".format(testDstDir, arg[7:]) + if candidate in targetNames: linkTarget.addDependency(candidate) + ldflags.append(arg) + elif arg[:9] == "-upward-l": + candidate = "{}/lib{}.dylib".format(testDstDir, arg[9:]) + if candidate in targetNames: linkTarget.addDependency(candidate) + ldflags.append(arg) + elif arg[:8] == "-fuse-ld": + # This is not typically used, but if we ever wanted to try a new ld64, it can be useful + ldflags.append(arg) + else: + cflags.append(arg) + if linkTestSupport: + ldflags.append("-force_load $BUILT_PRODUCTS_DIR/libtest_support.a") + for source in sources: + objectHash = hashlib.sha1(linkTarget.output+source+tool+"".join(cflags)).hexdigest() + target = ninja.newTarget(tool, "$OBJROOT/dyld_tests.build/Objects-normal/" + objectHash + ".o") + target.addInput(source) + target.dependencies = dependencies + if cflags: target.addVariable("cflags", " ".join(cflags)) + if minOS: target.addVariable("minOS", "-" + osFlag + "=" + minOS) + if forceArchs: target.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs])) + linkTarget.addInput(target) + if ldflags: linkTarget.addVariable("ldflags", " ".join(ldflags)) + if platformVersion: linkTarget.addVariable("minOS", platformVersion) + elif minOS: linkTarget.addVariable("minOS", "-" + osFlag + "=" + minOS) + if forceArchs: linkTarget.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs])) + installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(linkTarget.output[9:])) + installTarget.addInput(linkTarget) + testTarget.addInput(linkTarget) + testInstallTarget.addInput(installTarget) + elif args[0] == "$APP_CACHE_UTIL": + tool = args[0][1:].lower() + sources = [] + flags = [] + dependencies = [] + skipCount = 0 + linkTarget = None + args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]] + skipCount = 0 + for idx, arg in enumerate(args): + if skipCount: skipCount -= 1 + elif arg == "$DEPENDS_ON": + skipCount = 1 + dependencies.append(args[idx+1]) + elif arg == "-create-kernel-collection": + skipCount = 1 + linkTarget = ninja.newTarget("app-cache-util", args[idx+1]) + linkTarget.addVariable("create_kind", arg) + testTarget.addInput(linkTarget) + dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util") + elif arg == "-kernel": + skipCount = 1 + linkTarget.addInput(args[idx+1]) + flags.append(arg) + flags.append(args[idx+1]) + elif arg == "-create-aux-kernel-collection": + skipCount = 1 + linkTarget = ninja.newTarget("app-cache-util", args[idx+1]) + linkTarget.addVariable("create_kind", arg) + testTarget.addInput(linkTarget) + dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util") + elif arg == "-create-pageable-kernel-collection": + skipCount = 1 + linkTarget = ninja.newTarget("app-cache-util", args[idx+1]) + linkTarget.addVariable("create_kind", arg) + testTarget.addInput(linkTarget) + dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util") + elif arg == "-kernel-collection": + skipCount = 1 + linkTarget.addInput(args[idx+1]) + flags.append(arg) + flags.append(args[idx+1]) + elif arg == "-pageable-collection": + skipCount = 1 + linkTarget.addInput(args[idx+1]) + flags.append(arg) + flags.append(args[idx+1]) + else: + flags.append(arg) + linkTarget.dependencies = dependencies + if flags: linkTarget.addVariable("flags", " ".join(flags)) + if forceArchs: linkTarget.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs])) + installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(linkTarget.output[9:])) + installTarget.addInput(linkTarget) + testTarget.addInput(linkTarget) + testInstallTarget.addInput(installTarget) + else: raise ValueError("Unknown Command: {}".format(args[0])) + + +def processRunLine(runFile, runLine, environment): + if runLine.startswith("sudo "): + runFile.write("sudo {} {}\n".format(environment, runLine[5:])) + else: + runFile.write("{} {}\n".format(environment, runLine)) + +def processRunLines(ninja, runLines, testName, platform, runStatic, symRoot, xcTestInvocations): + runFilePath = "{}/{}/run.sh".format(symRoot, testName) + for runLine in runLines: + xcTestInvocations.append("{{ \"{}\", \"{}\" }}".format(testName, runLine.replace("\"","\\\"").replace("sudo",""))) + with BufferedFile(runFilePath) as runFile: + runFile.write("#!/bin/sh\n") + runFile.write("cd {}\n".format(testRunDir)) + + if runStatic: + runFile.write("echo \"run static\" \n"); + for runLine in runLines: + processRunLine(runFile, runLine, "TEST_OUTPUT=BATS") + else: + runFile.write("echo \"run in dyld2 mode\" \n"); + for runLine in runLines: + processRunLine(runFile, runLine, "TEST_OUTPUT=BATS DYLD_USE_CLOSURES=0") + + runFile.write("echo \"run in dyld3 mode\" \n"); + for runLine in runLines: + processRunLine(runFile, runLine, "TEST_OUTPUT=BATS DYLD_USE_CLOSURES=1") + + runFile.write("echo \"run in dyld3s mode\" \n"); + for runLine in runLines: + if runLine.startswith("sudo "): + runFile.write("sudo TEST_OUTPUT=BATS DYLD_USE_CLOSURES=2 {}\n".format(runLine[5:])) + else: + runFile.write("TEST_OUTPUT=BATS DYLD_USE_CLOSURES=2 {}\n".format(runLine)) + os.chmod(runFilePath, 0755) + installPath = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}/run.sh".format(testName) + target = ninja.newTarget("install", installPath) + target.addInput(runFilePath) + ninja.findTarget("install-{}".format(testName)).addInput(installPath) + +# returns a tuple of: +# 1. Idx after end of directive +# 2. Set of platforms directive is restricted to +# 3. Bool indicatin if the directive has a platform specifier +def parseDirective(line, directive, platform, archs): + idx = string.find(line, directive) + if idx == -1: return -1, archs, False + if line[idx + len(directive)] == ':': return idx+len(directive)+1, archs, False + match = re.match("\((.*?)\)?(?:\|(.*?))?\:", line[idx + len(directive):]); + if match: + foundPlatform = False + platforms = [] + restrictedArchs = [] + if match.group(1): + foundPlatform = True + platforms = match.group(1).split(","); + if match.group(2): restrictedArchs = match.group(2).split(","); + if platforms and platform not in platforms: return -1, archs, foundPlatform + effectiveArchs = list(set(archs) & set(restrictedArchs)) + if effectiveArchs: return idx + len(directive) + len(match.group()), effectiveArchs, foundPlatform + return line.find(':')+1, archs, foundPlatform + return -1, archs, False + +if __name__ == "__main__": + configPath = sys.argv[1] + configMap = {} + with open(configPath) as configFile: + for line in configFile.read().splitlines(): + args = line.split() + configMap[args[0]] = args[2:] + sys.stderr.write("CONFIG: {}\n".format(configMap)) + srcRoot = configMap["SRCROOT"][0] + symRoot = configMap["SYMROOT"][0] + sdkRoot = configMap["SDKROOT"][0] + objRoot = configMap["OBJROOT"][0] + osFlag = configMap["OSFLAG"][0] + osVers = configMap["OSVERSION"][0] + linkerFlags = configMap["LDFLAGS"][0]; + installOwner = configMap["INSTALL_OWNER"][0]; + installGroup = configMap["INSTALL_GROUP"][0]; + installMode = configMap["INSTALL_MODE_FLAG"][0]; + installDir = configMap["INSTALL_DIR"][0]; + userHeaderSearchPaths = configMap["USER_HEADER_SEARCH_PATHS"] + systemHeaderSearchPaths = configMap["SYSTEM_HEADER_SEARCH_PATHS"] + + derivedFilesDir = configMap["DERIVED_FILES_DIR"][0] + archs = configMap["ARCHS"] + + if not os.path.exists(derivedFilesDir): os.makedirs(derivedFilesDir) + if not os.path.exists(objRoot): os.makedirs(objRoot) + + sys.stderr.write("srcRoot = {}\n".format(srcRoot)) + sys.stderr.write("sdkRoot = {}\n".format(sdkRoot)) + sys.stderr.write("objRoot = {}\n".format(objRoot)) + sys.stderr.write("osFlag = {}\n".format(osFlag)) + sys.stderr.write("osVers = {}\n".format(osVers)) + sys.stderr.write("archs = {}\n".format(archs)) + sys.stderr.write("derivedFilesDir = {}\n".format(derivedFilesDir)) + + testSrcRoot = os.path.abspath(srcRoot + "/testing/test-cases") + ccTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang").read().rstrip() + cxxTool = os.popen("xcrun --sdk " + sdkRoot + " --find clang++").read().rstrip() + headerPaths = " -isysroot " + sdkRoot + for headerPath in userHeaderSearchPaths: headerPaths += " -I{}".format(headerPath) + for headerPath in systemHeaderSearchPaths: headerPaths += " -I{}".format(headerPath) + sudoCmd = "" + platform = "" + if osFlag == "mmacosx-version-min": + platform = "macos" + sudoCmd = "sudo" + elif osFlag == "miphoneos-version-min": platform = "ios" + elif osFlag == "mtvos-version-min": platform = "tvos" + elif osFlag == "mwatchos-version-min": platform = "watchos" + elif osFlag == "mbridgeos-version-min": platform = "bridgeos" + else: + sys.stderr.write("Unknown platform\n") + sys.exit(-1) + + with NinjaFile(derivedFilesDir + "/build.ninja") as ninja: + extraCmds = "$extraCmds" + if "RC_XBS" in os.environ and os.environ["RC_XBS"] == "YES": + extraCmds = "&& dsymutil -o $out.dSYM $out $extraCmds" + ninja.addInclude("config.ninja") + ninja.addVariable("minOS", "-" + osFlag + "=" + osVers) + ninja.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in archs])) + ninja.addVariable("mode", "0755") + ninja.addVariable("headerpaths", headerPaths) + + ninja.addRule("cc", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(ccTool), "$out.d") + ninja.addRule("cxx", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(cxxTool), "$out.d") + ninja.addRule("cc-link", "{} -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags {} && codesign --force --sign - $entitlements $out".format(ccTool, sdkRoot, linkerFlags, extraCmds), False) + ninja.addRule("cxx-link", "{} -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags {} && codesign --force --sign - $entitlements $out".format(cxxTool, sdkRoot, linkerFlags, extraCmds), False) + ninja.addRule("app-cache-util", "$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util $archs $create_kind $out $flags", False) + ninja.addRule("dtrace", "/usr/sbin/dtrace -h -s $in -o $out", False) + ninja.addRule("cp", "/bin/cp -p $in $out", False) + ninja.addRule("install", "/usr/bin/install -m $mode -o {} -g {} $install_flags $in $out".format(installOwner, installGroup), False) + ninja.addRule("symlink", "ln -sfh $source $out", False) + + allTarget = ninja.newTarget("phony", "all") + masterInstallTarget = ninja.newTarget("phony", "install") + + runAllScriptPath = "{}/run_all_dyld_tests.sh".format(derivedFilesDir) + xctestPath = "{}/dyld_xctest.h".format(derivedFilesDir) + if "XCTestGenPath" in os.environ: xctestPath = os.environ["XCTestGenPath"] + batsTests = [] + batsSuppressedCrashes = [] + xctestInvocations = [] + with BufferedFile(runAllScriptPath) as runAllScript: + missingPlatformDirectives = False + runAllScript.write("#!/bin/sh\n") + for entry in os.listdir(testSrcRoot): + if entry.endswith((".dtest")): + testName = entry[:-6] + sys.stdout.write("Processing " + testName + "\n") + runLines = [] + runStaticLines = [] + buildLines = [] + + testSrcDir = "$SRCROOT/testing/test-cases/{}.dtest".format(testName) + testDstDir = "$SYMROOT/{}".format(testName) + testRunDir = "/AppleInternal/CoreOS/tests/dyld/{}".format(testName) + + batsTest = {} + batsTest["TestName"] = testName + batsTest["Arch"] = "platform-native" + batsTest["WorkingDirectory"] = testRunDir + batsTest["ShowSubtestResults"] = True + batsTest["Command"] = [] + batsTest["Command"].append("./run.sh") + for file in os.listdir(testSrcRoot + "/" + entry): + buildSubs = { + "BUILD_DIR": testDstDir, + "RUN_DIR": testRunDir, + "SRC_DIR": testSrcDir + } + runSubs = { + "RUN_DIR": testRunDir, + "SUDO": sudoCmd, + "RUN_STATIC": "/AppleInternal/CoreOS/tests/dyld/run-static", + } + if file.endswith((".c", ".cpp", ".cxx", ".m", ".mm")): + with open(testSrcRoot + "/" + entry + "/" + file) as f: + requiresPlatformDirective = False + foundPlatformDirective = False + for line in f.read().splitlines(): + idx, forceArchs, foundPlatform = parseDirective(line, "BUILD", platform, archs); + if foundPlatform: requiresPlatformDirective = True + if idx != -1: + foundPlatformDirective = True + if line[idx:]: buildLines.append(string.Template(line[idx:]).safe_substitute(buildSubs)) + continue + idx, _, _ = parseDirective(line, "RUN", platform, archs); + if idx != -1: + if "$SUDO" in line: batsTest["AsRoot"] = True + runLines.append(string.Template(line[idx:]).safe_substitute(runSubs).lstrip()) + continue + idx, _, _ = parseDirective(line,"RUN_STATIC", platform, archs) + if idx != -1: + runStaticLines.append(string.Template(line[idx:]).safe_substitute(runSubs).lstrip()) + continue + idx, _, _ = parseDirective(line,"RUN_TIMEOUT", platform, archs) + if idx != -1: + batsTest["Timeout"] = line[idx:].lstrip() + continue + idx, _, _ = parseDirective(line,"BOOT_ARGS", platform, archs) + if idx != -1: + batsTest["BootArgsSet"] = ",".join(line[idx:].split()) + continue + idx, _, _ = parseDirective(line,"NO_CRASH_LOG", platform, archs) + if idx != -1: + batsSuppressedCrashes.append(line[idx:].lstrip()) + continue + if requiresPlatformDirective and not foundPlatformDirective: + missingPlatformDirectives = True + sys.stderr.write("Did not find platform({}) BUILD directive for {}\n".format(platform, testName)) + if buildLines and (runLines or runStaticLines): + processBuildLines(ninja, buildLines, testName, platform, osFlag, forceArchs, testDstDir, testSrcDir) + if runLines: + processRunLines(ninja, runLines, testName, platform, False, symRoot, xctestInvocations) + if runStaticLines: + processRunLines(ninja, runStaticLines, testName, platform, True, symRoot, xctestInvocations) + runAllScript.write("/AppleInternal/CoreOS/tests/dyld/{}/run.sh\n".format(testName)) + batsTests.append(batsTest) + if missingPlatformDirectives: sys.exit(-1) + sys.stderr.write("Wrote test config to: {}\n".format(xctestPath)) + with BufferedFile(xctestPath) as xcTestFile: + xcTestFile.write("static const TestInfo sTests[] = {\n") + xcTestFile.write(",\n".join(xctestInvocations)) + xcTestFile.write("\n};") + os.chmod(runAllScriptPath, 0755) + runAllFilesInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh") + runAllFilesInstallTarget.addInput("$DERIVED_FILES_DIR/run_all_dyld_tests.sh") + masterInstallTarget.addInput(runAllFilesInstallTarget) + batsFilePath = derivedFilesDir + "/dyld.plist" + batsTests.sort(key=lambda test: test["TestName"]) + with BufferedFile(batsFilePath) as batsFile: + batsConfig = { "BATSConfigVersion": "0.1.0", + "Project": "dyld_tests", + "Tests": batsTests } + if batsSuppressedCrashes: batsConfig["IgnoreCrashes"] = batsSuppressedCrashes + batsFile.write(plistlib.writePlistToString(batsConfig)) + os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary + batsConfigInstallTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist") + batsConfigInstallTarget.addInput(batsFilePath) + batsConfigInstallTarget.addVariable("mode", "0644") + masterInstallTarget.addInput(batsConfigInstallTarget) + sys.stdout.write("DONE\n") + diff --git a/testing/build_tests.py b/testing/build_tests.py deleted file mode 100755 index f33149f..0000000 --- a/testing/build_tests.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/python2.7 - -import plistlib -import string -import argparse -import sys -import os -import tempfile -import shutil -import subprocess -import uuid - - -# -# Scan files in .dtest directory looking for BUILD: or RUN: directives. -# Return a dictionary of all directives. -# -def parseDirectives(testCaseSourceDir): - onlyLines = [] - buildLines = [] - extractLines = [] - runLines = [] - minOS = "" - timeout = "" - noCrashLogs = [] - bootArgs = [] - for file in os.listdir(testCaseSourceDir): - if file.endswith((".c", ".cpp", ".cxx", ".m", ".mm")): - with open(testCaseSourceDir + "/" + file) as f: - for line in f.read().splitlines(): - buildIndex = string.find(line, "BUILD:") - if buildIndex != -1: - buildLines.append(line[buildIndex + 6:].lstrip()) - runIndex = string.find(line, "RUN:") - if runIndex != -1: - runLines.append(line[runIndex+4:].lstrip()) - onlyIndex = string.find(line, "BUILD_ONLY:") - if onlyIndex != -1: - onlyLines.append(line[onlyIndex+11:].lstrip()) - minOsIndex = string.find(line, "BUILD_MIN_OS:") - if minOsIndex != -1: - minOS = line[minOsIndex+13:].lstrip() - timeoutIndex = string.find(line, "RUN_TIMEOUT:") - if timeoutIndex != -1: - timeout = line[timeoutIndex+12:].lstrip() - noCrashLogsIndex = string.find(line, "NO_CRASH_LOG:") - if noCrashLogsIndex != -1: - noCrashLogs.append(line[noCrashLogsIndex+13:].lstrip()) - bootArgsIndex = string.find(line, "BOOT_ARGS:") - if bootArgsIndex != -1: - bootArgs.append(line[bootArgsIndex+10:].lstrip()) - return { - "BUILD": buildLines, - "BUILD_ONLY": onlyLines, - "BUILD_MIN_OS": minOS, - "RUN": runLines, - "RUN_TIMEOUT": timeout, - "NO_CRASH_LOG": noCrashLogs, - "BOOT_ARGS": bootArgs, - } - - -# -# Look at directives dictionary to see if this test should be skipped for this platform -# -def useTestCase(testName, testCaseDirectives, platformName): - onlyLines = testCaseDirectives["BUILD_ONLY"] - for only in onlyLines: - if only == "MacOSX" and platformName != "macosx": - return False - if only == "iOS" and platformName != "iphoneos": - return False - return True - - -# -# Use BUILD directives to construct the test case -# Use RUN directives to generate a shell script to run test(s) -# -def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, dyldIncludesDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun, plistDir): - 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" + " -I" + dyldIncludesDir + " -I" + testsSrcTopDir + "../include/" - defines = " -DINSTALL_PATH=\"" + testCaseDestDirRun + "\"" - if minOsOptionsName == "mmacosx-version-min": - taskForPidCommand = "touch " - envEnableCommand = "touch " - else: - taskForPidCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../task_for_pid_entitlement.plist " - envEnableCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../get_task_allow_entitlement.plist " - buildSubs = { - "CC": toolsDir + "/usr/bin/clang " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions + defines, - "CXX": toolsDir + "/usr/bin/clang++ " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions + defines, - "BUILD_DIR": testCaseDestDirBuild, - "RUN_DIR": testCaseDestDirRun, - "TEMP_DIR": scratchDir, - "TASK_FOR_PID_ENABLE": taskForPidCommand, - "DYLD_ENV_VARS_ENABLE": envEnableCommand - } - os.makedirs(testCaseDestDirBuild) - os.chdir(testCaseSourceDir) - outputfiles = [] - alreadySigned = [] - print >> sys.stderr, "cd " + testCaseSourceDir - for line in testCaseDirectives["BUILD"]: - cmd = string.Template(line).safe_substitute(buildSubs) - if "codesign" in cmd: - alreadySigned.append(string.split(cmd).pop()) - print >> sys.stderr, cmd - if "&&" in cmd: - result = subprocess.call(cmd, shell=True) - else: - cmdList = [] - cmdList = string.split(cmd) - result = subprocess.call(cmdList) - if result: - return result - args = cmd.split() - for index, arg in enumerate(args): - if arg == "-o": - outputfiles.append(args[index+1]) - break - print >> sys.stderr, "outfiles: " + ' '.join(outputfiles) + "already signed: " + ' '.join(alreadySigned) - for outfile in outputfiles: - if outfile not in alreadySigned: - cmd = "codesign --force --sign - " + outfile - print >> sys.stderr, cmd - subprocess.call(string.split(cmd)) - shutil.rmtree(scratchDir, ignore_errors=True) - sudoSub = "" - if minOsOptionsName == "mmacosx-version-min": - sudoSub = "sudo" - runSubs = { - "RUN_DIR": testCaseDestDirRun, - "REQUIRE_CRASH": "nocr -require_crash", - "SUDO": sudoSub, - } - runFilePath = testCaseDestDirBuild + "/run.sh" - with open(runFilePath, "a") as runFile: - runFile.write("#!/bin/sh\n") - runFile.write("cd " + testCaseDestDirRun + "\n") - os.chmod(runFilePath, 0755) - - runFile.write("echo \"run in dyld2 mode\" \n"); - for runline in testCaseDirectives["RUN"]: - subLine = string.Template(runline).safe_substitute(runSubs) - subLine = "TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 " + subLine - runFile.write(subLine + "\n") - - if minOsOptionsName == "mmacosx-version-min": - runFile.write("echo \"run in dyld2 mode with no shared cache\" \n"); - for runline in testCaseDirectives["RUN"]: - subLine = string.Template(runline).safe_substitute(runSubs) - subLine = "TEST_DYLD_MODE=2 DYLD_SHARED_REGION=avoid " + subLine - runFile.write(subLine + "\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 TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 " + subLine[5:] - else: - subLine = "TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 " + subLine - runFile.write(subLine + "\n") - - if minOsOptionsName == "mmacosx-version-min": - runFile.write("echo \"run in dyld3 mode with no shared cache\" \n"); - for runline in testCaseDirectives["RUN"]: - subLine = string.Template(runline).safe_substitute(runSubs) - if subLine.startswith("sudo "): - subLine = "sudo TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 " + subLine[5:] - else: - subLine = "TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 " + subLine - runFile.write(subLine + "\n") - - runFile.write("\n") - runFile.close() - for runline in testCaseDirectives["RUN"]: - runTarget = runline.split().pop() - os.system("xcrun dt_extractmeta extract -i " + testCaseDestDirRun + "/" + runTarget + " -b " + testCaseDestDirBuild + "/" + runTarget + " -o " + plistDir + "/" + str(uuid.uuid4()) + ".plist 2> /dev/null") - return 0 - - - -# -# Use XCode build settings to build all unit tests for specified platform -# Generate a .plist for BATS to use to run all tests -# -if __name__ == "__main__": - dstDir = os.getenv("DSTROOT", "/tmp/dyld_tests/") - testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/" - testsBuildDstTopDir = dstDir + testsRunDstTopDir - # If we want to run directly from the dstroot then override that now - runFromDstRoot = os.getenv("RUN_FROM_DSTROOT", "") - if runFromDstRoot: - testsRunDstTopDir = testsBuildDstTopDir - shutil.rmtree(testsBuildDstTopDir, ignore_errors=True) - dyldSrcDir = os.getenv("SRCROOT", "") - 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", "-sdk", "macosx.internal", "--show-sdk-path"]).rstrip() - toolsDir = os.getenv("TOOLCHAIN_DIR", "/") - defaultMinOS = "" - minVersNum = "10.14" - minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "") - if minOSOption: - minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "") - if minOSVersName: - minVersNum = os.getenv(minOSVersName, "") - else: - minOSOption = "mmacosx-version-min" - platformName = os.getenv("PLATFORM_NAME", "macosx") - archOptions = "" - archList = os.getenv("RC_ARCHS", "") - if archList: - for arch in string.split(archList, " "): - archOptions = archOptions + " -arch " + arch - else: - if platformName == "watchos": - archOptions = "-arch armv7k" - elif platformName == "appletvos": - archOptions = "-arch arm64" - elif platformName == "macosx": - archList = os.getenv("ARCHS_STANDARD_64_BIT", "") - if archList: - for arch in string.split(archList, " "): - archOptions = archOptions + " -arch " + arch - else: - archOptions = "-arch x86_64" - else: - archList = os.getenv("ARCHS_STANDARD_32_64_BIT", "") - if archList: - for arch in string.split(archList, " "): - archOptions = archOptions + " -arch " + arch - else: - archOptions = "-arch x86_64" - allTests = [] - suppressCrashLogs = [] - plistDir = tempfile.mkdtemp() - for f in sorted(os.listdir(testsSrcTopDir)): - if f.endswith(".dtest"): - testName = f[0:-6] - outDirBuild = testsBuildDstTopDir + testName - outDirRun = testsRunDstTopDir + testName - testCaseDir = testsSrcTopDir + f - onlyTestDir = os.getenv("ONLY_BUILD_TEST", "") - if onlyTestDir: - if onlyTestDir != testName: - continue - print >> sys.stderr, "Going to build " + testName - testCaseDirectives = parseDirectives(testCaseDir) - if useTestCase(testName, testCaseDirectives, platformName): - result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, dyldIncludesDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun, plistDir) - if result: - sys.exit(result) - mytest = {} - mytest["TestName"] = testName - mytest["Arch"] = "platform-native" - mytest["WorkingDirectory"] = testsRunDstTopDir + testName - mytest["Command"] = [] - mytest["Command"].append("./run.sh") - for runline in testCaseDirectives["RUN"]: - if "$SUDO" in runline: - mytest["AsRoot"] = True - if testCaseDirectives["RUN_TIMEOUT"]: - mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"] - if testCaseDirectives["BOOT_ARGS"]: - mytest["BootArgsSet"] = ",".join(testCaseDirectives["BOOT_ARGS"]); - 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) - batsFilePath = batsFileDir + "dyld.plist" - with open(batsFilePath, "w") as batsFile: - batsFile.write(plistlib.writePlistToString(batsInfo)) - batsFile.close() - os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary - runHelper = dstDir + "/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh" - print runHelper - with open(runHelper, "w") as shFile: - shFile.write("#!/bin/sh\n") - for test in allTests: - shFile.write(test["WorkingDirectory"] + "/run.sh\n") - shFile.close() - os.chmod(runHelper, 0755) - if not os.path.exists(dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/"): os.makedirs(dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/") - os.system("xcrun dt_extractmeta merge -o " + dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/dyld.plist " + plistDir + "/*") -# FIXME: Enable this once all tests move to darwintest -# os.system("xcrun dt_convertmeta " + dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist dyld_tests " + dstDir + "/AppleInternal/CoreOS/tests/metadata/dyld/dyld.plist") - shutil.rmtree(plistDir, ignore_errors=True) - diff --git a/testing/include/dyld_test.h b/testing/include/dyld_test.h deleted file mode 100644 index fe2227b..0000000 --- a/testing/include/dyld_test.h +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#include "darwintest.h" - -T_GLOBAL_META(T_META_NAMESPACE("dyld")); - -extern char** environ; - -#if __x86_64__ -cpu_type_t otherArch[] = { CPU_TYPE_I386 }; -#elif __i386__ -cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; -#elif __arm64__ -cpu_type_t otherArch[] = { CPU_TYPE_ARM }; -#elif __arm__ -cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; -#endif - -#define T_DECL_DYLD(name, description, ...) \ - static void dyld_test_ ## name(void); \ - T_DECL(name, description, ## __VA_ARGS__) { \ - dyld_test_ ## name(); \ - } \ - static void dyld_test_ ## name(void) - -/* Since the test runner manually invokes us in both dyld2 and dyld3 mode we do not need this yet - - T_DECL(name ## _dyld3, description, T_META_ENVVAR("DYLD_USE_CLOSURES=1"), ## __VA_ARGS__) { \ - dyld_test_ ## name(); \ - } \ - - */ - - -#define T_DLOPEN_EXPECT_NOTNULL(path, mode) \ -({ \ - void* handle = dlopen(path, mode); \ - T_QUIET; T_ASSERT_NOTNULL(handle, "Image \"%s\" failed to load with error: %s", path, dlerror()); \ - T_QUIET; T_ASSERT_NULL(dlerror(), "dlerror() should be null after successfull dloepn()"); \ - handle; \ -}) - -#define T_DLSYM_EXPECT_NOTNULL(handle, symbol) \ -({ \ - const void* sym = dlsym((void *)handle, symbol); \ - T_QUIET; T_ASSERT_NOTNULL(sym, "dlsym(%s) should not be null", symbol); \ - sym; \ -}) - -#define T_DLCLOSE_EXPECT_NULL(handle) \ -({ \ - int result = dlclose((void *)handle); \ - T_QUIET; T_ASSERT_EQ_INT(result, 0, "dlclose() failed with error: %s", dlerror()); \ - result; \ -}) - -#define T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, program) \ -({ \ - pid_t result = 0; \ - posix_spawnattr_t attr = 0; \ - T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed"); \ - if ( launchSuspended ) { \ - int result = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); \ - T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setflags() failed"); \ - } \ - if ( launchOtherArch ) { \ - size_t copied; \ - int result = posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied); \ - T_QUIET; T_ASSERT_EQ_INT(result, 0, "posix_spawnattr_setbinpref_np(), &copied) failed"); \ - } \ - const char* argv[] = { program, NULL }; \ - int psResult = posix_spawn(&result, program, NULL, &attr, (char**)argv, environ); \ - T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult); \ - T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed"); \ - result; \ -}) - -#define T_TASK_FOR_PID_ASSERT(pid) \ -({ \ - task_t result; \ - T_QUIET; T_ASSERT_MACH_SUCCESS(task_for_pid(mach_task_self(), pid, &result), "task_for_pid() failed"); \ - result; \ -}) - -#pragma pack(4) -typedef struct exception_data { - mach_msg_header_t Head; - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - __int64_t code[2]; -} exception_data; -#pragma pack() - -typedef bool(^exceptionValidator)(task_t task); - -#define T_POSIXSPAWN_CRASH(program, validatorFunc, ...) \ -({ \ - pid_t pid = 0; \ - posix_spawnattr_t attr = 0; \ - mach_port_t exceptionPort = MACH_PORT_NULL; \ - T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_init(&attr), 0, "dyld_process_info posix_spawnattr_init() failed"); \ - mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }}; \ - T_QUIET; T_ASSERT_MACH_SUCCESS(mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, \ - &exceptionPort), "mach_port_construct() failed"); \ - int epResult = posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CORPSE_NOTIFY, exceptionPort, \ - EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0); \ - T_QUIET; T_ASSERT_EQ_INT(epResult, 0, "posix_spawnattr_setexceptionports_np() failed"); \ - const char* argv[] = { program, NULL }; \ - int psResult = posix_spawn(&pid, program, NULL, &attr, (char**)argv, environ); \ - T_QUIET; T_ASSERT_EQ_INT(psResult, 0, "dyld_process_info posix_spawn(%s) failed, err=%d\n", program, psResult); \ - T_QUIET; T_ASSERT_EQ_INT(posix_spawnattr_destroy(&attr), KERN_SUCCESS, "posix_spawnattr_destroy() failed"); \ - uint8_t data[MACH_MSG_SIZE_RELIABLE]; \ - exception_data* request = (exception_data*)&data[0]; \ - T_QUIET; T_ASSERT_MACH_SUCCESS(mach_msg(&request->Head, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, \ - MACH_MSG_SIZE_RELIABLE, exceptionPort, 10000, MACH_PORT_NULL), "mach_msg() failed"); \ - validatorFunc((task_t)request->task.name); \ -}) diff --git a/testing/include/test_support.h b/testing/include/test_support.h new file mode 100644 index 0000000..b287e4c --- /dev/null +++ b/testing/include/test_support.h @@ -0,0 +1,99 @@ +#ifndef __DYLD_TEST_SUPPORT_H__ +#define __DYLD_TEST_SUPPORT_H__ 1 + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include +#include + +#if __cplusplus +}; + +// Only allow this interface for Objective-C++ due to typename and ARC issues in the default constructor + +typedef void (^_dyld_test_reader_t)(int fd); +typedef void (^_dyld_test_exit_handler_t)(pid_t pid); +typedef void (^_dyld_test_crash_handler_t)(task_t task); + +struct _process { + _process(); + ~_process(); + void set_executable_path(const char* EP); + void set_args(const char** A); + void set_env(const char** E); + void set_stdout_handler(_dyld_test_reader_t SOH); + void set_stderr_handler(_dyld_test_reader_t SEH); + void set_exit_handler(_dyld_test_exit_handler_t EH); + void set_crash_handler(_dyld_test_crash_handler_t CH); + void set_launch_suspended(bool S); + void set_launch_arch(cpu_type_t A); + pid_t launch(); + void *operator new(size_t size); + void operator delete(void *ptr); +private: + const char* executablePath; + const char** args; + const char** env; + _dyld_test_reader_t stdoutHandler; + _dyld_test_reader_t stderrHandler; + _dyld_test_crash_handler_t crashHandler; + _dyld_test_exit_handler_t exitHandler; + pid_t pid; + cpu_type_t arch; + bool suspended; + bool async; +}; + +#define STDERR_WRITER ^(int fd) { \ + char buffer[16384]; \ + ssize_t size = 0; \ + do { \ + size = read(fd, &buffer[0], 16384); \ + buffer[size] = 0; \ + fprintf(stderr, "%s", &buffer[0]); \ + } while (size > 0); \ +} + +#define STDOUT_WRITER ^(int fd) { \ + char buffer[16384]; \ + ssize_t size = 0; \ + do { \ + size = read(fd, &buffer[0], 16384); \ + buffer[size] = 0; \ + fprintf(stdout, "%s", &buffer[0]); \ + } while (size > 0); \ +} + +#endif /* __cplusplus */ + +#define PASS(...) _PASS(__FILE__,__LINE__,__VA_ARGS__) +#define FAIL(...) _FAIL(__FILE__,__LINE__,__VA_ARGS__) +#define LOG(...) _LOG(__FILE__,__LINE__,__VA_ARGS__) +#define TIMEOUT(seconds) _TIMEOUT(__FILE__,__LINE__,seconds) + +// MARK: Private implementation details + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _PASS(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _FAIL(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +extern void _LOG(const char* file, unsigned line, const char* format, ...); + +extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds); +#if __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* __DYLD_TEST_SUPPORT_H__ */ diff --git a/testing/kernel-cache-tests/KernelCollection.py b/testing/kernel-cache-tests/KernelCollection.py new file mode 100644 index 0000000..cf7ea08 --- /dev/null +++ b/testing/kernel-cache-tests/KernelCollection.py @@ -0,0 +1,206 @@ +#!/usr/bin/python2.7 + +import string +import os +import json +import sys +import commands +import subprocess + + +class KernelCollection: + + def __init__(self): + self.print_json=False + + def __init__(self, print_json): + self.print_json = print_json + + def buildKernelCollection(self, arch_flag, kernel_cache_path, kernel_path, extensions_dir, bundle_ids=[], options=[]): + try: + test_root = os.path.dirname(__file__) + build_root = os.path.realpath(os.path.dirname(__file__) + "/..") + app_cache_util = build_root + "/../build/Release/dyld_app_cache_util" + args = [app_cache_util, + "-create-kernel-collection", test_root + kernel_cache_path, + "-kernel", test_root + kernel_path, + "-arch", arch_flag] + if extensions_dir is not None: + args.append("-extensions") + args.append(test_root + extensions_dir) + for bundle_id in bundle_ids: + args.append("-bundle-id") + args.append(bundle_id) + file_text = subprocess.check_output(["file", build_root + kernel_cache_path]); + for opt in options: + args.append(opt.replace("$PWD", test_root)) + + runline = "" + for arg in args: + if not arg: + runline = runline + '"' + arg + '"' + ' ' + else: + runline = runline + arg + ' ' + + self.dict = {} + if self.print_json: + print "Run with: " + runline + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.json_text, self.error_message = process.communicate() + if self.print_json: + print self.json_text + print self.error_message + if process.returncode: + if not self.print_json: + print self.error_message + print "Non-zero return code" + print "Run with: " + runline + sys.exit(0) + #print self.json_text + #print self.error_message + if self.json_text: + self.dict = json.loads(self.json_text) + self.error_message = "" + except subprocess.CalledProcessError as e: + #print "can't make closure for " + kernel_cache_path + self.error_message = e.output + self.dict = {} + except: + assert False + self.dict = {} + + def buildPageableKernelCollection(self, arch_flag, aux_kernel_cache_path, kernel_cache_path, extensions_dir, bundle_ids=[], options=[]): + try: + test_root = os.path.dirname(__file__) + build_root = os.path.realpath(os.path.dirname(__file__) + "/..") + app_cache_util = build_root + "/../build/Release/dyld_app_cache_util" + args = [app_cache_util, + "-create-pageable-kernel-collection", test_root + aux_kernel_cache_path, + "-kernel-collection", test_root + kernel_cache_path, + "-arch", arch_flag] + if extensions_dir is not None: + args.append("-extensions") + args.append(test_root + extensions_dir) + for bundle_id in bundle_ids: + args.append("-bundle-id") + args.append(bundle_id) + file_text = subprocess.check_output(["file", build_root + kernel_cache_path]); + for opt in options: + args.append(opt) + self.dict = {} + if self.print_json: + print "Run with: " + ' '.join(args) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.json_text, self.error_message = process.communicate() + if self.print_json: + print self.json_text + print self.error_message + if process.returncode: + if not self.print_json: + print self.error_message + print "Non-zero return code" + print "Run with: " + ' '.join(args) + sys.exit(0) + #print self.json_text + #print self.error_message + if self.json_text: + self.dict = json.loads(self.json_text) + self.error_message = "" + except subprocess.CalledProcessError as e: + #print "can't make closure for " + kernel_cache_path + self.error_message = e.output + self.dict = {} + except: + assert False + self.dict = {} + + def buildAuxKernelCollection(self, arch_flag, aux_kernel_cache_path, kernel_cache_path, pageable_cache_path, extensions_dir, bundle_ids=[], options=[]): + try: + test_root = os.path.dirname(__file__) + build_root = os.path.realpath(os.path.dirname(__file__) + "/..") + app_cache_util = build_root + "/../build/Release/dyld_app_cache_util" + args = [app_cache_util, + "-create-aux-kernel-collection", test_root + aux_kernel_cache_path, + "-kernel-collection", test_root + kernel_cache_path, + "-arch", arch_flag] + if pageable_cache_path: + args.append("-pageable-collection") + args.append(test_root + pageable_cache_path) + if extensions_dir is not None: + args.append("-extensions") + args.append(test_root + extensions_dir) + for bundle_id in bundle_ids: + args.append("-bundle-id") + args.append(bundle_id) + file_text = subprocess.check_output(["file", build_root + kernel_cache_path]); + for opt in options: + args.append(opt) + self.dict = {} + if self.print_json: + print "Run with: " + ' '.join(args) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.json_text, self.error_message = process.communicate() + if self.print_json: + print self.json_text + print self.error_message + if process.returncode: + if not self.print_json: + print self.error_message + print "Non-zero return code" + print "Run with: " + ' '.join(args) + sys.exit(0) + #print self.json_text + #print self.error_message + if self.json_text: + self.dict = json.loads(self.json_text) + self.error_message = "" + except subprocess.CalledProcessError as e: + #print "can't make closure for " + kernel_cache_path + self.error_message = e.output + self.dict = {} + except: + assert False + self.dict = {} + + def analyze(self, app_cache_path, options=[]): + try: + test_root = os.path.dirname(__file__) + build_root = os.path.realpath(os.path.dirname(__file__) + "/..") + app_cache_util = build_root + "/../build/Release/dyld_app_cache_util" + args = [app_cache_util, "-app-cache", test_root + app_cache_path, "-platform", "kernel"] + file_text = subprocess.check_output(["file", build_root + app_cache_path]); + for opt in options: + args.append(opt) + self.dict = {} + if self.print_json: + print "Run with: " + ' '.join(args) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.json_text, self.error_message = process.communicate() + if self.print_json: + print self.json_text + print self.error_message + if process.returncode: + if not self.print_json: + print self.error_message + print "Non-zero return code" + print "Run with: " + ' '.join(args) + sys.exit(0) + #print self.json_text + #print self.error_message + if self.json_text: + self.dict = json.loads(self.json_text) + self.error_message = "" + except subprocess.CalledProcessError as e: + #print "can't make closure for " + app_cache_path + self.error_message = e.output + self.dict = {} + except: + assert False + self.dict = {} + + def dictionary(self): + return self.dict + + def error(self): + return self.error_message + diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo differ diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c similarity index 53% rename from unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c rename to testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c index 4684921..5a6eec2 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c @@ -1,5 +1,4 @@ -int bar() -{ +int foo() { return 0; } diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py new file mode 100644 index 0000000..d5c61e3 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py @@ -0,0 +1,35 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/main.kernel", "", [], []) + kernel_cache.analyze("/auxkc-bind-to-pageablekc/main.kc", ["-layout", "-arch", "arm64"]) + + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-bind-to-pageablekc/pageable.kc", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-bind-to-pageablekc/pageable.kc", ["-symbols", "-arch", "arm64"]) + + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["vmAddr"] == "0x8000" + + # Now build an aux cache using the baseline and pageable kernel collections + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-bind-to-pageablekc/aux.kc", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/pageable.kc", "/auxkc-bind-to-pageablekc/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/auxkc-bind-to-pageablekc/aux.kc", ["-fixups", "-arch", "arm64"]) + + # bar.kext + assert len(kernel_cache.dictionary()["fixups"]) == 1 + # extern int foo() + assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(1) + 0x8000" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/bar.c b/testing/kernel-cache-tests/auxkc-branch-fixups/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar new file mode 100755 index 0000000..9aca68e Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo new file mode 100755 index 0000000..fd1bc1a Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/foo.c b/testing/kernel-cache-tests/auxkc-branch-fixups/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/main.c b/testing/kernel-cache-tests/auxkc-branch-fixups/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel b/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel new file mode 100755 index 0000000..cdc3eba Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/test.py b/testing/kernel-cache-tests/auxkc-branch-fixups/test.py new file mode 100644 index 0000000..f5d864f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-branch-fixups/test.py @@ -0,0 +1,21 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that we can link x86_64 kext's with branch relocations + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("x86_64", "/auxkc-branch-fixups/main.kc", "/auxkc-branch-fixups/main.kernel", "/auxkc-branch-fixups/extensions", [], []) + kernel_cache.analyze("/auxkc-branch-fixups/main.kc", ["-layout", "-arch", "x86_64"]) + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-branch-fixups/aux.kc", "/auxkc-branch-fixups/main.kc", "", "/auxkc-branch-fixups/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/auxkc-branch-fixups/aux.kc", ["-layout", "-arch", "x86_64"]) + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__HIB,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist new file mode 100644 index 0000000..91041a2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist @@ -0,0 +1,26 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.bar + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _symbol_from_bar + AliasTarget + _symbol_from_xnu + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports new file mode 100644 index 0000000..32a6100 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports @@ -0,0 +1 @@ +_symbol_from_bar:_symbol_from_xnu diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist new file mode 100644 index 0000000..1ec2b4b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.codeless + 1.0 + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo new file mode 100755 index 0000000..1e68cc8 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist new file mode 100644 index 0000000..baa1369 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + codeless + CFBundleIdentifier + com.apple.codeless + CFBundleName + codeless + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c new file mode 100644 index 0000000..f71c06d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c @@ -0,0 +1,6 @@ + +extern int symbol_from_bar(); + +int foo() { + return symbol_from_bar(); +} diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c new file mode 100644 index 0000000..263aaad --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c @@ -0,0 +1,9 @@ + +// This will be re-exported from a symbol set in bar +int symbol_from_xnu() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel new file mode 100755 index 0000000..bc817c4 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py new file mode 100644 index 0000000..818103a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py @@ -0,0 +1,30 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# codeless kext's are in the PRELINK_INFO in the baseKC, but can be a dependency for auxKC kexts + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel", "/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc", ["com.apple.bar", "com.apple.codeless"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", "", "/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", ["-layout", "-arch", "arm64"]) + + # Check the fixups + kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(0) + 0x8000" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist new file mode 100644 index 0000000..91041a2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist @@ -0,0 +1,26 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.bar + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _symbol_from_bar + AliasTarget + _symbol_from_xnu + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports new file mode 100644 index 0000000..32a6100 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports @@ -0,0 +1 @@ +_symbol_from_bar:_symbol_from_xnu diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..1111344 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo new file mode 100755 index 0000000..1e68cc8 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c new file mode 100644 index 0000000..f71c06d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c @@ -0,0 +1,6 @@ + +extern int symbol_from_bar(); + +int foo() { + return symbol_from_bar(); +} diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c new file mode 100644 index 0000000..263aaad --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c @@ -0,0 +1,9 @@ + +// This will be re-exported from a symbol set in bar +int symbol_from_xnu() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel new file mode 100755 index 0000000..bc817c4 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py new file mode 100644 index 0000000..85219ad --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py @@ -0,0 +1,30 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that we use the symbol set when resolving symbols from aux KC to the kernel + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-kernel/main.kc", "/auxkc-kext-bind-to-kernel/main.kernel", "/auxkc-kext-bind-to-kernel/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-kernel/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-kernel/aux.kc", "/auxkc-kext-bind-to-kernel/main.kc", "", "/auxkc-kext-bind-to-kernel/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-kernel/aux.kc", ["-layout", "-arch", "arm64"]) + + # Check the fixups + kernel_cache.analyze("/auxkc-kext-bind-to-kernel/aux.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(0) + 0x8000" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c new file mode 100644 index 0000000..8d0c3b4 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c @@ -0,0 +1,4 @@ + +int symbol_from_bar() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist new file mode 100644 index 0000000..1ec2b4b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.codeless + 1.0 + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo new file mode 100755 index 0000000..1e68cc8 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist new file mode 100644 index 0000000..77b3de8 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar new file mode 100755 index 0000000..0f2e34a Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist new file mode 100644 index 0000000..baa1369 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + codeless + CFBundleIdentifier + com.apple.codeless + CFBundleName + codeless + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c new file mode 100644 index 0000000..f71c06d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c @@ -0,0 +1,6 @@ + +extern int symbol_from_bar(); + +int foo() { + return symbol_from_bar(); +} diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c new file mode 100644 index 0000000..263aaad --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c @@ -0,0 +1,9 @@ + +// This will be re-exported from a symbol set in bar +int symbol_from_xnu() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel new file mode 100755 index 0000000..1c0fb7f Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py new file mode 100644 index 0000000..1e0eaa1 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# codeless kext's are in the PRELINK_INFO in the pageableKC, but can be a dependency for auxKC kexts + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel", None, [], []) + kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc", ["com.apple.bar", "com.apple.codeless"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", ["-layout", "-arch", "arm64"]) + + # Check the fixups + kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(1) + 0x8000" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions-auxkc/foo.kext/foo +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions-pageablekc/bar.kext/bar +# [~]> rm -r extensions-*/*.kext/*.ld diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist new file mode 100644 index 0000000..df941be --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.libkern + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _gOSKextUnresolved + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c new file mode 100644 index 0000000..9e23395 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c @@ -0,0 +1,12 @@ + +__attribute__((weak)) +extern int weakValue; + +extern int gOSKextUnresolved; + +int bar() { + // Missing weak import test + if ( &weakValue != &gOSKextUnresolved ) + return 0; + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..7e9987d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.libkern + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar new file mode 100755 index 0000000..920f5e6 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..7a43b38 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.libkern + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo new file mode 100755 index 0000000..f29c662 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c new file mode 100644 index 0000000..9e23395 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c @@ -0,0 +1,12 @@ + +__attribute__((weak)) +extern int weakValue; + +extern int gOSKextUnresolved; + +int bar() { + // Missing weak import test + if ( &weakValue != &gOSKextUnresolved ) + return 0; + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c new file mode 100644 index 0000000..31fd572 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c @@ -0,0 +1,7 @@ + +// We need this symbol to bind missing weak imports to +int gOSKextUnresolved = 0; + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel new file mode 100755 index 0000000..476d3aa Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py b/testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py new file mode 100644 index 0000000..4b7f49c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py @@ -0,0 +1,38 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that weak binds can be missing, so long as we check for the magic symbol + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/auxkc-missing-weak-bind/main.kc", "/auxkc-missing-weak-bind/main.kernel", "/auxkc-missing-weak-bind/extensions", [], []) + kernel_cache.analyze("/auxkc-missing-weak-bind/main.kc", ["-symbols", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_gOSKextUnresolved" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0x10000" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-missing-weak-bind/aux.kc", "/auxkc-missing-weak-bind/main.kc", "", "/auxkc-missing-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/auxkc-missing-weak-bind/aux.kc", ["-fixups", "-arch", "arm64"]) + + # Check the fixups + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0x10000" + assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0x10000" + assert kernel_cache.dictionary()["fixups"]["0x14010"] == "kc(0) + 0x10000" + assert kernel_cache.dictionary()["fixups"]["0x14018"] == "kc(0) + 0x10000" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/bar.c b/testing/kernel-cache-tests/auxkc-no-split-seg/bar.c new file mode 100644 index 0000000..3bb1d0b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/bar.c @@ -0,0 +1,7 @@ + +int i = 2; +int*p = &i; + +int bar() { + return i; +} diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar new file mode 100755 index 0000000..c53aafd Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo new file mode 100755 index 0000000..07ee33d Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/foo.c b/testing/kernel-cache-tests/auxkc-no-split-seg/foo.c new file mode 100644 index 0000000..3a9c313 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/foo.c @@ -0,0 +1,7 @@ + +int i = 2; +int*p = &i; + +int foo() { + return i; +} diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/main.c b/testing/kernel-cache-tests/auxkc-no-split-seg/main.c new file mode 100644 index 0000000..2aa96d7 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/main.c @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB,__text")))) +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel b/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel new file mode 100755 index 0000000..1bcd894 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/test.py b/testing/kernel-cache-tests/auxkc-no-split-seg/test.py new file mode 100644 index 0000000..430e9ba --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-no-split-seg/test.py @@ -0,0 +1,97 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that we can build an auxKC from third party kext's without split seg + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("x86_64", "/auxkc-no-split-seg/main.kc", "/auxkc-no-split-seg/main.kernel", "/auxkc-no-split-seg/extensions", [], []) + kernel_cache.analyze("/auxkc-no-split-seg/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__HIB" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__HIB" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x14000" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-no-split-seg/aux.kc", "/auxkc-no-split-seg/main.kc", "", "/auxkc-no-split-seg/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/auxkc-no-split-seg/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 8 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__REGION0" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__REGION1" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x9000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__REGION2" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xA000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__REGION3" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xB000" + assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0xC000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x9000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xC000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xA000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xB000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xC000" + + # Check the fixups + kernel_cache.analyze("/auxkc-no-split-seg/aux.kc", ["-fixups", "-arch", "x86_64"]) + assert kernel_cache.dictionary()["fixups"] == "" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x5008"] == "kc(3) + 0x9000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x5008"] == "kc(3) + 0xB000" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py new file mode 100644 index 0000000..2ee1618 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py @@ -0,0 +1,43 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that an auxKC references the UUID of the base KC +# And also the UUID of the pageable KC + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/main.kernel", "/auxkc-pageablekc-uuid/extensions", [], []) + kernel_cache.analyze("/auxkc-pageablekc-uuid/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the kernel UUID + kernel_cache.analyze("/auxkc-pageablekc-uuid/main.kc", ["-uuid", "-arch", "arm64"]) + kernelUUID = kernel_cache.dictionary()["uuid"] + assert kernelUUID != "00000000-0000-0000-0000-000000000000" + assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"] + + # Now build a pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-pageablekc-uuid/pageable.kc", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the pageable UUID + kernel_cache.analyze("/auxkc-pageablekc-uuid/pageable.kc", ["-uuid", "-arch", "arm64"]) + pageableUUID = kernel_cache.dictionary()["uuid"] + assert pageableUUID != "00000000-0000-0000-0000-000000000000" + assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID + + # Now build an aux cache using the baseline and pageable kernel collections + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-pageablekc-uuid/aux.kc", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/pageable.kc", "/auxkc-pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the aux UUID + kernel_cache.analyze("/auxkc-pageablekc-uuid/aux.kc", ["-uuid", "-arch", "arm64"]) + assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000" + assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID + assert kernel_cache.dictionary()["prelink-info-pageable-uuid"] == pageableUUID + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist new file mode 100644 index 0000000..c910db1 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN1X11KernelClass + + + SymbolPrefix + __ZTVN1X11KernelClass + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp new file mode 100644 index 0000000..ce2c54f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp @@ -0,0 +1,10 @@ + +#include "bar1.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Bar1, Foo2 ) + +int Bar1::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h new file mode 100644 index 0000000..f17541f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h @@ -0,0 +1,14 @@ + +#include "foo2.h" + +namespace X { + +class Bar1 : public X::Foo2 +{ + OSDeclareDefaultStructors( Bar1 ) + +public: + virtual int foo() override; +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp new file mode 100644 index 0000000..4aec80a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp @@ -0,0 +1,18 @@ + +#include "bar1.h" + +using namespace X; + +class Bar2 : public Bar1 +{ + OSDeclareDefaultStructors( Bar2 ) + +public: + virtual int foo() override; +}; + +OSDefineMetaClassAndStructors( Bar2, Bar1 ) + +int Bar2::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist new file mode 100644 index 0000000..473c118 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar1 + CFBundleIdentifier + com.apple.bar1 + CFBundleName + bar1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 new file mode 100755 index 0000000..5d41141 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist new file mode 100644 index 0000000..d766b97 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist @@ -0,0 +1,25 @@ + + + + + CFBundleExecutable + bar2 + CFBundleIdentifier + com.apple.bar2 + CFBundleName + bar2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.bar1 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 new file mode 100755 index 0000000..2efd434 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist new file mode 100644 index 0000000..80ea13d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo1 + CFBundleIdentifier + com.apple.foo1 + CFBundleName + foo1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 new file mode 100755 index 0000000..053b894 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist new file mode 100644 index 0000000..276b51c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo2 + CFBundleIdentifier + com.apple.foo2 + CFBundleName + foo2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + com.apple.foo1 + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 new file mode 100755 index 0000000..f1acca4 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h new file mode 100644 index 0000000..3ee59b5 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h @@ -0,0 +1,24 @@ + +#include "kernel.h" + +namespace X { + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo(); + +#ifdef FOO1_USED + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo1, 1); + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp new file mode 100644 index 0000000..e32f75a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp @@ -0,0 +1,26 @@ + +#include "foo1.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Foo1, KernelClass ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo1, 0) +// Index 1 has been replaced with a method +OSMetaClassDefineReservedUsed( Foo1, 1 ) + +OSMetaClassDefineReservedUnused( Foo1, 2 ) +OSMetaClassDefineReservedUnused( Foo1, 3 ) + +int Foo1::foo() { + return 0; +} + +int Foo1::foo1Used0() { + return 0; +} + +int Foo1::foo1Used1() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h new file mode 100644 index 0000000..906c4b0 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h @@ -0,0 +1,31 @@ + +#include "kernel.h" + +namespace X { + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo() override; + +#ifdef FOO1_USED0 + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + +#ifdef FOO1_USED1 + OSMetaClassDeclareReservedUsed(Foo1, 1); + virtual int foo1Used1(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 1); +#endif + + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp new file mode 100644 index 0000000..e41096f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp @@ -0,0 +1,14 @@ + +#include "foo2.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Foo2, Foo1 ) + +int Foo2::foo() { + return 0; +} + +int Foo2::foo1Used0() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h new file mode 100644 index 0000000..d6d98a2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h @@ -0,0 +1,16 @@ + +#include "foo1.h" + +namespace X { + +class Foo2 : public Foo1 +{ + OSDeclareDefaultStructors( Foo2 ) + +public: + virtual int foo() override; + + virtual int foo1Used0() override; +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp new file mode 100644 index 0000000..ee0c8c2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp @@ -0,0 +1,17 @@ + +#include "kernel.h" + +using namespace X; + +OSDefineMetaClassAndStructors( KernelClass, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(KernelClass, 0) + +int KernelClass::foo() { + return 0; +} + +int KernelClass::kernelClassUsed0() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h new file mode 100644 index 0000000..33e1a43 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h @@ -0,0 +1,22 @@ + +#include +#include + +namespace X { + +class KernelClass : public OSObject +{ + OSDeclareDefaultStructors( KernelClass ) + +public: + virtual int foo(); + +#ifdef KERNEL_USED + OSMetaClassDeclareReservedUsed(KernelClass, 0); + virtual int kernelClassUsed0(); +#else + OSMetaClassDeclareReservedUnused(KernelClass, 0); +#endif +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel new file mode 100755 index 0000000..dc41fde Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py new file mode 100644 index 0000000..6896775 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py @@ -0,0 +1,294 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This is the same as auxkc-pageablekc-vtable-patching-namespaces +# but this test has all local symbols instead of global symbols + + +# The kernel has class OSObject and subclass KernelClass +# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2 +# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2 + +# In KernelClass the vtable layout is: +# [ ..., foo() kernelClassUsed0() ] + +# In Foo1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + +# In Foo2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0() +# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1() + +def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def findLocalSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["local-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def findFixupVMAddr(kernel_cache, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def offsetVMAddr(vmAddr, offset): + het_int = int(vmAddr, 16) + het_int = het_int + offset + return ''.join([ '0x', hex(het_int).upper()[2:] ]) + +def check(kernel_cache): + enableLogging = False + kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel", "", [], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable + # KernelClass::foo() + kernelClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass3fooEv") + if enableLogging: + print "kernelClassFooVMAddr: " + kernelClassFooVMAddr + + # KernelClass::kernelClassUsed0() + kernelClassUsed0VMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass16kernelClassUsed0Ev") + if enableLogging: + print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64") + if enableLogging: + print "kernelFooFixupAddr: " + kernelFooFixupAddr + # Then the following fixup should be to KernelClass::kernelClassUsed0() + kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8) + if enableLogging: + print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr + assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64" + + # From this point on, the vmAddr for __ZN1X11KernelClass16kernelClassUsed0Ev is an offset in to kc(0) + # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB + kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000) + if enableLogging: + print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset + + # ----------------------------------------------------------- + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions", ["com.apple.foo1", "com.apple.foo2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo1, find the vtable and its override of foo() + # Foo1::foo() + pageableFoo1FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo13fooEv") + if enableLogging: + print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr + + pageableFoo1FooUsed0VMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used0Ev") + if enableLogging: + print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr + + pageableFoo1FooUsed1VMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used1Ev") + if enableLogging: + print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr + + # From foo2, find the vtable and its override of foo() + # Foo2::foo() + pageableFoo2FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo23fooEv") + if enableLogging: + print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr + # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass + pageableFoo2FooUsed0VMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo29foo1Used0Ev") + if enableLogging: + print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-fixups", "-arch", "x86_64"]) + kernel_cache.dictionary()["fixups"] == "none" + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup + pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr) + if enableLogging: + print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- foo2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr) + if enableLogging: + print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions", ["com.apple.bar1", "com.apple.bar2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar1, find the vtable and its override of foo() + # Bar1::foo() + auxBar1FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Bar13fooEv") + if enableLogging: + print "auxBar1FooVMAddr: " + auxBar1FooVMAddr + + # From bar2, find the vtable and its override of foo() + # Bar1::foo() + auxBar2FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv") + if enableLogging: + print "auxBar2FooVMAddr: " + auxBar2FooVMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup + auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr) + if enableLogging: + print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- bar2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr) + if enableLogging: + print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1 -Wl,-exported_symbol,__ZN1X11KernelClass10gMetaClassE -Wl,-exported_symbol,__ZN8OSObject10gMetaClassE -Wl,-exported_symbol,__ZNK11OSMetaClass19instanceConstructedEv +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -DFOO1_USED1=1 -Wl,-exported_symbol,__ZN1X4Foo110gMetaClassE -Wl,-exported_symbol,__ZN1X4Foo1C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Foo1E -Wl,-exported_symbol,__ZN1X4Foo1D2Ev +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -Wl,-exported_symbol,__ZN1X4Foo210gMetaClassE -Wl,-exported_symbol,__ZN1X4Foo2C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Foo2E -Wl,-exported_symbol,__ZN1X4Foo2D2Ev +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -Wl,-exported_symbol,__ZN1X4Bar110gMetaClassE -Wl,-exported_symbol,__ZN1X4Bar1C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Bar1E -Wl,-exported_symbol,__ZN1X4Bar1D2Ev +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist new file mode 100644 index 0000000..c910db1 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN1X11KernelClass + + + SymbolPrefix + __ZTVN1X11KernelClass + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp new file mode 100644 index 0000000..ce2c54f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp @@ -0,0 +1,10 @@ + +#include "bar1.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Bar1, Foo2 ) + +int Bar1::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h new file mode 100644 index 0000000..f17541f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h @@ -0,0 +1,14 @@ + +#include "foo2.h" + +namespace X { + +class Bar1 : public X::Foo2 +{ + OSDeclareDefaultStructors( Bar1 ) + +public: + virtual int foo() override; +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp new file mode 100644 index 0000000..4aec80a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp @@ -0,0 +1,18 @@ + +#include "bar1.h" + +using namespace X; + +class Bar2 : public Bar1 +{ + OSDeclareDefaultStructors( Bar2 ) + +public: + virtual int foo() override; +}; + +OSDefineMetaClassAndStructors( Bar2, Bar1 ) + +int Bar2::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist new file mode 100644 index 0000000..473c118 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar1 + CFBundleIdentifier + com.apple.bar1 + CFBundleName + bar1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 new file mode 100755 index 0000000..e6b8a8b Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist new file mode 100644 index 0000000..d766b97 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist @@ -0,0 +1,25 @@ + + + + + CFBundleExecutable + bar2 + CFBundleIdentifier + com.apple.bar2 + CFBundleName + bar2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.bar1 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 new file mode 100755 index 0000000..0341c74 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist new file mode 100644 index 0000000..80ea13d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo1 + CFBundleIdentifier + com.apple.foo1 + CFBundleName + foo1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 new file mode 100755 index 0000000..7caf637 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist new file mode 100644 index 0000000..276b51c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo2 + CFBundleIdentifier + com.apple.foo2 + CFBundleName + foo2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + com.apple.foo1 + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 new file mode 100755 index 0000000..0b0aecd Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h new file mode 100644 index 0000000..3ee59b5 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h @@ -0,0 +1,24 @@ + +#include "kernel.h" + +namespace X { + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo(); + +#ifdef FOO1_USED + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo1, 1); + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp new file mode 100644 index 0000000..e32f75a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp @@ -0,0 +1,26 @@ + +#include "foo1.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Foo1, KernelClass ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo1, 0) +// Index 1 has been replaced with a method +OSMetaClassDefineReservedUsed( Foo1, 1 ) + +OSMetaClassDefineReservedUnused( Foo1, 2 ) +OSMetaClassDefineReservedUnused( Foo1, 3 ) + +int Foo1::foo() { + return 0; +} + +int Foo1::foo1Used0() { + return 0; +} + +int Foo1::foo1Used1() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h new file mode 100644 index 0000000..906c4b0 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h @@ -0,0 +1,31 @@ + +#include "kernel.h" + +namespace X { + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo() override; + +#ifdef FOO1_USED0 + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + +#ifdef FOO1_USED1 + OSMetaClassDeclareReservedUsed(Foo1, 1); + virtual int foo1Used1(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 1); +#endif + + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp new file mode 100644 index 0000000..e41096f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp @@ -0,0 +1,14 @@ + +#include "foo2.h" + +using namespace X; + +OSDefineMetaClassAndStructors( Foo2, Foo1 ) + +int Foo2::foo() { + return 0; +} + +int Foo2::foo1Used0() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h new file mode 100644 index 0000000..d6d98a2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h @@ -0,0 +1,16 @@ + +#include "foo1.h" + +namespace X { + +class Foo2 : public Foo1 +{ + OSDeclareDefaultStructors( Foo2 ) + +public: + virtual int foo() override; + + virtual int foo1Used0() override; +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp new file mode 100644 index 0000000..ee0c8c2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp @@ -0,0 +1,17 @@ + +#include "kernel.h" + +using namespace X; + +OSDefineMetaClassAndStructors( KernelClass, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(KernelClass, 0) + +int KernelClass::foo() { + return 0; +} + +int KernelClass::kernelClassUsed0() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h new file mode 100644 index 0000000..33e1a43 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h @@ -0,0 +1,22 @@ + +#include +#include + +namespace X { + +class KernelClass : public OSObject +{ + OSDeclareDefaultStructors( KernelClass ) + +public: + virtual int foo(); + +#ifdef KERNEL_USED + OSMetaClassDeclareReservedUsed(KernelClass, 0); + virtual int kernelClassUsed0(); +#else + OSMetaClassDeclareReservedUnused(KernelClass, 0); +#endif +}; + +} // namespace X \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel new file mode 100755 index 0000000..b48d3f3 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py new file mode 100644 index 0000000..7b6ec1d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py @@ -0,0 +1,285 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +# The kernel has class OSObject and subclass KernelClass +# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2 +# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2 + +# In KernelClass the vtable layout is: +# [ ..., foo() kernelClassUsed0() ] + +# In Foo1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + +# In Foo2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0() +# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1() + +def findSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def findFixupVMAddr(kernel_cache, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def offsetVMAddr(vmAddr, offset): + het_int = int(vmAddr, 16) + het_int = het_int + offset + return ''.join([ '0x', hex(het_int).upper()[2:] ]) + +def check(kernel_cache): + enableLogging = False + kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kernel", "", [], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable + # KernelClass::foo() + kernelClassFooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass3fooEv") + if enableLogging: + print "kernelClassFooVMAddr: " + kernelClassFooVMAddr + + # KernelClass::kernelClassUsed0() + kernelClassUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass16kernelClassUsed0Ev") + if enableLogging: + print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64") + if enableLogging: + print "kernelFooFixupAddr: " + kernelFooFixupAddr + # Then the following fixup should be to KernelClass::kernelClassUsed0() + kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8) + if enableLogging: + print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr + assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64" + + # From this point on, the vmAddr for __ZN1X11KernelClass16kernelClassUsed0Ev is an offset in to kc(0) + # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB + kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000) + if enableLogging: + print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset + + # ----------------------------------------------------------- + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/extensions", ["com.apple.foo1", "com.apple.foo2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo1, find the vtable and its override of foo() + # Foo1::foo() + pageableFoo1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo13fooEv") + if enableLogging: + print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr + + pageableFoo1FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used0Ev") + if enableLogging: + print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr + + pageableFoo1FooUsed1VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used1Ev") + if enableLogging: + print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr + + # From foo2, find the vtable and its override of foo() + # Foo2::foo() + pageableFoo2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo23fooEv") + if enableLogging: + print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr + # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass + pageableFoo2FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo29foo1Used0Ev") + if enableLogging: + print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-fixups", "-arch", "x86_64"]) + kernel_cache.dictionary()["fixups"] == "none" + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup + pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr) + if enableLogging: + print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- foo2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr) + if enableLogging: + print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces/extensions", ["com.apple.bar1", "com.apple.bar2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar1, find the vtable and its override of foo() + # Bar1::foo() + auxBar1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Bar13fooEv") + if enableLogging: + print "auxBar1FooVMAddr: " + auxBar1FooVMAddr + + # From bar2, find the vtable and its override of foo() + # Bar1::foo() + auxBar2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv") + if enableLogging: + print "auxBar2FooVMAddr: " + auxBar2FooVMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup + auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr) + if enableLogging: + print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- bar2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr) + if enableLogging: + print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -DFOO1_USED1=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist new file mode 100644 index 0000000..ff59c2e --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN11KernelClass + + + SymbolPrefix + __ZTV11KernelClass + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp new file mode 100644 index 0000000..3db027a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp @@ -0,0 +1,8 @@ + +#include "bar1.h" + +OSDefineMetaClassAndStructors( Bar1, Foo2 ) + +int Bar1::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h new file mode 100644 index 0000000..41dfaf0 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h @@ -0,0 +1,10 @@ + +#include "foo2.h" + +class Bar1 : public Foo2 +{ + OSDeclareDefaultStructors( Bar1 ) + +public: + virtual int foo() override; +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp new file mode 100644 index 0000000..1fc3b48 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp @@ -0,0 +1,16 @@ + +#include "bar1.h" + +class Bar2 : public Bar1 +{ + OSDeclareDefaultStructors( Bar2 ) + +public: + virtual int foo() override; +}; + +OSDefineMetaClassAndStructors( Bar2, Bar1 ) + +int Bar2::foo() { + return 1; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist new file mode 100644 index 0000000..473c118 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar1 + CFBundleIdentifier + com.apple.bar1 + CFBundleName + bar1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 new file mode 100755 index 0000000..780b793 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist new file mode 100644 index 0000000..d766b97 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist @@ -0,0 +1,25 @@ + + + + + CFBundleExecutable + bar2 + CFBundleIdentifier + com.apple.bar2 + CFBundleName + bar2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo1 + 1.0 + com.apple.foo2 + 1.0 + com.apple.bar1 + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 new file mode 100755 index 0000000..bb78e82 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist new file mode 100644 index 0000000..80ea13d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo1 + CFBundleIdentifier + com.apple.foo1 + CFBundleName + foo1 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 new file mode 100755 index 0000000..bf4819f Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist new file mode 100644 index 0000000..276b51c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo2 + CFBundleIdentifier + com.apple.foo2 + CFBundleName + foo2 + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + com.apple.foo1 + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 new file mode 100755 index 0000000..92926ea Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h new file mode 100644 index 0000000..606653f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h @@ -0,0 +1,20 @@ + +#include "kernel.h" + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo(); + +#ifdef FOO1_USED + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo1, 1); + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp new file mode 100644 index 0000000..a1de799 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp @@ -0,0 +1,24 @@ + +#include "foo1.h" + +OSDefineMetaClassAndStructors( Foo1, KernelClass ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo1, 0) +// Index 1 has been replaced with a method +OSMetaClassDefineReservedUsed( Foo1, 1 ) + +OSMetaClassDefineReservedUnused( Foo1, 2 ) +OSMetaClassDefineReservedUnused( Foo1, 3 ) + +int Foo1::foo() { + return 0; +} + +int Foo1::foo1Used0() { + return 0; +} + +int Foo1::foo1Used1() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h new file mode 100644 index 0000000..76b479c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h @@ -0,0 +1,27 @@ + +#include "kernel.h" + +class Foo1 : public KernelClass +{ + OSDeclareDefaultStructors( Foo1 ) + +public: + virtual int foo() override; + +#ifdef FOO1_USED0 + OSMetaClassDeclareReservedUsed(Foo1, 0); + virtual int foo1Used0(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 0); +#endif + +#ifdef FOO1_USED1 + OSMetaClassDeclareReservedUsed(Foo1, 1); + virtual int foo1Used1(); +#else + OSMetaClassDeclareReservedUnused(Foo1, 1); +#endif + + OSMetaClassDeclareReservedUnused(Foo1, 2); + OSMetaClassDeclareReservedUnused(Foo1, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp new file mode 100644 index 0000000..e763b7e --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp @@ -0,0 +1,12 @@ + +#include "foo2.h" + +OSDefineMetaClassAndStructors( Foo2, Foo1 ) + +int Foo2::foo() { + return 0; +} + +int Foo2::foo1Used0() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h new file mode 100644 index 0000000..410a21c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h @@ -0,0 +1,12 @@ + +#include "foo1.h" + +class Foo2 : public Foo1 +{ + OSDeclareDefaultStructors( Foo2 ) + +public: + virtual int foo() override; + + virtual int foo1Used0() override; +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp new file mode 100644 index 0000000..a577f37 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp @@ -0,0 +1,15 @@ + +#include "kernel.h" + +OSDefineMetaClassAndStructors( KernelClass, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(KernelClass, 0) + +int KernelClass::foo() { + return 0; +} + +int KernelClass::kernelClassUsed0() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h new file mode 100644 index 0000000..9edf15c --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h @@ -0,0 +1,18 @@ + +#include +#include + +class KernelClass : public OSObject +{ + OSDeclareDefaultStructors( KernelClass ) + +public: + virtual int foo(); + +#ifdef KERNEL_USED + OSMetaClassDeclareReservedUsed(KernelClass, 0); + virtual int kernelClassUsed0(); +#else + OSMetaClassDeclareReservedUnused(KernelClass, 0); +#endif +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel new file mode 100755 index 0000000..a937770 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py new file mode 100644 index 0000000..afd49fe --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py @@ -0,0 +1,286 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +# The kernel has class OSObject and subclass KernelClass +# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2 +# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2 + +# In KernelClass the vtable layout is: +# [ ..., foo() kernelClassUsed0() ] + +# In Foo1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + +# In Foo2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar1, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# In Bar2, the layout is: +# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ] + +# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0() +# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1() + +def findSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def findFixupVMAddr(kernel_cache, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def offsetVMAddr(vmAddr, offset): + het_int = int(vmAddr, 16) + het_int = het_int + offset + return ''.join([ '0x', hex(het_int).upper()[2:] ]) + +def check(kernel_cache): + enableLogging = False + kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/main.kernel", "", [], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable + # KernelClass::foo() + kernelClassFooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN11KernelClass3fooEv") + if enableLogging: + print "kernelClassFooVMAddr: " + kernelClassFooVMAddr + + # KernelClass::kernelClassUsed0() + kernelClassUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN11KernelClass16kernelClassUsed0Ev") + if enableLogging: + print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64") + if enableLogging: + print "kernelFooFixupAddr: " + kernelFooFixupAddr + # Then the following fixup should be to KernelClass::kernelClassUsed0() + kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8) + if enableLogging: + print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr + assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64" + + # From this point on, the vmAddr for __ZN11KernelClass16kernelClassUsed0Ev is an offset in to kc(0) + # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB + kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000) + if enableLogging: + print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset + + # ----------------------------------------------------------- + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/pageable.kc", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/extensions", ["com.apple.foo1", "com.apple.foo2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo1, find the vtable and its override of foo() + # Foo1::foo() + pageableFoo1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo13fooEv") + if enableLogging: + print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr + + pageableFoo1FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo19foo1Used0Ev") + if enableLogging: + print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr + + pageableFoo1FooUsed1VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo19foo1Used1Ev") + if enableLogging: + print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr + + # From foo2, find the vtable and its override of foo() + # Foo2::foo() + pageableFoo2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Foo23fooEv") + if enableLogging: + print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr + # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass + pageableFoo2FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Foo29foo1Used0Ev") + if enableLogging: + print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-fixups", "-arch", "x86_64"]) + kernel_cache.dictionary()["fixups"] == "none" + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup + pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr) + if enableLogging: + print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24) + if enableLogging: + print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- foo2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr) + if enableLogging: + print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24) + if enableLogging: + print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/aux.kc", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/pageable.kc", "/auxkc-pageablekc-vtable-patching/extensions", ["com.apple.bar1", "com.apple.bar2"], []) + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar1, find the vtable and its override of foo() + # Bar1::foo() + auxBar1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Bar13fooEv") + if enableLogging: + print "auxBar1FooVMAddr: " + auxBar1FooVMAddr + + # From bar2, find the vtable and its override of foo() + # Bar1::foo() + auxBar2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv") + if enableLogging: + print "auxBar2FooVMAddr: " + auxBar2FooVMAddr + + + # Check the fixups + kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # --- foo1.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup + auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr) + if enableLogging: + print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24) + if enableLogging: + print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + + # --- bar2.kext --- + # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ] + # and we want [ ..., foo(), kernelClassUsed0(), foo1Used0(), foo1Used1() ] + + # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup + auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr) + if enableLogging: + print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr + + # Then the following fixup should be to KernelClass::kernelClassUsed0() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset + + # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1 + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr + + # And then foo1Used1() + auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24) + if enableLogging: + print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr + assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -DFOO1_USED1=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-uuid/bar.c b/testing/kernel-cache-tests/auxkc-uuid/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-uuid/foo.c b/testing/kernel-cache-tests/auxkc-uuid/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/auxkc-uuid/main.c b/testing/kernel-cache-tests/auxkc-uuid/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-uuid/main.kernel b/testing/kernel-cache-tests/auxkc-uuid/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-uuid/test.py b/testing/kernel-cache-tests/auxkc-uuid/test.py new file mode 100644 index 0000000..7aad4e7 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-uuid/test.py @@ -0,0 +1,32 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that an auxKC references the UUID of the base KC + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/auxkc-uuid/main.kc", "/auxkc-uuid/main.kernel", "/auxkc-uuid/extensions", [], []) + kernel_cache.analyze("/auxkc-uuid/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the kernel UUID + kernel_cache.analyze("/auxkc-uuid/main.kc", ["-uuid", "-arch", "arm64"]) + kernelUUID = kernel_cache.dictionary()["uuid"] + assert kernelUUID != "00000000-0000-0000-0000-000000000000" + assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"] + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-uuid/aux.kc", "/auxkc-uuid/main.kc", "", "/auxkc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the aux UUID + kernel_cache.analyze("/auxkc-uuid/aux.kc", ["-uuid", "-arch", "arm64"]) + assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000" + assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist new file mode 100644 index 0000000..73a1da6 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZTV11OSMetaClass + + + SymbolPrefix + __ZTV15OSMetaClassBase + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo new file mode 100755 index 0000000..f0d6164 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp new file mode 100644 index 0000000..a292275 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp @@ -0,0 +1,13 @@ + +#include "foo.h" +#include + +void* operator new(size_t size) { return (void*)1; } +void operator delete(void*) { } + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +OSMetaClassDefineReservedUnused( Foo, 0 ) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h new file mode 100644 index 0000000..662ad0e --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h @@ -0,0 +1,16 @@ + +#include "osobject.h" + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + // Override from OSMetaClassBase to let us find this slot in the vtable + virtual void placeholder() { } + + OSMetaClassDeclareReservedUnused(Foo, 0); + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp new file mode 100644 index 0000000..168e130 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp @@ -0,0 +1,44 @@ + +#include "metaclass.h" +#include "osobject.h" + +int __cxa_pure_virtual = 0; +void operator delete(void*) { } + +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::placeholder() { } +//void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +//void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +//void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +//void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +int OSMetaClassBase::metaclassBaseUsed4() { return 0; } +int OSMetaClassBase::metaclassBaseUsed5() { return 0; } +int OSMetaClassBase::metaclassBaseUsed6() { return 0; } +int OSMetaClassBase::metaclassBaseUsed7() { return 0; } + +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +OSObject::OSObject(const OSMetaClass *) { } +OSObject::~OSObject() { } + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +OSObject::MetaClass::MetaClass() { } +OSObject* OSObject::MetaClass::alloc() const { return NULL; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel new file mode 100755 index 0000000..6653523 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h new file mode 100644 index 0000000..fb140f8 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h @@ -0,0 +1,116 @@ + +#ifndef METACLASS_H +#define METACLASS_H + +#define NULL 0 +#define APPLE_KEXT_OVERRIDE + +#define OSDeclareCommonStructors(className, dispatch) \ + private: \ + static const OSMetaClass * const superClass; \ + public: \ + static const OSMetaClass * const metaClass; \ + static class MetaClass : public OSMetaClass { \ + public: \ + MetaClass(); \ + virtual OSObject *alloc() const APPLE_KEXT_OVERRIDE;\ + } gMetaClass; \ + friend class className ::MetaClass; \ + protected: \ + className (const OSMetaClass *); \ + virtual ~ className () APPLE_KEXT_OVERRIDE; + +#define _OSDeclareAbstractStructors(className, dispatch) \ + OSDeclareCommonStructors(className, dispatch); \ + private: \ + className (void); /* Make primary constructor private in abstract */ \ + protected: + +#define OSDeclareAbstractStructorsWithDispatch(className) \ +_OSDeclareAbstractStructors(className, dispatch) + +#define OSMetaClassDefineReservedUsed(className, index) +#define OSMetaClassDefineReservedUnused(className, index) \ +void className ::_RESERVED ## className ## index () { } + +#define OSMetaClassDeclareReservedUsed(className, index) +#define OSMetaClassDeclareReservedUnused(className, index) \ + private: \ + virtual void _RESERVED ## className ## index () + +#define _OSDeclareDefaultStructors(className, dispatch) \ + OSDeclareCommonStructors(className, dispatch); \ + public: \ + className (void); \ + protected: + +#define OSDeclareDefaultStructors(className) \ +_OSDeclareDefaultStructors(className, ) + +#define OSDefineMetaClassWithInit(className, superclassName, init) \ + /* Class global data */ \ + className ::MetaClass className ::gMetaClass; \ + const OSMetaClass * const className ::metaClass = \ + & className ::gMetaClass; \ + const OSMetaClass * const className ::superClass = \ + & superclassName ::gMetaClass; \ + /* Class member functions */ \ + className :: className(const OSMetaClass *meta) \ + : superclassName (meta) { } \ + className ::~ className() { } \ + /* The ::MetaClass constructor */ \ + className ::MetaClass::MetaClass() { init; } + +#define OSDefineDefaultStructors(className, superclassName) \ + OSObject * className ::MetaClass::alloc() const { return new className; } \ + className :: className () : superclassName (&gMetaClass) { } + +#define OSDefineMetaClassAndStructorsWithInit(className, superclassName, init) \ + OSDefineMetaClassWithInit(className, superclassName, init) \ + OSDefineDefaultStructors(className, superclassName) + +#define OSDefineMetaClassAndStructors(className, superclassName) \ + OSDefineMetaClassAndStructorsWithInit(className, superclassName, ) + + +class OSMetaClassBase +{ +public: + virtual ~OSMetaClassBase(); + + // Add a placeholder we can find later + virtual void placeholder(); + + // Virtual Padding +#ifdef METACLASS_BASE_USED + OSMetaClassDeclareReservedUsed(OSMetaClassBase, 4); + virtual int metaclassBaseUsed4(); + OSMetaClassDeclareReservedUsed(OSMetaClassBase, 5); + virtual int metaclassBaseUsed5(); + OSMetaClassDeclareReservedUsed(OSMetaClassBase, 6); + virtual int metaclassBaseUsed6(); + OSMetaClassDeclareReservedUsed(OSMetaClassBase, 7); + virtual int metaclassBaseUsed7(); +#else + virtual void _RESERVEDOSMetaClassBase4(); + virtual void _RESERVEDOSMetaClassBase5(); + virtual void _RESERVEDOSMetaClassBase6(); + virtual void _RESERVEDOSMetaClassBase7(); +#endif +}; + +class OSMetaClass : public OSMetaClassBase { +public: + virtual ~OSMetaClass(); + // Virtual Padding functions for MetaClass's + OSMetaClassDeclareReservedUnused(OSMetaClass, 0); + OSMetaClassDeclareReservedUnused(OSMetaClass, 1); + OSMetaClassDeclareReservedUnused(OSMetaClass, 2); + OSMetaClassDeclareReservedUnused(OSMetaClass, 3); + OSMetaClassDeclareReservedUnused(OSMetaClass, 4); + OSMetaClassDeclareReservedUnused(OSMetaClass, 5); + OSMetaClassDeclareReservedUnused(OSMetaClass, 6); + OSMetaClassDeclareReservedUnused(OSMetaClass, 7); +}; + +#endif // METACLASS_H diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h new file mode 100644 index 0000000..187ca21 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h @@ -0,0 +1,8 @@ + + +#include "metaclass.h" + +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructorsWithDispatch(OSObject); +}; diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py new file mode 100644 index 0000000..a628667 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py @@ -0,0 +1,81 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# OSMetaClass in the kernel has a vtable which has to be patched in to Foo::MetaClass + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-metaclass-patching/main.kc", "/auxkc-vtable-metaclass-patching/main.kernel", None, [], []) + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From OSMetaClass, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][11]["name"] == "__ZN15OSMetaClassBase11placeholderEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][11]["vmAddr"] == "0x14BE0" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][12]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed4Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][12]["vmAddr"] == "0x14BF0" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][13]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed5Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][13]["vmAddr"] == "0x14C10" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][14]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed6Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][14]["vmAddr"] == "0x14C30" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][15]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed7Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][15]["vmAddr"] == "0x14C50" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for OSMetaClass, we match the entry for OSMetaClass::placeholder() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x14168"] == "kc(0) + 0x14BE0 : pointer64" + # Then the following fixup should be to OSMetaClass::metaclassBaseUsed4() + assert kernel_cache.dictionary()["fixups"]["0x14170"] == "kc(0) + 0x14BF0 : pointer64" + # Then OSMetaClass::metaclassBaseUsed5() + assert kernel_cache.dictionary()["fixups"]["0x14178"] == "kc(0) + 0x14C10 : pointer64" + # Then OSMetaClass::metaclassBaseUsed6() + assert kernel_cache.dictionary()["fixups"]["0x14180"] == "kc(0) + 0x14C30 : pointer64" + # Then OSMetaClass::metaclassBaseUsed7() + assert kernel_cache.dictionary()["fixups"]["0x14188"] == "kc(0) + 0x14C50 : pointer64" + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-metaclass-patching/aux.kc", "/auxkc-vtable-metaclass-patching/main.kc", "", "/auxkc-vtable-metaclass-patching/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo.kext, find the vtable and its override of placeholder() + # Foo::placeholder() + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][4]["name"] == "__ZN3Foo11placeholderEv" + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][4]["vmAddr"] == "0xCF90" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # Now in foo.kext, match the entry for its Foo::placeholder() symbol + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x50"] == "kc(3) + 0xCF90" + # And if the patching was correct, then following entry should be to OSMetaClass::metaclassBaseUsed4() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x58"] == "kc(0) + 0x10BF0" + # Then OSMetaClass::metaclassBaseUsed5() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x60"] == "kc(0) + 0x10C10" + # Then OSMetaClass::metaclassBaseUsed6() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x68"] == "kc(0) + 0x10C30" + # Then OSMetaClass::metaclassBaseUsed7() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x70"] == "kc(0) + 0x10C50" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DMETACLASS_BASE_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar new file mode 100755 index 0000000..ec61097 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo new file mode 100755 index 0000000..30c7f3d Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp new file mode 100644 index 0000000..13f66ef --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp @@ -0,0 +1,29 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Redefine this just so that we can write tests +#undef OSMetaClassDefineReservedUnused +#define OSMetaClassDefineReservedUnused(className, index) \ +void className ::_RESERVED ## className ## index () \ + { gMetaClass.reservedCalled(index); } + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h new file mode 100644 index 0000000..401eeb4 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h @@ -0,0 +1,27 @@ + +#include +#include + +// Redefine this just so that we can write tests +#undef OSMetaClassDeclareReservedUnused +#define OSMetaClassDeclareReservedUnused(className, index) \ + private: \ + virtual void _RESERVED ## className ## index () + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp new file mode 100644 index 0000000..5dd800d --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp @@ -0,0 +1,108 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +// Thee are none of these for arm64e + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__TEXT_EXEC, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel new file mode 100755 index 0000000..0fb7e40 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py new file mode 100644 index 0000000..cf3c913 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py @@ -0,0 +1,70 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it +# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64e", "/auxkc-vtable-patching-arm64e/main.kc", "/auxkc-vtable-patching-arm64e/main.kernel", "/auxkc-vtable-patching-arm64e/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x20408" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x20420" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x200F0"] == "kc(0) + 0x20408 auth(IA addr 49764)" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x200F8"] == "kc(0) + 0x20420 auth(IA addr 61962)" + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64e", "/auxkc-vtable-patching-arm64e/aux.kc", "/auxkc-vtable-patching-arm64e/main.kc", "", "/auxkc-vtable-patching-arm64e/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-symbols", "-arch", "arm64e"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x1036C" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-fixups", "-arch", "arm64e"]) + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x140E8"] == "kc(3) + 0x1036C auth(IA addr 49764)" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x140F0"] == "kc(0) + 0x10420 auth(IA addr 61962)" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -Wl,-fixup_chains -Wl,-kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar new file mode 100755 index 0000000..b3bd60b Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo new file mode 100755 index 0000000..5fe8196 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py new file mode 100644 index 0000000..dd0a8ba --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py @@ -0,0 +1,73 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Note, this is the same as auxkc-vtable-patching but without __DATA_CONST. This tests that we get the correct offsets +# even when the vtables are in __DATA which will be mapped lower than the auxKC mach_header + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it +# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-patching-no-data-const/main.kc", "/auxkc-vtable-patching-no-data-const/main.kernel", "/auxkc-vtable-patching-no-data-const/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0" + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-patching-no-data-const/aux.kc", "/auxkc-vtable-patching-no-data-const/main.kc", "", "/auxkc-vtable-patching-no-data-const/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xCF10" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x150"] == "kc(3) + 0xCF10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x158"] == "kc(0) + 0x12EF0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-no_data_const +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar new file mode 100755 index 0000000..0bdcc52 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo new file mode 100755 index 0000000..5fe8196 Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching/test.py new file mode 100644 index 0000000..d09f9fc --- /dev/null +++ b/testing/kernel-cache-tests/auxkc-vtable-patching/test.py @@ -0,0 +1,70 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it +# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-patching/main.kc", "/auxkc-vtable-patching/main.kernel", "/auxkc-vtable-patching/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0" + + + # ----------------------------------------------------------- + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-patching/aux.kc", "/auxkc-vtable-patching/main.kc", "", "/auxkc-vtable-patching/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xCF10" + + + # Check the fixups + kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-fixups", "-arch", "x86_64"]) + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x10150"] == "kc(3) + 0xCF10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x10158"] == "kc(0) + 0x12EF0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/bundle-ids/bar.c b/testing/kernel-cache-tests/bundle-ids/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..77b3de8 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar new file mode 100755 index 0000000..4467cee Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo new file mode 100755 index 0000000..1e2e3c3 Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/bundle-ids/foo.c b/testing/kernel-cache-tests/bundle-ids/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/bundle-ids/main.c b/testing/kernel-cache-tests/bundle-ids/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/bundle-ids/main.kernel b/testing/kernel-cache-tests/bundle-ids/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/main.kernel differ diff --git a/testing/kernel-cache-tests/bundle-ids/test.py b/testing/kernel-cache-tests/bundle-ids/test.py new file mode 100644 index 0000000..cdaf2e3 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-ids/test.py @@ -0,0 +1,77 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that we are able to build a kernel collection with a kernel and kext's referenced as bundle id's + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/bundle-ids/main.kc", "/bundle-ids/main.kernel", "/bundle-ids/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/bundle-ids/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x1C000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"].endswith("com.apple.kernel") + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x20000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"].endswith("bar") + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x20000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"].endswith("foo") + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x14030" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x1C010" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0x20000" + + # Check the fixups + kernel_cache.analyze("/bundle-ids/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14000" + assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"].endswith("com.apple.kernel") + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"].endswith("bar") + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"].endswith("foo") + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/bar.c b/testing/kernel-cache-tests/bundle-libraries-arch/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..49493c1 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + OSBundleLibraries_arm64 + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/foo.c b/testing/kernel-cache-tests/bundle-libraries-arch/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/main.c b/testing/kernel-cache-tests/bundle-libraries-arch/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel b/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel differ diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/test.py b/testing/kernel-cache-tests/bundle-libraries-arch/test.py new file mode 100644 index 0000000..381eb20 --- /dev/null +++ b/testing/kernel-cache-tests/bundle-libraries-arch/test.py @@ -0,0 +1,34 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that OSBundleLibraries in bar's Info.plust appends the arch to find foo + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/bundle-libraries-arch/main.kc", "/bundle-libraries-arch/main.kernel", "/bundle-libraries-arch/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/bundle-libraries-arch/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Check the fixups + kernel_cache.analyze("/bundle-libraries-arch/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x14020" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..fdef95d --- /dev/null +++ b/testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist @@ -0,0 +1,12 @@ + + + + + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/codeless-kexts/main.cpp b/testing/kernel-cache-tests/codeless-kexts/main.cpp new file mode 100644 index 0000000..fbbaa3d --- /dev/null +++ b/testing/kernel-cache-tests/codeless-kexts/main.cpp @@ -0,0 +1,5 @@ + + +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/codeless-kexts/main.kernel b/testing/kernel-cache-tests/codeless-kexts/main.kernel new file mode 100755 index 0000000..0426d15 Binary files /dev/null and b/testing/kernel-cache-tests/codeless-kexts/main.kernel differ diff --git a/testing/kernel-cache-tests/codeless-kexts/test.py b/testing/kernel-cache-tests/codeless-kexts/test.py new file mode 100644 index 0000000..2702e51 --- /dev/null +++ b/testing/kernel-cache-tests/codeless-kexts/test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Code-less kext's have a plist, but no mach-o. We still need to put them in the prelink info + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/codeless-kexts/main.kc", "/codeless-kexts/main.kernel", "/codeless-kexts/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/codeless-kexts/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 5 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/ctf-arm64e/bar.c b/testing/kernel-cache-tests/ctf-arm64e/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/ctf-arm64e/ctf.txt b/testing/kernel-cache-tests/ctf-arm64e/ctf.txt new file mode 100644 index 0000000..1ed771b --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/ctf.txt @@ -0,0 +1,2048 @@ +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar new file mode 100755 index 0000000..5d5b0e9 Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo new file mode 100755 index 0000000..e9599f1 Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/ctf-arm64e/foo.c b/testing/kernel-cache-tests/ctf-arm64e/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/ctf-arm64e/main.cpp b/testing/kernel-cache-tests/ctf-arm64e/main.cpp new file mode 100644 index 0000000..59f93f9 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/main.cpp @@ -0,0 +1,37 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + int k; + int *p2; // aligned to 4 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +// We want a section in __TEXT so that ctf_insert works +__attribute__((section(("__TEXT,__const")))) +int x = 1; + +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr() + x; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/ctf-arm64e/main.kernel b/testing/kernel-cache-tests/ctf-arm64e/main.kernel new file mode 100755 index 0000000..78df4e1 Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/main.kernel differ diff --git a/testing/kernel-cache-tests/ctf-arm64e/test.py b/testing/kernel-cache-tests/ctf-arm64e/test.py new file mode 100644 index 0000000..60e6822 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-arm64e/test.py @@ -0,0 +1,99 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This is the fixups-arm64e test, but with __CTF inserted so that we can see that CTF doesn't impact the result +# Note the ctf.txt file is 16k just to ensure that if its vm addr wasn't updated, we'd exceed the size of __LINKEDIT with the __CTF + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64e", "/ctf-arm64e/main.kc", "/ctf-arm64e/main.kernel", "/ctf-arm64e/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/ctf-arm64e/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007010000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF00701C000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007020000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF007030000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007010000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007020000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF007014000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__CTF" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmSize"] == "0x0" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmSize"] == "0x0" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0xFFFFFFF007030000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFFF007018000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFFF00700C000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFFF007018040" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C010" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000" + + # Check the fixups + kernel_cache.analyze("/ctf-arm64e/main.kc", ["-fixups", "-arch", "arm64e"]) + assert len(kernel_cache.dictionary()["fixups"]) == 11 + # main.kernel: S s = { &func, &func, &g, &func, &g }; + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x1C008"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0xFFFFFFF00702802C" + assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x20008"] == "kc(0) + 0xFFFFFFF00702802C" + # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + assert kernel_cache.dictionary()["fixups"]["0x24004"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x2400C"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x24018"] == "kc(0) + 0xFFFFFFF00702802C" + assert kernel_cache.dictionary()["fixups"]["0x24024"] == "kc(0) + 0xFFFFFFF00702802C" + # bar.kext: __typeof(&bar) barPtr = &bar; + assert kernel_cache.dictionary()["fixups"]["0x28000"] == "kc(0) + 0xFFFFFFF007018000 auth(IA !addr 0)" + # foo.kext: int* gPtr = &g; + assert kernel_cache.dictionary()["fixups"]["0x28010"] == "kc(0) + 0xFFFFFFF00702C018" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal ctf_insert main.kernel -arch arm64e ctf.txt -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/ctf-x86_64/bar.c b/testing/kernel-cache-tests/ctf-x86_64/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/ctf-x86_64/ctf.txt b/testing/kernel-cache-tests/ctf-x86_64/ctf.txt new file mode 100644 index 0000000..1ed771b --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/ctf.txt @@ -0,0 +1,2048 @@ +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata +CTFdata diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar new file mode 100755 index 0000000..bb30537 Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo new file mode 100755 index 0000000..6c4d15b Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/ctf-x86_64/foo.c b/testing/kernel-cache-tests/ctf-x86_64/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/ctf-x86_64/main.cpp b/testing/kernel-cache-tests/ctf-x86_64/main.cpp new file mode 100644 index 0000000..b3811a2 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/main.cpp @@ -0,0 +1,36 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + char k; + int *p2; // aligned to 1 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret"); + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/ctf-x86_64/main.kernel b/testing/kernel-cache-tests/ctf-x86_64/main.kernel new file mode 100755 index 0000000..e7ee13e Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/main.kernel differ diff --git a/testing/kernel-cache-tests/ctf-x86_64/test.py b/testing/kernel-cache-tests/ctf-x86_64/test.py new file mode 100644 index 0000000..b9ac0f2 --- /dev/null +++ b/testing/kernel-cache-tests/ctf-x86_64/test.py @@ -0,0 +1,102 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This is the fixups-x86_64 test, but with __CTF inserted so that we can see that CTF doesn't impact the result +# Note the ctf.txt file is 16k just to ensure that if its vm addr wasn't updated, we'd exceed the size of __LINKEDIT with the __CTF + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/ctf-x86_64/main.kc", "/ctf-x86_64/main.kernel", "/ctf-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/ctf-x86_64/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 7 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__CTF" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmSize"] == "0x0" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmSize"] == "0x0" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0xFFFFFF8000218000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000205000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000215000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF8000206000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF8000215010" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000" + + # Check the fixups + kernel_cache.analyze("/ctf-x86_64/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 12 + # main.kernel: S s = { &func, &func, &g, &func, &g }; + # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # bar.kext: __typeof(&bar) barPtr = &bar; + # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x115000"] == "kc(0) + 0xFFFFFF8000205FE0" + # foo.kext: int* gPtr = &g; + # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x115010"] == "kc(0) + 0xFFFFFF8000215018" + # main.kernel: movl _foo, %esp + # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal ctf_insert main.kernel -arch x86_64 ctf.txt -o main.kernel +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/data-const/bar.c b/testing/kernel-cache-tests/data-const/bar.c new file mode 100644 index 0000000..3f90848 --- /dev/null +++ b/testing/kernel-cache-tests/data-const/bar.c @@ -0,0 +1,8 @@ + +int pack = 0; + +extern int foo(); + +int bar() { + return foo() + pack; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar new file mode 100755 index 0000000..5d8a757 Binary files /dev/null and b/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo new file mode 100755 index 0000000..af1710e Binary files /dev/null and b/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/data-const/foo.c b/testing/kernel-cache-tests/data-const/foo.c new file mode 100644 index 0000000..acd9563 --- /dev/null +++ b/testing/kernel-cache-tests/data-const/foo.c @@ -0,0 +1,6 @@ + +int pack; + +int foo() { + return pack; +} diff --git a/testing/kernel-cache-tests/data-const/main.c b/testing/kernel-cache-tests/data-const/main.c new file mode 100644 index 0000000..5bdda59 --- /dev/null +++ b/testing/kernel-cache-tests/data-const/main.c @@ -0,0 +1,7 @@ + +int x = 0; +int* g = &x; + +int _start() { + return *g; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/data-const/main.kernel b/testing/kernel-cache-tests/data-const/main.kernel new file mode 100755 index 0000000..d43c633 Binary files /dev/null and b/testing/kernel-cache-tests/data-const/main.kernel differ diff --git a/testing/kernel-cache-tests/data-const/test.py b/testing/kernel-cache-tests/data-const/test.py new file mode 100644 index 0000000..6d0bb30 --- /dev/null +++ b/testing/kernel-cache-tests/data-const/test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# __DATA_CONST is r/o in xnu and r/w in the kext's but should be r/o in the final image + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/data-const/main.kc", "/data-const/main.kernel", "/data-const/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/data-const/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 7 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][0]["vmSize"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][1]["vmSize"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][2]["vmSize"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x20000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x24000" + assert kernel_cache.dictionary()["cache-segments"][5]["vmSize"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0x2C000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x24000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0x2C000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 5 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x28000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x1C000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][4]["vmAddr"] == "0x2C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x14030" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x28004" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0x2C000" + + # Check we have the correct addresses of the symbols being bound to + kernel_cache.analyze("/data-const/main.kc", ["-symbols", "-arch", "arm64"]) + # int g = &x; + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000" + # foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030" + + kernel_cache.analyze("/data-const/main.kc", ["-fixups", "-arch", "arm64"]) + # int g = &x; + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000" + # foo() + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__DATA_CONST,__data -Wl,-segprot,__DATA_CONST,r--,r-- main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/empty-bundle-dir/bar.c b/testing/kernel-cache-tests/empty-bundle-dir/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist new file mode 100644 index 0000000..82cd176 --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + empty + CFBundleIdentifier + com.apple.empty + CFBundleName + empty + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist new file mode 100644 index 0000000..77b3de8 --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar new file mode 100755 index 0000000..4467cee Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo new file mode 100755 index 0000000..1e2e3c3 Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/empty-bundle-dir/foo.c b/testing/kernel-cache-tests/empty-bundle-dir/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/empty-bundle-dir/main.c b/testing/kernel-cache-tests/empty-bundle-dir/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/empty-bundle-dir/main.kernel b/testing/kernel-cache-tests/empty-bundle-dir/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/main.kernel differ diff --git a/testing/kernel-cache-tests/empty-bundle-dir/test.py b/testing/kernel-cache-tests/empty-bundle-dir/test.py new file mode 100644 index 0000000..c20c570 --- /dev/null +++ b/testing/kernel-cache-tests/empty-bundle-dir/test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Make sure empty kext dirs don't crash + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/empty-bundle-dir/main.kc", "/empty-bundle-dir/main.kernel", "/empty-bundle-dir/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/empty-bundle-dir/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/empty.kext/PlugIns/bar.kext/bar +# [~]> rm -r extensions/foo.kext/*.ld +# [~]> rm -r extensions/empty.kext/PlugIns/bar.kext/*.ld + diff --git a/testing/kernel-cache-tests/extra-prelink-info/main.c b/testing/kernel-cache-tests/extra-prelink-info/main.c new file mode 100644 index 0000000..f545f5b --- /dev/null +++ b/testing/kernel-cache-tests/extra-prelink-info/main.c @@ -0,0 +1,13 @@ + +// Add a large buffer so that we know we have a bunch of stuff in __DATA +// and can more easily see that all the segments have moved around correctly, not just +// got lucky that all are the same size +__attribute__((used)) +char buffer[1024 * 16]; + +int f = 0; +int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] }; + +int _start() { + return *gs[0] + *gs[1];; +} diff --git a/testing/kernel-cache-tests/extra-prelink-info/main.kernel b/testing/kernel-cache-tests/extra-prelink-info/main.kernel new file mode 100755 index 0000000..a27921c Binary files /dev/null and b/testing/kernel-cache-tests/extra-prelink-info/main.kernel differ diff --git a/testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist b/testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist new file mode 100644 index 0000000..e244742 --- /dev/null +++ b/testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist @@ -0,0 +1,15 @@ + + + + + key1 + string value + key2 + 500 + key3 + + element 0 + element 1 + + + diff --git a/testing/kernel-cache-tests/extra-prelink-info/test.py b/testing/kernel-cache-tests/extra-prelink-info/test.py new file mode 100755 index 0000000..2576557 --- /dev/null +++ b/testing/kernel-cache-tests/extra-prelink-info/test.py @@ -0,0 +1,23 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/extra-prelink-info/main.kc", "/extra-prelink-info/main.kernel", None, [], + ["-prelink-info-extra", "$PWD/extra-prelink-info/prelink-info.plist"]) + + # Check the layout + kernel_cache.analyze("/extra-prelink-info/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/fixups-arm64e/bar.c b/testing/kernel-cache-tests/fixups-arm64e/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar new file mode 100755 index 0000000..5d5b0e9 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo new file mode 100755 index 0000000..e9599f1 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/fixups-arm64e/foo.c b/testing/kernel-cache-tests/fixups-arm64e/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/fixups-arm64e/main.cpp b/testing/kernel-cache-tests/fixups-arm64e/main.cpp new file mode 100644 index 0000000..e5217ce --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/main.cpp @@ -0,0 +1,33 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + int k; + int *p2; // aligned to 4 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/fixups-arm64e/main.kernel b/testing/kernel-cache-tests/fixups-arm64e/main.kernel new file mode 100755 index 0000000..0b24c4a Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/main.kernel differ diff --git a/testing/kernel-cache-tests/fixups-arm64e/test.py b/testing/kernel-cache-tests/fixups-arm64e/test.py new file mode 100644 index 0000000..a38b99b --- /dev/null +++ b/testing/kernel-cache-tests/fixups-arm64e/test.py @@ -0,0 +1,90 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64e", "/fixups-arm64e/main.kc", "/fixups-arm64e/main.kernel", "/fixups-arm64e/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/fixups-arm64e/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007010000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF00701C000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007020000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF007030000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007010000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007020000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF007014000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFFF007018000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFFF00700C000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFFF007018040" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C010" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000" + + # Check the fixups + kernel_cache.analyze("/fixups-arm64e/main.kc", ["-fixups", "-arch", "arm64e"]) + assert len(kernel_cache.dictionary()["fixups"]) == 11 + # main.kernel: S s = { &func, &func, &g, &func, &g }; + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x1C008"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0xFFFFFFF00702802C" + assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x20008"] == "kc(0) + 0xFFFFFFF00702802C" + # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + assert kernel_cache.dictionary()["fixups"]["0x24004"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x2400C"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x24018"] == "kc(0) + 0xFFFFFFF00702802C" + assert kernel_cache.dictionary()["fixups"]["0x24024"] == "kc(0) + 0xFFFFFFF00702802C" + # bar.kext: __typeof(&bar) barPtr = &bar; + assert kernel_cache.dictionary()["fixups"]["0x28000"] == "kc(0) + 0xFFFFFFF007018000 auth(IA !addr 0)" + # foo.kext: int* gPtr = &g; + assert kernel_cache.dictionary()["fixups"]["0x28010"] == "kc(0) + 0xFFFFFFF00702C018" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo new file mode 100755 index 0000000..b248d69 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c b/testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c new file mode 100644 index 0000000..d883a14 --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c @@ -0,0 +1,30 @@ + +int g = 0; + +static int func() { + return g; +} + +struct __attribute__((packed)) __attribute__((aligned((4096)))) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + char k; + int *p2; // aligned to 1 +}; + +struct PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +struct PackedS ps_array[4] = { + { 0, &func, &func, 0, &g, 0, &g }, + { 0, &func, &func, 0, &g, 0, &g }, + { 0, &func, &func, 0, &g, 0, &g }, + { 0, &func, &func, 0, &g, 0, &g } +}; + + +int foo() { + return ps.funcPtr(); +} diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp new file mode 100644 index 0000000..0f3943c --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel new file mode 100755 index 0000000..ab172f1 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel differ diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py b/testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py new file mode 100644 index 0000000..1c71726 --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py @@ -0,0 +1,61 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Test unaligned fixups in x86_64 kexts + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/fixups-x86_64-unaligned/main.kc", "/fixups-x86_64-unaligned/main.kernel", "/fixups-x86_64-unaligned/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/fixups-x86_64-unaligned/main.kc", ["-symbols", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + # int foo(); + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo" + foo_vmaddr_foo = kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"] + # int g; + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["name"] == "_g" + foo_vmaddr_g = kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["vmAddr"] + # PackedS ps; + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][2]["name"] == "_ps" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][2]["vmAddr"] == "0x20C000" + # int func(); + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_func" + foo_vmaddr_func = kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["vmAddr"] + + # Check the fixups + kernel_cache.analyze("/fixups-x86_64-unaligned/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 20 + # foo.kext: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + # _ps is at 0x20C000 which is offset 0x10C000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10C004"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10C00C"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10C018"] == "kc(0) + " + foo_vmaddr_g + assert kernel_cache.dictionary()["fixups"]["0x10C021"] == "kc(0) + " + foo_vmaddr_g + # foo.kext: PackedS ps_array = { { 0, &func, &func, 0, &g, 0, &g }, ... } + # _ps_array[0] is at 0x20D000 which is offset 0x10D000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10D004"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10D00C"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10D018"] == "kc(0) + " + foo_vmaddr_g + assert kernel_cache.dictionary()["fixups"]["0x10D021"] == "kc(0) + " + foo_vmaddr_g + # _ps_array[1] is at 0x20E000 which is offset 0x10D000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10E004"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10E00C"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10E018"] == "kc(0) + " + foo_vmaddr_g + assert kernel_cache.dictionary()["fixups"]["0x10E021"] == "kc(0) + " + foo_vmaddr_g + # _ps_array[2] is at 0x20F000 which is offset 0x10D000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10F004"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10F00C"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x10F018"] == "kc(0) + " + foo_vmaddr_g + assert kernel_cache.dictionary()["fixups"]["0x10F021"] == "kc(0) + " + foo_vmaddr_g + # _ps_array[3] is at 0x210000 which is offset 0x10D000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x110004"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x11000C"] == "kc(0) + " + foo_vmaddr_func + assert kernel_cache.dictionary()["fixups"]["0x110018"] == "kc(0) + " + foo_vmaddr_g + assert kernel_cache.dictionary()["fixups"]["0x110021"] == "kc(0) + " + foo_vmaddr_g + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo + diff --git a/testing/kernel-cache-tests/fixups-x86_64/bar.c b/testing/kernel-cache-tests/fixups-x86_64/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar new file mode 100755 index 0000000..bb30537 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo new file mode 100755 index 0000000..6c4d15b Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/fixups-x86_64/foo.c b/testing/kernel-cache-tests/fixups-x86_64/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/fixups-x86_64/main.cpp b/testing/kernel-cache-tests/fixups-x86_64/main.cpp new file mode 100644 index 0000000..b3811a2 --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/main.cpp @@ -0,0 +1,36 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + char k; + int *p2; // aligned to 1 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret"); + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/fixups-x86_64/main.kernel b/testing/kernel-cache-tests/fixups-x86_64/main.kernel new file mode 100755 index 0000000..f2ebee2 Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/main.kernel differ diff --git a/testing/kernel-cache-tests/fixups-x86_64/test.py b/testing/kernel-cache-tests/fixups-x86_64/test.py new file mode 100644 index 0000000..890e7da --- /dev/null +++ b/testing/kernel-cache-tests/fixups-x86_64/test.py @@ -0,0 +1,94 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/fixups-x86_64/main.kc", "/fixups-x86_64/main.kernel", "/fixups-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/fixups-x86_64/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 7 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000218000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000205000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000215000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF8000206000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF8000215010" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000" + + # Check the fixups + kernel_cache.analyze("/fixups-x86_64/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 12 + # main.kernel: S s = { &func, &func, &g, &func, &g }; + # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # bar.kext: __typeof(&bar) barPtr = &bar; + # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x115000"] == "kc(0) + 0xFFFFFF8000205FE0" + # foo.kext: int* gPtr = &g; + # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x115010"] == "kc(0) + 0xFFFFFF8000215018" + # main.kernel: movl _foo, %esp + # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/hello-world-auxkc/bar.c b/testing/kernel-cache-tests/hello-world-auxkc/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/hello-world-auxkc/foo.c b/testing/kernel-cache-tests/hello-world-auxkc/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/hello-world-auxkc/main.c b/testing/kernel-cache-tests/hello-world-auxkc/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/hello-world-auxkc/main.kernel b/testing/kernel-cache-tests/hello-world-auxkc/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/main.kernel differ diff --git a/testing/kernel-cache-tests/hello-world-auxkc/test.py b/testing/kernel-cache-tests/hello-world-auxkc/test.py new file mode 100644 index 0000000..bb55681 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-auxkc/test.py @@ -0,0 +1,91 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that an auxKC has a reverse vmAddr order + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/hello-world-auxkc/main.kc", "/hello-world-auxkc/main.kernel", "/hello-world-auxkc/extensions", [], []) + kernel_cache.analyze("/hello-world-auxkc/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 5 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/hello-world-auxkc/aux.kc", "/hello-world-auxkc/main.kc", "", "/hello-world-auxkc/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/hello-world-auxkc/aux.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x18000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14020" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x18000" + + # Check the fixups + kernel_cache.analyze("/hello-world-auxkc/aux.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(3) + 0x14020" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/hello-world-kernel/main.c b/testing/kernel-cache-tests/hello-world-kernel/main.c new file mode 100644 index 0000000..f545f5b --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-kernel/main.c @@ -0,0 +1,13 @@ + +// Add a large buffer so that we know we have a bunch of stuff in __DATA +// and can more easily see that all the segments have moved around correctly, not just +// got lucky that all are the same size +__attribute__((used)) +char buffer[1024 * 16]; + +int f = 0; +int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] }; + +int _start() { + return *gs[0] + *gs[1];; +} diff --git a/testing/kernel-cache-tests/hello-world-kernel/main.kernel b/testing/kernel-cache-tests/hello-world-kernel/main.kernel new file mode 100755 index 0000000..a27921c Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-kernel/main.kernel differ diff --git a/testing/kernel-cache-tests/hello-world-kernel/test.py b/testing/kernel-cache-tests/hello-world-kernel/test.py new file mode 100755 index 0000000..0fe437d --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-kernel/test.py @@ -0,0 +1,55 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/hello-world-kernel/main.kc", "/hello-world-kernel/main.kernel", None, [], []) + + # Check the layout + kernel_cache.analyze("/hello-world-kernel/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x18000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000" + + # Check the entry point + kernel_cache.analyze("/hello-world-kernel/main.kc", ["-entrypoint", "-arch", "arm64"]) + assert kernel_cache.dictionary()["entrypoint"] == "0x8000" + + # Check the fixups + kernel_cache.analyze("/hello-world-kernel/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x10000"] == "kc(0) + 0x10028" + assert kernel_cache.dictionary()["fixups"]["0x10008"] == "kc(0) + 0x10028" + assert kernel_cache.dictionary()["fixups"]["0x10018"] == "kc(0) + 0x1002C" + assert kernel_cache.dictionary()["fixups"]["0x10020"] == "kc(0) + 0x1002D" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/bar.c b/testing/kernel-cache-tests/hello-world-pageablekc/bar.c new file mode 100644 index 0000000..0a34978 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/bar.c @@ -0,0 +1,9 @@ + +int x = 0; +int *p = &x; + +extern int foo(); + +int bar() { + return foo() + *p; +} diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar new file mode 100755 index 0000000..f2afedf Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo new file mode 100755 index 0000000..4b5f159 Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/foo.c b/testing/kernel-cache-tests/hello-world-pageablekc/foo.c new file mode 100644 index 0000000..27dec52 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/foo.c @@ -0,0 +1,7 @@ + +int x = 0; +int *p = &x; + +int foo() { + return *p; +} diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/main.c b/testing/kernel-cache-tests/hello-world-pageablekc/main.c new file mode 100644 index 0000000..1023537 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/main.c @@ -0,0 +1,7 @@ + +int x; +int *p = &x; + +int _start() { + return *p; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel b/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel new file mode 100755 index 0000000..b15dbc6 Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel differ diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/test.py b/testing/kernel-cache-tests/hello-world-pageablekc/test.py new file mode 100644 index 0000000..e585454 --- /dev/null +++ b/testing/kernel-cache-tests/hello-world-pageablekc/test.py @@ -0,0 +1,106 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that pageableKC has nothing packed on the same page + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/hello-world-pageablekc/main.kc", "/hello-world-pageablekc/main.kernel", "/hello-world-pageablekc/extensions", [], []) + kernel_cache.analyze("/hello-world-pageablekc/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x14000" + + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/hello-world-pageablekc/pageable.kc", "/hello-world-pageablekc/main.kc", "/hello-world-pageablekc/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/hello-world-pageablekc/pageable.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x20000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x20000" + + # Check the fixups + kernel_cache.analyze("/hello-world-pageablekc/pageable.kc", ["-fixups", "-arch", "arm64"]) + assert kernel_cache.dictionary()["fixups"] == "" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 2 + # extern int foo() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x14000"] == "kc(1) + 0x10000" + # int* p = &x; + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x14008"] == "kc(1) + 0x18010" + + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1 + # int* p = &x; + assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x14000"] == "kc(1) + 0x1C008" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/implicit-dependencies/bar.c b/testing/kernel-cache-tests/implicit-dependencies/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/implicit-dependencies/foo.c b/testing/kernel-cache-tests/implicit-dependencies/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/implicit-dependencies/main.c b/testing/kernel-cache-tests/implicit-dependencies/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/implicit-dependencies/main.kernel b/testing/kernel-cache-tests/implicit-dependencies/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/main.kernel differ diff --git a/testing/kernel-cache-tests/implicit-dependencies/test.py b/testing/kernel-cache-tests/implicit-dependencies/test.py new file mode 100644 index 0000000..ddefa40 --- /dev/null +++ b/testing/kernel-cache-tests/implicit-dependencies/test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# foo.kext is not listed on the command line, but should be found by the dependency in the plist in bar.kext + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/implicit-dependencies/main.kc", "/implicit-dependencies/main.kernel", "/implicit-dependencies/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/implicit-dependencies/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.c b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.c new file mode 100644 index 0000000..6e90f69 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.c @@ -0,0 +1,19 @@ + +int g = 2; + +int foo() { + return g; +} + +__typeof(&foo) fooPtr = &foo; + +__attribute__((section(("__HIB, __data")))) +int f = 1; + +__attribute__((section(("__HIB, __data")))) +int* fPtr = &f; + +__attribute__((section(("__HIB, __text")))) +int _start() { + return f + fooPtr(); +} diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel new file mode 100755 index 0000000..d38d6fd Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 new file mode 100644 index 0000000..1b5d53d Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 differ diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/test.py b/testing/kernel-cache-tests/kernel-base-address-x86_64/test.py new file mode 100755 index 0000000..0d2408b --- /dev/null +++ b/testing/kernel-cache-tests/kernel-base-address-x86_64/test.py @@ -0,0 +1,55 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kernel-base-address-x86_64/main.kc", "/kernel-base-address-x86_64/main.kernel", None, [], []) + + # Check the layout + kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 7 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000210000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000210000" + + # Check the entry point + kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-entrypoint", "-arch", "x86_64"]) + assert kernel_cache.dictionary()["entrypoint"] == "0xFFFFFF8000100000" + + # Check the fixups + kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x30"] == "kc(0) + 0xFFFFFF8000100028 : pointer64" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/kernel-base-address/main.c b/testing/kernel-cache-tests/kernel-base-address/main.c new file mode 100644 index 0000000..f545f5b --- /dev/null +++ b/testing/kernel-cache-tests/kernel-base-address/main.c @@ -0,0 +1,13 @@ + +// Add a large buffer so that we know we have a bunch of stuff in __DATA +// and can more easily see that all the segments have moved around correctly, not just +// got lucky that all are the same size +__attribute__((used)) +char buffer[1024 * 16]; + +int f = 0; +int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] }; + +int _start() { + return *gs[0] + *gs[1];; +} diff --git a/testing/kernel-cache-tests/kernel-base-address/main.kernel b/testing/kernel-cache-tests/kernel-base-address/main.kernel new file mode 100755 index 0000000..e9d9d00 Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-base-address/test.py b/testing/kernel-cache-tests/kernel-base-address/test.py new file mode 100755 index 0000000..45de5de --- /dev/null +++ b/testing/kernel-cache-tests/kernel-base-address/test.py @@ -0,0 +1,55 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kernel-base-address/main.kc", "/kernel-base-address/main.kernel", None, [], []) + + # Check the layout + kernel_cache.analyze("/kernel-base-address/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 6 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF007010000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007014000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF00701C000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007014000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF00700C000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF00701C000" + + # Check the entry point + kernel_cache.analyze("/kernel-base-address/main.kc", ["-entrypoint", "-arch", "arm64"]) + assert kernel_cache.dictionary()["entrypoint"] == "0xFFFFFFF00700C000" + + # Check the fixups + kernel_cache.analyze("/kernel-base-address/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x10000"] == "kc(0) + 0xFFFFFFF007014028" + assert kernel_cache.dictionary()["fixups"]["0x10008"] == "kc(0) + 0xFFFFFFF007014028" + assert kernel_cache.dictionary()["fixups"]["0x10018"] == "kc(0) + 0xFFFFFFF00701402C" + assert kernel_cache.dictionary()["fixups"]["0x10020"] == "kc(0) + 0xFFFFFFF00701402D" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-image_base,0xfffffff007004000 + diff --git a/testing/kernel-cache-tests/kernel-uuid/main.c b/testing/kernel-cache-tests/kernel-uuid/main.c new file mode 100644 index 0000000..f545f5b --- /dev/null +++ b/testing/kernel-cache-tests/kernel-uuid/main.c @@ -0,0 +1,13 @@ + +// Add a large buffer so that we know we have a bunch of stuff in __DATA +// and can more easily see that all the segments have moved around correctly, not just +// got lucky that all are the same size +__attribute__((used)) +char buffer[1024 * 16]; + +int f = 0; +int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] }; + +int _start() { + return *gs[0] + *gs[1];; +} diff --git a/testing/kernel-cache-tests/kernel-uuid/main.kernel b/testing/kernel-cache-tests/kernel-uuid/main.kernel new file mode 100755 index 0000000..a27921c Binary files /dev/null and b/testing/kernel-cache-tests/kernel-uuid/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-uuid/test.py b/testing/kernel-cache-tests/kernel-uuid/test.py new file mode 100755 index 0000000..4847ad1 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-uuid/test.py @@ -0,0 +1,15 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kernel-uuid/main.kc", "/kernel-uuid/main.kernel", None, [], []) + + # Check the UUID + kernel_cache.analyze("/kernel-uuid/main.kc", ["-uuid", "-arch", "arm64"]) + assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist new file mode 100644 index 0000000..e80d5d5 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN3Foo + + + SymbolName + __ZTV3Foo + + + + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp new file mode 100644 index 0000000..2082782 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); + + virtual int replaced_with_fooUsed0(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..917ef6c --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar new file mode 100755 index 0000000..4c4073d Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp new file mode 100644 index 0000000..b1056d6 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp @@ -0,0 +1,105 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__TEXT_EXEC, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel new file mode 100755 index 0000000..1cee7ea Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py new file mode 100644 index 0000000..426fadd --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py @@ -0,0 +1,86 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests verifies that the vtable in bar.kext is patched +# But also that this can be done against a subclass in the kernel, not just + +# Note this is the same as kernel-vtable-patching but with a large base address and chained fixups + +def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def findFixupVMAddr(kernel_cache, fixup_name): + for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems(): + if fixup_target == fixup_name: + return fixup_vmaddr + return None + +def offsetVMAddr(vmAddr, offset): + het_int = int(vmAddr, 16) + het_int = het_int + offset + return ''.join([ '0x', hex(het_int).upper()[2:] ]) + +def check(kernel_cache): + enableLogging = False + kernel_cache.buildKernelCollection("arm64e", "/kernel-vtable-patching-arm64e/main.kc", "/kernel-vtable-patching-arm64e/main.kernel", "/kernel-vtable-patching-arm64e/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + fooClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN3Foo3fooEv") + if enableLogging: + print "fooClassFooVMAddr: " + fooClassFooVMAddr + + # Foo::fooUsed0() + fooClassUsed0VMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN3Foo8fooUsed0Ev") + if enableLogging: + print "fooClassUsed0VMAddr: " + fooClassUsed0VMAddr + + # From bar, find the vtable and its override of foo() + # Bar::foo() + barClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 1, "__ZN3Bar3fooEv") + if enableLogging: + print "barClassFooVMAddr: " + barClassFooVMAddr + + + # Check the fixups + kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"]) + + # foo.kext + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + fooFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + fooClassFooVMAddr + " auth(IA addr 49764)") + if enableLogging: + print "fooFooFixupAddr: " + fooFooFixupAddr + # Then the following fixup should be to Foo::fooUsed0() + fooFooNextFixupAddr = offsetVMAddr(fooFooFixupAddr, 8) + if enableLogging: + print "fooFooNextFixupAddr: " + fooFooNextFixupAddr + assert kernel_cache.dictionary()["fixups"][fooFooNextFixupAddr] == "kc(0) + " + fooClassUsed0VMAddr + " auth(IA addr 61962)" + + # bar.kext + # Now in bar, again match the entry for its Bar::foo() symbol + barFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + barClassFooVMAddr + " auth(IA addr 49764)") + if enableLogging: + print "barFooFixupAddr: " + barFooFixupAddr + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + barFooNextFixupAddr = offsetVMAddr(barFooFixupAddr, 8) + if enableLogging: + print "barFooNextFixupAddr: " + barFooNextFixupAddr + assert kernel_cache.dictionary()["fixups"][barFooNextFixupAddr] == "kc(0) + " + fooClassUsed0VMAddr + " auth(IA addr 61962)" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-image_base,0xfffffff000000000 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DFOO_USED=1 -Wl,-kernel -Wl,-fixup_chains +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-fixup_chains +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist new file mode 100644 index 0000000..e80d5d5 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN3Foo + + + SymbolName + __ZTV3Foo + + + + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..917ef6c --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar new file mode 100755 index 0000000..b3bd60b Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel new file mode 100755 index 0000000..77cac7b Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-error/test.py new file mode 100644 index 0000000..49c8be3 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-error/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Bar has a superclass in Foo, but we don't export that symbol. This causes the vtable patcher to fail + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching-error/main.kc", "/kernel-vtable-patching-error/main.kernel", "/kernel-vtable-patching-error/extensions", ["com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + # bar + assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Cannot find symbol for metaclass pointed to by '__ZN3Bar10superClassE'. Expected symbol '__ZN3Foo10gMetaClassE' to be defined in another kext" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist new file mode 100644 index 0000000..e80d5d5 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN3Foo + + + SymbolName + __ZTV3Foo + + + + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..917ef6c --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar new file mode 100755 index 0000000..b3bd60b Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel new file mode 100755 index 0000000..c72998c Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py new file mode 100644 index 0000000..1b02f4b --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py @@ -0,0 +1,53 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests verifies that the vtable in bar.kext is patched +# But also that this can be done against a subclass in the kernel, not just + +# Note this is the same as kernel-vtable-patching but with a large base address + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching-large-base-addr/main.kc", "/kernel-vtable-patching-large-base-addr/main.kernel", "/kernel-vtable-patching-large-base-addr/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["vmAddr"] == "0xFFFFFF8000205EC0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["vmAddr"] == "0xFFFFFF8000205EE0" + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0xFFFFFF8000206F10" + + + # Check the fixups + kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x108500"] == "kc(0) + 0xFFFFFF8000205EC0 : pointer64" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x108508"] == "kc(0) + 0xFFFFFF8000205EE0 : pointer64" + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x111150"] == "kc(0) + 0xFFFFFF8000206F10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x111158"] == "kc(0) + 0xFFFFFF8000205EE0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist new file mode 100644 index 0000000..e80d5d5 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist @@ -0,0 +1,56 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + SymbolPrefix + __ZN3Foo + + + SymbolName + __ZTV3Foo + + + + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..917ef6c --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar new file mode 100755 index 0000000..b3bd60b Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel new file mode 100755 index 0000000..5999f32 Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/test.py b/testing/kernel-cache-tests/kernel-vtable-patching/test.py new file mode 100644 index 0000000..635705f --- /dev/null +++ b/testing/kernel-cache-tests/kernel-vtable-patching/test.py @@ -0,0 +1,51 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests verifies that the vtable in bar.kext is patched +# But also that this can be done against a subclass in the kernel, not just + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching/main.kc", "/kernel-vtable-patching/main.kernel", "/kernel-vtable-patching/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["vmAddr"] == "0x15EC0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["vmAddr"] == "0x15EE0" + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x16F10" + + + # Check the fixups + kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x14500"] == "kc(0) + 0x15EC0 : pointer64" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x14508"] == "kc(0) + 0x15EE0 : pointer64" + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16F10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x15EE0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..1111344 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo new file mode 100755 index 0000000..aef0390 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/foo.c b/testing/kernel-cache-tests/kext-bind-missing-dep/foo.c new file mode 100644 index 0000000..d8f3634 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-missing-dep/foo.c @@ -0,0 +1,6 @@ + +extern int bar; + +int foo() { + return bar; +} diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/main.c b/testing/kernel-cache-tests/kext-bind-missing-dep/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-missing-dep/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel b/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/test.py b/testing/kernel-cache-tests/kext-bind-missing-dep/test.py new file mode 100644 index 0000000..34a3170 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-missing-dep/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that we get sensible errors on the bad kext + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-missing-dep/main.kc", "/kext-bind-missing-dep/main.kernel", "/kext-bind-missing-dep/extensions", ["com.apple.foo"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert kernel_cache.dictionary()[0]["id"] == "com.apple.foo" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_bar' as could not find a kext with 'com.apple.bar' bundle-id" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar new file mode 100755 index 0000000..261f5ad Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo new file mode 100755 index 0000000..65356c1 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c new file mode 100644 index 0000000..30b797d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c @@ -0,0 +1,9 @@ + +int func() { + return 0; +} + +__typeof(&func) funcPtr = &func; +int _start() { + return funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel new file mode 100755 index 0000000..e8b37a3 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py new file mode 100644 index 0000000..2ddd899 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py @@ -0,0 +1,50 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests that kexts can bind to each other using DYLD_CHAINED_PTR_64_OFFSET + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-arm64-chains/main.kc", "/kext-bind-to-kext-arm64-chains/main.kernel", "/kext-bind-to-kext-arm64-chains/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # layout + kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-layout", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00701C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Symbols + kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-symbols", "-arch", "arm64"]) + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0xFFFFFFF007018020" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + # bar.kext: extern int foo(); + assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000" + # main.kernel: __typeof(&func) funcPtr = &func; + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xFFFFFFF007018020" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c new file mode 100644 index 0000000..388b52a --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c @@ -0,0 +1,9 @@ + +extern int foo(); +extern int f; + +__typeof(&foo) fooPtr = &foo; + +int bar() { + return foo() + fooPtr() + f; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar new file mode 100755 index 0000000..2827ca5 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo new file mode 100755 index 0000000..ee50afc Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c new file mode 100644 index 0000000..21cece2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c @@ -0,0 +1,6 @@ + +int f = 0; + +int foo() { + return f; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c new file mode 100644 index 0000000..28207ed --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c @@ -0,0 +1,10 @@ + +int f = 0; +int func() { + return 0; +} + +__typeof(&func) funcPtr = &func; +int _start() { + return funcPtr() + f; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel new file mode 100755 index 0000000..20a53df Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py new file mode 100644 index 0000000..f085fdd --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py @@ -0,0 +1,61 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests that kexts can bind to each other using DYLD_CHAINED_PTR_ARM64E_KERNEL + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64e", "/kext-bind-to-kext-arm64e-chains/main.kc", "/kext-bind-to-kext-arm64e-chains/main.kernel", "/kext-bind-to-kext-arm64e-chains/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF00701C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Symbols + kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-symbols", "-arch", "arm64e"]) + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["name"] == "_fooPtr" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["vmAddr"] == "0xFFFFFFF007028000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_f" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0xFFFFFFF007028008" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][1]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][1]["vmAddr"] == "0xFFFFFFF007018070" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-fixups", "-arch", "arm64e"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + # __DATA_CONST + # bar.kext: extern int foo(); + # bar.kext: extern int f; + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xFFFFFFF007018070 auth(IA addr 0)" + assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0xFFFFFFF007028008" + # __DATA + # main.kernel: __typeof(&func) funcPtr = &func; + assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)" + # bar.kext: __typeof(&foo) fooPtr = &foo; + assert kernel_cache.dictionary()["fixups"]["0x24000"] == "kc(0) + 0xFFFFFFF007018070 auth(IA !addr 0)" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c new file mode 100644 index 0000000..3fa0fab --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c @@ -0,0 +1,7 @@ + +extern int foo(); +extern int baz; + +int bar() { + return foo() + baz; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar new file mode 100755 index 0000000..423d808 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo new file mode 100755 index 0000000..8615d2c Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c new file mode 100644 index 0000000..7671e86 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c @@ -0,0 +1,6 @@ + +extern int baz; + +int foo() { + return baz; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py new file mode 100644 index 0000000..1aa511e --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py @@ -0,0 +1,26 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that we throw errors on each kext which is missing a symbol + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-missing-symbol/main.kc", "/kext-bind-to-kext-missing-symbol/main.kernel", "/kext-bind-to-kext-missing-symbol/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 2 + # bar.kext + assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_baz' in 'com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol" + # bar.kext + assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo" + assert len(kernel_cache.dictionary()[1]["errors"]) == 1 + assert kernel_cache.dictionary()[1]["errors"][0] == "Failed to bind '_baz' in 'com.apple.foo' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c new file mode 100644 index 0000000..e381442 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c @@ -0,0 +1,6 @@ + +extern int foo; + +int bar() { + return foo; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar new file mode 100755 index 0000000..3a9ad1d Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo new file mode 100755 index 0000000..1f0515b Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c new file mode 100644 index 0000000..42e0a8e --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c @@ -0,0 +1,2 @@ + +int foo; diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel new file mode 100755 index 0000000..cdc3eba Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py new file mode 100644 index 0000000..f926ee6 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py @@ -0,0 +1,46 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This tests that the very old bar.kext can be parsed. It's __got section has a S_NON_LAZY_SYMBOL_POINTERS type, +# but newer linkers changed to just S_REGULAR. + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kext-bind-to-kext-old-section-type/main.kc", "/kext-bind-to-kext-old-section-type/main.kernel", "/kext-bind-to-kext-old-section-type/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x19000" + + kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-symbols", "-arch", "x86_64"]) + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"] == "0x14000" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x15000"] == "kc(0) + 0x14000" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# Note, bar.kext has to be linked with a very old linker from 10.7 to get the __got section with S_NON_LAZY_SYMBOL_POINTERS. +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -Wl,-kernel -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__HIB,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar -fuse-ld=... + diff --git a/testing/task_for_pid_entitlement.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist similarity index 61% rename from testing/task_for_pid_entitlement.plist rename to testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist index 2398d67..9ecc3fe 100644 --- a/testing/task_for_pid_entitlement.plist +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist @@ -2,9 +2,10 @@ - com.apple.system-task-ports - - task_for_pid-allow - + CFBundleIdentifier + com.apple.foo + kpi + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..96e0388 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + not.com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo new file mode 100755 index 0000000..bf5dc5e Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py new file mode 100644 index 0000000..37cea02 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that an non-Apple kext cannot bind to another Apple kext with a symbol set in the kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-symbol-set-error/main.kc", "/kext-bind-to-kext-symbol-set-error/main.kernel", "/kext-bind-to-kext-symbol-set-error/extensions", ["com.apple.foo", "not.com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert kernel_cache.dictionary()[0]["id"] == "not.com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_foo' in 'not.com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist new file mode 100644 index 0000000..d60c2d8 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist @@ -0,0 +1,13 @@ + + + + + CFBundleIdentifier + com.apple.foo + Symbols + + _foo + _missingFoo + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt new file mode 100644 index 0000000..8037631 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt @@ -0,0 +1,2 @@ +_foo +_missingFoo diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..96e0388 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + not.com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo new file mode 100755 index 0000000..2a84e6e Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py new file mode 100644 index 0000000..b83eae1 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py @@ -0,0 +1,47 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that an non-Apple kext can bind to an Apple kext with a symbol set in the kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-symbol-set/main.kc", "/kext-bind-to-kext-symbol-set/main.kernel", "/kext-bind-to-kext-symbol-set/extensions", ["com.apple.foo", "not.com.apple.bar"], []) + kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "not.com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-symbols", "-arch", "arm64"]) + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14020" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14020" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "not.com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> ../kext-symbols.sh SymbolSets.plist ./extensions/foo.kext/Info.plist exports.txt +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/main.c b/testing/kernel-cache-tests/kext-bind-to-kext/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/test.py b/testing/kernel-cache-tests/kext-bind-to-kext/test.py new file mode 100644 index 0000000..19bce34 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kext/test.py @@ -0,0 +1,46 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext/main.kc", "/kext-bind-to-kext/main.kernel", "/kext-bind-to-kext/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-symbols", "-arch", "arm64"]) + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14020" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14020" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist new file mode 100644 index 0000000..25926e6 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.kpi.private + CFBundleVersion + 1.0.0 + OSBundleCompatibleVersion + 1.0.0 + Symbols + + + SymbolName + _foo + + + + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2a0b2db --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + not.com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.kpi.private + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel new file mode 100755 index 0000000..ae9427d Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py new file mode 100644 index 0000000..636decf --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that an non-Apple kext cannot bind to the com.apple.kpi.private symbol set + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kpi-private-symbol-set-error/main.kc", "/kext-bind-to-kpi-private-symbol-set-error/main.kernel", "/kext-bind-to-kpi-private-symbol-set-error/extensions", ["com.apple.foo", "not.com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert kernel_cache.dictionary()[0]["id"] == "not.com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_foo' in 'not.com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist new file mode 100644 index 0000000..25926e6 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.kpi.private + CFBundleVersion + 1.0.0 + OSBundleCompatibleVersion + 1.0.0 + Symbols + + + SymbolName + _foo + + + + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt new file mode 100644 index 0000000..8037631 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt @@ -0,0 +1,2 @@ +_foo +_missingFoo diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..649a915 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.kpi.private + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c new file mode 100644 index 0000000..4ea1b0a --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c @@ -0,0 +1,8 @@ + +int _start() { + return 0; +} + +int foo() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel new file mode 100755 index 0000000..77bf56d Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py new file mode 100644 index 0000000..cd3f0b0 --- /dev/null +++ b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py @@ -0,0 +1,41 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that an Apple kext can bind to KPI.private with a symbol set in the kext +# com.apple.kpi.private exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kpi-private-symbol-set/main.kc", "/kext-bind-to-kpi-private-symbol-set/main.kernel", "/kext-bind-to-kpi-private-symbol-set/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x18000" + + kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-symbols", "-arch", "arm64"]) + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC00C" + + # Check the fixups + kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 1 + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xC00C" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist new file mode 100644 index 0000000..df941be --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.libkern + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _gOSKextUnresolved + + + + + + diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/bar.c b/testing/kernel-cache-tests/kext-missing-weak-bind/bar.c new file mode 100644 index 0000000..9e23395 --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/bar.c @@ -0,0 +1,12 @@ + +__attribute__((weak)) +extern int weakValue; + +extern int gOSKextUnresolved; + +int bar() { + // Missing weak import test + if ( &weakValue != &gOSKextUnresolved ) + return 0; + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..7e9987d --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.libkern + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar new file mode 100755 index 0000000..920f5e6 Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..7a43b38 --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.libkern + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo new file mode 100755 index 0000000..f29c662 Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/foo.c b/testing/kernel-cache-tests/kext-missing-weak-bind/foo.c new file mode 100644 index 0000000..9e23395 --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/foo.c @@ -0,0 +1,12 @@ + +__attribute__((weak)) +extern int weakValue; + +extern int gOSKextUnresolved; + +int bar() { + // Missing weak import test + if ( &weakValue != &gOSKextUnresolved ) + return 0; + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/main.c b/testing/kernel-cache-tests/kext-missing-weak-bind/main.c new file mode 100644 index 0000000..31fd572 --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/main.c @@ -0,0 +1,7 @@ + +// We need this symbol to bind missing weak imports to +int gOSKextUnresolved = 0; + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel b/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel new file mode 100755 index 0000000..476d3aa Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/test.py b/testing/kernel-cache-tests/kext-missing-weak-bind/test.py new file mode 100644 index 0000000..42b0022 --- /dev/null +++ b/testing/kernel-cache-tests/kext-missing-weak-bind/test.py @@ -0,0 +1,46 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that weak binds can be missing, so long as we check for the magic symbol + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-missing-weak-bind/main.kc", "/kext-missing-weak-bind/main.kernel", "/kext-missing-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-layout", "-arch", "arm64"]) + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Symbols + kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-symbols", "-arch", "arm64"]) + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_gOSKextUnresolved" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0x20000" + + # Check the fixups + kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x20000" + assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0x20000" + assert kernel_cache.dictionary()["fixups"]["0x18010"] == "kc(0) + 0x20000" + assert kernel_cache.dictionary()["fixups"]["0x18018"] == "kc(0) + 0x20000" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-receipts/large.txt b/testing/kernel-cache-tests/kext-receipts/large.txt new file mode 100644 index 0000000..190423f --- /dev/null +++ b/testing/kernel-cache-tests/kext-receipts/large.txt @@ -0,0 +1,100 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 diff --git a/testing/kernel-cache-tests/kext-receipts/large2.txt b/testing/kernel-cache-tests/kext-receipts/large2.txt new file mode 100644 index 0000000..4ab65cc --- /dev/null +++ b/testing/kernel-cache-tests/kext-receipts/large2.txt @@ -0,0 +1 @@ +large data \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-receipts/main.c b/testing/kernel-cache-tests/kext-receipts/main.c new file mode 100644 index 0000000..f545f5b --- /dev/null +++ b/testing/kernel-cache-tests/kext-receipts/main.c @@ -0,0 +1,13 @@ + +// Add a large buffer so that we know we have a bunch of stuff in __DATA +// and can more easily see that all the segments have moved around correctly, not just +// got lucky that all are the same size +__attribute__((used)) +char buffer[1024 * 16]; + +int f = 0; +int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] }; + +int _start() { + return *gs[0] + *gs[1];; +} diff --git a/testing/kernel-cache-tests/kext-receipts/main.kernel b/testing/kernel-cache-tests/kext-receipts/main.kernel new file mode 100755 index 0000000..a27921c Binary files /dev/null and b/testing/kernel-cache-tests/kext-receipts/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-receipts/small.txt b/testing/kernel-cache-tests/kext-receipts/small.txt new file mode 100644 index 0000000..71f64db --- /dev/null +++ b/testing/kernel-cache-tests/kext-receipts/small.txt @@ -0,0 +1 @@ +small data \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-receipts/test.py b/testing/kernel-cache-tests/kext-receipts/test.py new file mode 100755 index 0000000..ca7d574 --- /dev/null +++ b/testing/kernel-cache-tests/kext-receipts/test.py @@ -0,0 +1,78 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-receipts/main.kc", "/kext-receipts/main.kernel", None, [], + ["-sectcreate", "__SMALL", "", "$PWD/kext-receipts/small.txt", + "-sectcreate", "__SIXTEEN_CHARSS", "__sixteen_chaars", "$PWD/kext-receipts/large.txt", + "-sectcreate", "__SIXTEEN_CHARSS", "__large2", "$PWD/kext-receipts/large2.txt"]) + + # Check the layout + kernel_cache.analyze("/kext-receipts/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 8 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000" + + # small.txt + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__SMALL" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000" + assert not "sections" in kernel_cache.dictionary()["cache-segments"][3] + + # large.txt, then large2.txt + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__SIXTEEN_CHARSS" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["cache-segments"][4]["vmSize"] == "0x4000" + assert len(kernel_cache.dictionary()["cache-segments"][4]["sections"]) == 2 + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["name"] == "__sixteen_chaars"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmAddr"] == "0x10000"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmEnd"] == "0x10124"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmSize"] == "0x124"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["name"] == "__large2"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmAddr"] == "0x10124"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmEnd"] == "0x1012E"; + assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmSize"] == "0xA"; + + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0x20000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x20000" + + # Check the entry point + kernel_cache.analyze("/kext-receipts/main.kc", ["-entrypoint", "-arch", "arm64"]) + assert kernel_cache.dictionary()["entrypoint"] == "0x8000" + + # Check the fixups + kernel_cache.analyze("/kext-receipts/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x18028" + assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0x18028" + assert kernel_cache.dictionary()["fixups"]["0x18018"] == "kc(0) + 0x1802C" + assert kernel_cache.dictionary()["fixups"]["0x18020"] == "kc(0) + 0x1802D" + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack + diff --git a/testing/kernel-cache-tests/kext-relative-paths/bar.c b/testing/kernel-cache-tests/kext-relative-paths/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo differ diff --git a/testing/kernel-cache-tests/kext-relative-paths/foo.c b/testing/kernel-cache-tests/kext-relative-paths/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kext-relative-paths/main.c b/testing/kernel-cache-tests/kext-relative-paths/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-relative-paths/main.kernel b/testing/kernel-cache-tests/kext-relative-paths/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-relative-paths/test.py b/testing/kernel-cache-tests/kext-relative-paths/test.py new file mode 100644 index 0000000..2625b0d --- /dev/null +++ b/testing/kernel-cache-tests/kext-relative-paths/test.py @@ -0,0 +1,28 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Foo has a macOS style bundle layout while bar is an iOS style layout. Make sure we +# get the relative path right in each case + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-relative-paths/main.kc", "/kext-relative-paths/main.kernel", "/kext-relative-paths/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-relative-paths/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["relativePath"] == "bar" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["relativePath"] == "Contents/MacOS/foo" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/Contents/MacOS/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp new file mode 100644 index 0000000..d144562 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp @@ -0,0 +1,13 @@ + +#include "bar.h" + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h new file mode 100644 index 0000000..cd0713d --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h @@ -0,0 +1,10 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp new file mode 100644 index 0000000..e6ddc3f --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp @@ -0,0 +1,21 @@ + +#include "bar.h" + +class Baz : public Bar +{ + OSDeclareDefaultStructors( Baz ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Baz, Bar ) + +int Baz::foo() { + return 1; +} + +int baz() { + Baz* baz = new Baz(); + return baz->foo(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar new file mode 100755 index 0000000..19fb1b6 Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist new file mode 100644 index 0000000..073175d --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + baz + CFBundleIdentifier + com.apple.baz + CFBundleName + baz + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz new file mode 100755 index 0000000..8f1bd69 Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo new file mode 100755 index 0000000..30c7f3d Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp new file mode 100644 index 0000000..13f66ef --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp @@ -0,0 +1,29 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Redefine this just so that we can write tests +#undef OSMetaClassDefineReservedUnused +#define OSMetaClassDefineReservedUnused(className, index) \ +void className ::_RESERVED ## className ## index () \ + { gMetaClass.reservedCalled(index); } + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h new file mode 100644 index 0000000..401eeb4 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h @@ -0,0 +1,27 @@ + +#include +#include + +// Redefine this just so that we can write tests +#undef OSMetaClassDeclareReservedUnused +#define OSMetaClassDeclareReservedUnused(className, index) \ + private: \ + virtual void _RESERVED ## className ## index () + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp new file mode 100644 index 0000000..3cd8622 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp @@ -0,0 +1,108 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +// Thee are none of these for arm64e + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__TEXT_EXEC, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel new file mode 100755 index 0000000..0fb7e40 Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py new file mode 100644 index 0000000..297cd0e --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py @@ -0,0 +1,68 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it +# bar.kext then exports foo and baz.kext binds to it + +# Note this is the same as the kext-vtable-patching test, just with arm64e so ptrauth on the fixups + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64e", "/kext-vtable-patching-arm64e/main.kc", "/kext-vtable-patching-arm64e/main.kernel", "/kext-vtable-patching-arm64e/extensions", ["com.apple.foo", "com.apple.bar", "com.apple.baz"], []) + kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.baz" + assert kernel_cache.dictionary()["dylibs"][3]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x2036C" + + # From baz, find the vtable and its override of foo() + # Baz::foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["name"] == "__ZN3Baz3fooEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["vmAddr"] == "0x2085C" + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][6]["vmAddr"] == "0x20DE8" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][7]["vmAddr"] == "0x20E00" + + + # Check the fixups + kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x20480"] == "kc(0) + 0x20DE8 auth(IA addr 49764)" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x20488"] == "kc(0) + 0x20E00 auth(IA addr 61962)" + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x200E8"] == "kc(0) + 0x2036C auth(IA addr 49764)" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x200F0"] == "kc(0) + 0x20E00 auth(IA addr 61962)" + + # Now in baz, again match the entry for its Baz::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x202B0"] == "kc(0) + 0x2085C auth(IA addr 49764)" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x202B8"] == "kc(0) + 0x20E00 auth(IA addr 61962)" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -Wl,-fixup_chains -Wl,-kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const baz.cpp -o extensions/baz.kext/baz -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar new file mode 100755 index 0000000..111490e Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo new file mode 100755 index 0000000..9483e1f Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h new file mode 100644 index 0000000..67aff00 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h @@ -0,0 +1,22 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); + + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py new file mode 100644 index 0000000..572a025 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Try to patch a vtable, but the parent vtable is too small + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching-error-small-vtable/main.kc", "/kext-vtable-patching-error-small-vtable/main.kernel", "/kext-vtable-patching-error-small-vtable/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "Malformed vtable. Super class '__ZTV3Foo' has 40 entries vs subclass '__ZTV3Bar' with 37 entries" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp new file mode 100644 index 0000000..3985772 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public FooSub +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, FooSub ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar new file mode 100755 index 0000000..11d535e Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo new file mode 100755 index 0000000..2a6f8e2 Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp new file mode 100644 index 0000000..a82a5f7 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp @@ -0,0 +1,37 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooOverride() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +OSDefineMetaClassAndStructors( FooSub, Foo ) + +int FooSub::foo() { + return 0; +} + +int FooSub::fooOverride() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h new file mode 100644 index 0000000..826eccc --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h @@ -0,0 +1,35 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + virtual int fooOverride(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; + +class FooSub : public Foo +{ + OSDeclareDefaultStructors( FooSub ) + +public: + virtual int foo(); + +#ifdef FOO_OVERRIDE + virtual int fooOverride(); +#endif + +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py b/testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py new file mode 100644 index 0000000..67a237c --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py @@ -0,0 +1,78 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it +# This is the same as kext-vtable-patching but checks that overrides of methods in parent classes +# are propagated to child classes +# Foo defines fooOverride(). + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching-overrides/main.kc", "/kext-vtable-patching-overrides/main.kernel", "/kext-vtable-patching-overrides/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x24F10" + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::fooOverride() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["name"] == "__ZN3Foo11fooOverrideEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["vmAddr"] == "0x26BA0" + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["vmAddr"] == "0x26B80" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][8]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][8]["vmAddr"] == "0x26BC0" + + # From foo + # FooSub::fooOverride() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][21]["name"] == "__ZN6FooSub11fooOverrideEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][21]["vmAddr"] == "0x26EA0" + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][22]["name"] == "__ZN6FooSub3fooEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][22]["vmAddr"] == "0x26E80" + + + # Check the fixups + kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x23150"] == "kc(0) + 0x26B80" + # Then the following fixup should be to Foo::fooOverride() + assert kernel_cache.dictionary()["fixups"]["0x23158"] == "kc(0) + 0x26BA0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x23160"] == "kc(0) + 0x26BC0" + + # In vtable for FooSub, we match the entry for FooSub::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x233A8"] == "kc(0) + 0x26E80" + # Then the following fixup should be to FooSub::fooOverride() + assert kernel_cache.dictionary()["fixups"]["0x233B0"] == "kc(0) + 0x26EA0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x233B8"] == "kc(0) + 0x26BC0" + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x21150"] == "kc(0) + 0x24F10" + # Then the following fixup should be to FooSub::fooOverride() + assert kernel_cache.dictionary()["fixups"]["0x21158"] == "kc(0) + 0x26EA0" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x21160"] == "kc(0) + 0x26BC0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 -DFOO_OVERRIDE=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar new file mode 100755 index 0000000..b3bd60b Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo new file mode 100755 index 0000000..5fe8196 Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/kext-vtable-patching/foo.h b/testing/kernel-cache-tests/kext-vtable-patching/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-vtable-patching/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-vtable-patching/test.py b/testing/kernel-cache-tests/kext-vtable-patching/test.py new file mode 100644 index 0000000..088713b --- /dev/null +++ b/testing/kernel-cache-tests/kext-vtable-patching/test.py @@ -0,0 +1,53 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching/main.kc", "/kext-vtable-patching/main.kernel", "/kext-vtable-patching/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x16F10" + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][6]["vmAddr"] == "0x17ED0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["vmAddr"] == "0x17EF0" + + + # Check the fixups + kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x1D3E0"] == "kc(0) + 0x17ED0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D3E8"] == "kc(0) + 0x17EF0" + + # Now in bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16F10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x17EF0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/bar.c b/testing/kernel-cache-tests/kext-weak-bind-chained/bar.c new file mode 100644 index 0000000..b8f2ff3 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/bar.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakValue = 0; + +int bar() { + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar new file mode 100755 index 0000000..76d9c80 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo new file mode 100755 index 0000000..47e2270 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/foo.c b/testing/kernel-cache-tests/kext-weak-bind-chained/foo.c new file mode 100644 index 0000000..b58a506 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/foo.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakValue = 0; + +int foo() { + return weakValue; +} diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/main.c b/testing/kernel-cache-tests/kext-weak-bind-chained/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel b/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/test.py b/testing/kernel-cache-tests/kext-weak-bind-chained/test.py new file mode 100644 index 0000000..90b5c57 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind-chained/test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that weak binds point to the same symbol + +# FIXME: This should be re-enabled once we know how to handle classic relocs combined with split seg v2. + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-weak-bind-chained/main.kc", "/kext-weak-bind-chained/main.kernel", "/kext-weak-bind-chained/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-weak-bind-chained/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Check the fixups + kernel_cache.analyze("/kext-weak-bind-chained/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x1C018" + assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo -Wl,-fixup_chains +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kext-weak-bind/bar.c b/testing/kernel-cache-tests/kext-weak-bind/bar.c new file mode 100644 index 0000000..b8f2ff3 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/bar.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakValue = 0; + +int bar() { + return weakValue; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar new file mode 100755 index 0000000..58085a5 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo new file mode 100755 index 0000000..6d0c0c5 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kext-weak-bind/foo.c b/testing/kernel-cache-tests/kext-weak-bind/foo.c new file mode 100644 index 0000000..b58a506 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/foo.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakValue = 0; + +int foo() { + return weakValue; +} diff --git a/testing/kernel-cache-tests/kext-weak-bind/main.c b/testing/kernel-cache-tests/kext-weak-bind/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kext-weak-bind/main.kernel b/testing/kernel-cache-tests/kext-weak-bind/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/main.kernel differ diff --git a/testing/kernel-cache-tests/kext-weak-bind/test.py b/testing/kernel-cache-tests/kext-weak-bind/test.py new file mode 100644 index 0000000..d8f1731 --- /dev/null +++ b/testing/kernel-cache-tests/kext-weak-bind/test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that weak binds point to the same symbol + +# FIXME: This should be re-enabled once we know how to handle classic relocs combined with split seg v2. + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kext-weak-bind/main.kc", "/kext-weak-bind/main.kernel", "/kext-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/kext-weak-bind/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Check the fixups + kernel_cache.analyze("/kext-weak-bind/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x1C018" + assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar new file mode 100755 index 0000000..02d2b6f Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo new file mode 100755 index 0000000..ad1d740 Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel differ diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py b/testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py new file mode 100644 index 0000000..6fcfdd2 --- /dev/null +++ b/testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py @@ -0,0 +1,26 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check errors from canBePlacedInKernelCollection() +# We use a lack of split seg here as arm64 requires it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/kexts-missing-split-seg-error/main.kc", "/kexts-missing-split-seg-error/main.kernel", "/kexts-missing-split-seg-error/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + assert len(kernel_cache.dictionary()) == 2 + # bar + assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "cannot be placed in kernel collection because: Missing split seg v2" + # foo + assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo" + assert len(kernel_cache.dictionary()[1]["errors"]) == 1 + assert kernel_cache.dictionary()[1]["errors"][0] == "cannot be placed in kernel collection because: Missing split seg v2" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -mios-version-min=8.0 foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -mios-version-min=8.0 bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kmod-info-errors/bar.c b/testing/kernel-cache-tests/kmod-info-errors/bar.c new file mode 100644 index 0000000..81ad71b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/bar.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext) + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar new file mode 100755 index 0000000..513e0bf Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo new file mode 100755 index 0000000..06c3cc8 Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kmod-info-errors/foo.c b/testing/kernel-cache-tests/kmod-info-errors/foo.c new file mode 100644 index 0000000..cddf937 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/foo.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext) + +int bar() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-errors/kmod.h b/testing/kernel-cache-tests/kmod-info-errors/kmod.h new file mode 100644 index 0000000..b2bc0d3 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/kmod.h @@ -0,0 +1,39 @@ + +#include + +typedef uint64_t vm_address_t; +typedef uint64_t vm_size_t; + +// Taken from kmod.h +#define KMOD_MAX_NAME 64 + +#pragma pack(push, 4) +typedef struct kmod_info { + struct kmod_info * next; + int32_t info_version; // version of this structure + uint32_t id; + char name[KMOD_MAX_NAME]; + char version[KMOD_MAX_NAME]; + int32_t reference_count; // # linkage refs to this + void * reference_list; // who this refs (links on) + vm_address_t address; // starting address + vm_size_t size; // total size + vm_size_t hdr_size; // unwired hdr size + void * start; + void * stop; +} kmod_info_t; +#pragma pack(pop) + +#define KMOD_INFO_NAME kmod_info +#define KMOD_INFO_VERSION 2 +#define KMOD_DECL(name, version) \ + static kmod_start_func_t name ## _module_start; \ + static kmod_stop_func_t name ## _module_stop; \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + name ## _module_start, \ + name ## _module_stop }; +#define KMOD_EXPLICIT_DECL(name, version, start, stop) \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + start, stop }; diff --git a/testing/kernel-cache-tests/kmod-info-errors/main.c b/testing/kernel-cache-tests/kmod-info-errors/main.c new file mode 100644 index 0000000..27b70cb --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/main.c @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB, __text")))) +int _start() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-errors/main.kernel b/testing/kernel-cache-tests/kmod-info-errors/main.kernel new file mode 100755 index 0000000..ab172f1 Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/main.kernel differ diff --git a/testing/kernel-cache-tests/kmod-info-errors/test.py b/testing/kernel-cache-tests/kmod-info-errors/test.py new file mode 100755 index 0000000..32767b4 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-errors/test.py @@ -0,0 +1,25 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Error for bad kmod info + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kmod-info-errors/main.kc", "/kmod-info-errors/main.kernel", "/kmod-info-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 2 + # bar + assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "unsupported kmod_info version of 2" + # foo + assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo" + assert len(kernel_cache.dictionary()[1]["errors"]) == 1 + assert kernel_cache.dictionary()[1]["errors"][0] == "unsupported kmod_info version of 2" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/bar.c b/testing/kernel-cache-tests/kmod-info-local-symbols/bar.c new file mode 100644 index 0000000..81ad71b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/bar.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext) + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar new file mode 100755 index 0000000..d8f704c Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo new file mode 100755 index 0000000..d809a0a Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/foo.c b/testing/kernel-cache-tests/kmod-info-local-symbols/foo.c new file mode 100644 index 0000000..cddf937 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/foo.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext) + +int bar() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h b/testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h new file mode 100644 index 0000000..eb5f782 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h @@ -0,0 +1,39 @@ + +#include + +typedef uint64_t vm_address_t; +typedef uint64_t vm_size_t; + +// Taken from kmod.h +#define KMOD_MAX_NAME 64 + +#pragma pack(push, 4) +typedef struct kmod_info { + struct kmod_info * next; + int32_t info_version; // version of this structure + uint32_t id; + char name[KMOD_MAX_NAME]; + char version[KMOD_MAX_NAME]; + int32_t reference_count; // # linkage refs to this + void * reference_list; // who this refs (links on) + vm_address_t address; // starting address + vm_size_t size; // total size + vm_size_t hdr_size; // unwired hdr size + void * start; + void * stop; +} kmod_info_t; +#pragma pack(pop) + +#define KMOD_INFO_NAME kmod_info +#define KMOD_INFO_VERSION 1 +#define KMOD_DECL(name, version) \ + static kmod_start_func_t name ## _module_start; \ + static kmod_stop_func_t name ## _module_stop; \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + name ## _module_start, \ + name ## _module_stop }; +#define KMOD_EXPLICIT_DECL(name, version, start, stop) \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + start, stop }; diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/main.c b/testing/kernel-cache-tests/kmod-info-local-symbols/main.c new file mode 100644 index 0000000..27b70cb --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/main.c @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB, __text")))) +int _start() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel b/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel new file mode 100755 index 0000000..5c6c23a Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel differ diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/test.py b/testing/kernel-cache-tests/kmod-info-local-symbols/test.py new file mode 100755 index 0000000..2d76234 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info-local-symbols/test.py @@ -0,0 +1,55 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Update the kmod_info entry for the files here + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kmod-info-local-symbols/main.kc", "/kmod-info-local-symbols/main.kernel", "/kmod-info-local-symbols/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the layout + kernel_cache.analyze("/kmod-info-local-symbols/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Find the address and size of each kext in the layout and check that these match their kmod info values + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x205000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmSize"] == "0xFF8" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x206000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmSize"] == "0xFF8" + + # Check the kmod info + kernel_cache.analyze("/kmod-info-local-symbols/main.kc", ["-kmod", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()) == 3 + assert kernel_cache.dictionary()[0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()[0]["kmod_info"] == "none" + + # bar.kext + assert kernel_cache.dictionary()[1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()[1]["kmod_info"]["info-version"] == "1" + assert kernel_cache.dictionary()[1]["kmod_info"]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()[1]["kmod_info"]["version"] == "1.0.0" + assert kernel_cache.dictionary()[1]["kmod_info"]["address"] == "0x205000" + assert kernel_cache.dictionary()[1]["kmod_info"]["size"] == "0xFF8" + + # foo.kext + assert kernel_cache.dictionary()[2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()[2]["kmod_info"]["info-version"] == "1" + assert kernel_cache.dictionary()[2]["kmod_info"]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()[2]["kmod_info"]["version"] == "1.0.0" + assert kernel_cache.dictionary()[2]["kmod_info"]["address"] == "0x206000" + assert kernel_cache.dictionary()[2]["kmod_info"]["size"] == "0xFF8" + +# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/kmod-info/bar.c b/testing/kernel-cache-tests/kmod-info/bar.c new file mode 100644 index 0000000..81ad71b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/bar.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext) + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar new file mode 100755 index 0000000..69d6139 Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo new file mode 100755 index 0000000..8a6ecfc Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/kmod-info/foo.c b/testing/kernel-cache-tests/kmod-info/foo.c new file mode 100644 index 0000000..cddf937 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/foo.c @@ -0,0 +1,15 @@ + +#include "kmod.h" + +int startKext() { + return 0; +} +int endKext() { + return 0; +} + +KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext) + +int bar() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info/kmod.h b/testing/kernel-cache-tests/kmod-info/kmod.h new file mode 100644 index 0000000..eb5f782 --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/kmod.h @@ -0,0 +1,39 @@ + +#include + +typedef uint64_t vm_address_t; +typedef uint64_t vm_size_t; + +// Taken from kmod.h +#define KMOD_MAX_NAME 64 + +#pragma pack(push, 4) +typedef struct kmod_info { + struct kmod_info * next; + int32_t info_version; // version of this structure + uint32_t id; + char name[KMOD_MAX_NAME]; + char version[KMOD_MAX_NAME]; + int32_t reference_count; // # linkage refs to this + void * reference_list; // who this refs (links on) + vm_address_t address; // starting address + vm_size_t size; // total size + vm_size_t hdr_size; // unwired hdr size + void * start; + void * stop; +} kmod_info_t; +#pragma pack(pop) + +#define KMOD_INFO_NAME kmod_info +#define KMOD_INFO_VERSION 1 +#define KMOD_DECL(name, version) \ + static kmod_start_func_t name ## _module_start; \ + static kmod_stop_func_t name ## _module_stop; \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + name ## _module_start, \ + name ## _module_stop }; +#define KMOD_EXPLICIT_DECL(name, version, start, stop) \ + kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U, \ + { #name }, { version }, -1, 0, 0, 0, 0, \ + start, stop }; diff --git a/testing/kernel-cache-tests/kmod-info/main.c b/testing/kernel-cache-tests/kmod-info/main.c new file mode 100644 index 0000000..27b70cb --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/main.c @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB, __text")))) +int _start() { + return 0; +} diff --git a/testing/kernel-cache-tests/kmod-info/main.kernel b/testing/kernel-cache-tests/kmod-info/main.kernel new file mode 100755 index 0000000..ab172f1 Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/main.kernel differ diff --git a/testing/kernel-cache-tests/kmod-info/test.py b/testing/kernel-cache-tests/kmod-info/test.py new file mode 100755 index 0000000..22d204d --- /dev/null +++ b/testing/kernel-cache-tests/kmod-info/test.py @@ -0,0 +1,55 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Update the kmod_info entry for the files here + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/kmod-info/main.kc", "/kmod-info/main.kernel", "/kmod-info/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the layout + kernel_cache.analyze("/kmod-info/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Find the address and size of each kext in the layout and check that these match their kmod info values + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x205000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmSize"] == "0xFF8" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x206000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmSize"] == "0xFF8" + + # Check the kmod info + kernel_cache.analyze("/kmod-info/main.kc", ["-kmod", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()) == 3 + assert kernel_cache.dictionary()[0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()[0]["kmod_info"] == "none" + + # bar.kext + assert kernel_cache.dictionary()[1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()[1]["kmod_info"]["info-version"] == "1" + assert kernel_cache.dictionary()[1]["kmod_info"]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()[1]["kmod_info"]["version"] == "1.0.0" + assert kernel_cache.dictionary()[1]["kmod_info"]["address"] == "0x205000" + assert kernel_cache.dictionary()[1]["kmod_info"]["size"] == "0xFF8" + + # foo.kext + assert kernel_cache.dictionary()[2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()[2]["kmod_info"]["info-version"] == "1" + assert kernel_cache.dictionary()[2]["kmod_info"]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()[2]["kmod_info"]["version"] == "1.0.0" + assert kernel_cache.dictionary()[2]["kmod_info"]["address"] == "0x206000" + assert kernel_cache.dictionary()[2]["kmod_info"]["size"] == "0xFF8" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/large-auxkc-errors/bar.c b/testing/kernel-cache-tests/large-auxkc-errors/bar.c new file mode 100644 index 0000000..afee5a2 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/bar.c @@ -0,0 +1,9 @@ + +__attribute__((used)) +char largeBuffer[64 * 1024 * 1024]; + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar new file mode 100755 index 0000000..a3f42db Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo new file mode 100755 index 0000000..f0115a4 Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/large-auxkc-errors/foo.c b/testing/kernel-cache-tests/large-auxkc-errors/foo.c new file mode 100644 index 0000000..dd18f9a --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/foo.c @@ -0,0 +1,7 @@ + +__attribute__((used)) +char largeBuffer[32 * 1024 * 1024]; + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/large-auxkc-errors/main.c b/testing/kernel-cache-tests/large-auxkc-errors/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/large-auxkc-errors/main.kernel b/testing/kernel-cache-tests/large-auxkc-errors/main.kernel new file mode 100755 index 0000000..a46bfb5 Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/main.kernel differ diff --git a/testing/kernel-cache-tests/large-auxkc-errors/test.py b/testing/kernel-cache-tests/large-auxkc-errors/test.py new file mode 100644 index 0000000..fbbcef5 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-errors/test.py @@ -0,0 +1,24 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# The arm64e auxKC has a lower memory limit than other KCs. Verify that we get an error with only 64MB in there. + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64e", "/large-auxkc-errors/main.kc", "/large-auxkc-errors/main.kernel", "/large-auxkc-errors/extensions", [], []) + kernel_cache.analyze("/large-auxkc-errors/main.kc", ["-layout", "-arch", "arm64e"]) + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64e", "/large-auxkc-errors/aux.kc", "/large-auxkc-errors/main.kc", "", "/large-auxkc-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert "kernel collection size exceeds maximum size of 67108864" in kernel_cache.dictionary()[0] + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/bar.c b/testing/kernel-cache-tests/large-auxkc-no-errors/bar.c new file mode 100644 index 0000000..64c7316 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/bar.c @@ -0,0 +1,9 @@ + +__attribute__((used)) +char largeBuffer[512 * 1024 * 1024]; + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar new file mode 100755 index 0000000..a52f0f5 Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo new file mode 100755 index 0000000..f557435 Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/foo.c b/testing/kernel-cache-tests/large-auxkc-no-errors/foo.c new file mode 100644 index 0000000..607bfa0 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/foo.c @@ -0,0 +1,7 @@ + +__attribute__((used)) +char largeBuffer[400 * 1024 * 1024]; + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/main.c b/testing/kernel-cache-tests/large-auxkc-no-errors/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel b/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel differ diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/test.py b/testing/kernel-cache-tests/large-auxkc-no-errors/test.py new file mode 100644 index 0000000..acca4be --- /dev/null +++ b/testing/kernel-cache-tests/large-auxkc-no-errors/test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# The non-auxKC can still be 1GB. + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/large-auxkc-no-errors/main.kc", "/large-auxkc-no-errors/main.kernel", "/large-auxkc-no-errors/extensions", [], []) + kernel_cache.analyze("/large-auxkc-no-errors/main.kc", ["-layout", "-arch", "arm64"]) + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("arm64", "/large-auxkc-no-errors/aux.kc", "/large-auxkc-no-errors/main.kc", "", "/large-auxkc-no-errors/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/large-auxkc-no-errors/main.kc", ["-layout", "-arch", "arm64"]) + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/large-kcs-errors/bar.c b/testing/kernel-cache-tests/large-kcs-errors/bar.c new file mode 100644 index 0000000..93f5249 --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/bar.c @@ -0,0 +1,7 @@ + +__attribute__((used)) +char largeBuffer[512 * 1024 * 1024]; + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar new file mode 100755 index 0000000..9e23135 Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo new file mode 100755 index 0000000..3d568d9 Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/large-kcs-errors/foo.c b/testing/kernel-cache-tests/large-kcs-errors/foo.c new file mode 100644 index 0000000..6428460 --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/foo.c @@ -0,0 +1,7 @@ + +__attribute__((used)) +char largeBuffer[512 * 1024 * 1024]; + +int bar() { + return 0; +} diff --git a/testing/kernel-cache-tests/large-kcs-errors/main.c b/testing/kernel-cache-tests/large-kcs-errors/main.c new file mode 100644 index 0000000..27b70cb --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/main.c @@ -0,0 +1,5 @@ + +__attribute__((section(("__HIB, __text")))) +int _start() { + return 0; +} diff --git a/testing/kernel-cache-tests/large-kcs-errors/main.kernel b/testing/kernel-cache-tests/large-kcs-errors/main.kernel new file mode 100755 index 0000000..ab172f1 Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/main.kernel differ diff --git a/testing/kernel-cache-tests/large-kcs-errors/test.py b/testing/kernel-cache-tests/large-kcs-errors/test.py new file mode 100755 index 0000000..6c0109a --- /dev/null +++ b/testing/kernel-cache-tests/large-kcs-errors/test.py @@ -0,0 +1,17 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Error for bad kmod info + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/large-kcs-errors/main.kc", "/large-kcs-errors/main.kernel", "/large-kcs-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"]) + + assert len(kernel_cache.dictionary()) == 1 + assert "kernel collection size exceeds maximum size of 1073741824" in kernel_cache.dictionary()[0] + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/last-data-const/bar.c b/testing/kernel-cache-tests/last-data-const/bar.c new file mode 100644 index 0000000..3f90848 --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/bar.c @@ -0,0 +1,8 @@ + +int pack = 0; + +extern int foo(); + +int bar() { + return foo() + pack; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar new file mode 100755 index 0000000..5d8a757 Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo new file mode 100755 index 0000000..af1710e Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/last-data-const/foo.c b/testing/kernel-cache-tests/last-data-const/foo.c new file mode 100644 index 0000000..acd9563 --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/foo.c @@ -0,0 +1,6 @@ + +int pack; + +int foo() { + return pack; +} diff --git a/testing/kernel-cache-tests/last-data-const/main.c b/testing/kernel-cache-tests/last-data-const/main.c new file mode 100644 index 0000000..5bdda59 --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/main.c @@ -0,0 +1,7 @@ + +int x = 0; +int* g = &x; + +int _start() { + return *g; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/last-data-const/main.kernel b/testing/kernel-cache-tests/last-data-const/main.kernel new file mode 100755 index 0000000..f4ca636 Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/main.kernel differ diff --git a/testing/kernel-cache-tests/last-data-const/test.py b/testing/kernel-cache-tests/last-data-const/test.py new file mode 100644 index 0000000..9e54ad0 --- /dev/null +++ b/testing/kernel-cache-tests/last-data-const/test.py @@ -0,0 +1,49 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Last data const should be folded in under data const and allowed to have fixups + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/last-data-const/main.kc", "/last-data-const/main.kernel", "/last-data-const/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/last-data-const/main.kc", ["-layout", "-arch", "arm64"]) + + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LASTDATA_CONST" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Check we have the correct addresses of the symbols being bound to + kernel_cache.analyze("/last-data-const/main.kc", ["-symbols", "-arch", "arm64"]) + # main.kernel + # int g = &x; + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000" + # foo.kext + # foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030" + + kernel_cache.analyze("/last-data-const/main.kc", ["-fixups", "-arch", "arm64"]) + # main.kernel + # int g = &x; + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000" + # foo.kext + # foo() + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__LASTDATA_CONST,__data -Wl,-segprot,__LASTDATA_CONST,r--,r-- main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/packed-kext-data/bar.c b/testing/kernel-cache-tests/packed-kext-data/bar.c new file mode 100644 index 0000000..3f90848 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/bar.c @@ -0,0 +1,8 @@ + +int pack = 0; + +extern int foo(); + +int bar() { + return foo() + pack; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar new file mode 100755 index 0000000..5d8a757 Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo new file mode 100755 index 0000000..af1710e Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/packed-kext-data/foo.c b/testing/kernel-cache-tests/packed-kext-data/foo.c new file mode 100644 index 0000000..acd9563 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/foo.c @@ -0,0 +1,6 @@ + +int pack; + +int foo() { + return pack; +} diff --git a/testing/kernel-cache-tests/packed-kext-data/main.c b/testing/kernel-cache-tests/packed-kext-data/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/packed-kext-data/main.kernel b/testing/kernel-cache-tests/packed-kext-data/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/main.kernel differ diff --git a/testing/kernel-cache-tests/packed-kext-data/test.py b/testing/kernel-cache-tests/packed-kext-data/test.py new file mode 100644 index 0000000..8f763af --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-data/test.py @@ -0,0 +1,36 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# __DATA should be packed at sub-page alignment +# But only in the kexts. The kernel pages should not be packed + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/packed-kext-data/main.kc", "/packed-kext-data/main.kernel", "/packed-kext-data/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/packed-kext-data/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 7 + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000" + assert kernel_cache.dictionary()["cache-segments"][5]["vmSize"] == "0x4000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x20000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmSize"] == "0x4" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x20004" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmSize"] == "0x4" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/packed-kext-text/bar.c b/testing/kernel-cache-tests/packed-kext-text/bar.c new file mode 100644 index 0000000..f9cdb9b --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/bar.c @@ -0,0 +1,10 @@ + +// Hack to force a section on __TEXT as otherwise its given the vmSize by forEachSegment +__attribute__((used, section("__TEXT, __const"))) +int packHack = 0; + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar new file mode 100755 index 0000000..3e06336 Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo new file mode 100755 index 0000000..0a125c3 Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/packed-kext-text/foo.c b/testing/kernel-cache-tests/packed-kext-text/foo.c new file mode 100644 index 0000000..1911bd9 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/foo.c @@ -0,0 +1,8 @@ + +// Hack to force a section on __TEXT as otherwise its given the vmSize by forEachSegment +__attribute__((used, section("__TEXT, __const"))) +int packHack = 0; + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/packed-kext-text/main.c b/testing/kernel-cache-tests/packed-kext-text/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/packed-kext-text/main.kernel b/testing/kernel-cache-tests/packed-kext-text/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/main.kernel differ diff --git a/testing/kernel-cache-tests/packed-kext-text/test.py b/testing/kernel-cache-tests/packed-kext-text/test.py new file mode 100644 index 0000000..22672a3 --- /dev/null +++ b/testing/kernel-cache-tests/packed-kext-text/test.py @@ -0,0 +1,39 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# __DATA should be packed at sub-page alignment +# But only in the kexts. The kernel pages should not be packed + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/packed-kext-text/main.kc", "/packed-kext-text/main.kernel", "/packed-kext-text/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/packed-kext-text/main.kc", ["-layout", "-arch", "arm64"]) + + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["cache-segments"][2]["vmSize"] == "0xC000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmSize"] == "0x4000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x10000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmSize"] == "0x20" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x10020" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmSize"] == "0xC" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar new file mode 100755 index 0000000..7eafc00 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel differ diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py new file mode 100644 index 0000000..baf91e5 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py @@ -0,0 +1,32 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can bind to another kext +# foo.kext exports foo and bar.kext uses it + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/pageablekc-bind-to-basekc/main.kc", "/pageablekc-bind-to-basekc/main.kernel", "/pageablekc-bind-to-basekc/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/pageablekc-bind-to-basekc/main.kc", ["-symbols", "-arch", "arm64"]) + + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"] == "0x10000" + + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/pageablekc-bind-to-basekc/pageable.kc", "/pageablekc-bind-to-basekc/main.kc", "/pageablekc-bind-to-basekc/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/pageablekc-bind-to-basekc/pageable.kc", ["-fixups", "-arch", "arm64"]) + + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1 + # extern int foo() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0xC000"] == "kc(0) + 0x10000" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/pageablekc-uuid/bar.c b/testing/kernel-cache-tests/pageablekc-uuid/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/pageablekc-uuid/foo.c b/testing/kernel-cache-tests/pageablekc-uuid/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/pageablekc-uuid/main.c b/testing/kernel-cache-tests/pageablekc-uuid/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-uuid/main.kernel b/testing/kernel-cache-tests/pageablekc-uuid/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/main.kernel differ diff --git a/testing/kernel-cache-tests/pageablekc-uuid/test.py b/testing/kernel-cache-tests/pageablekc-uuid/test.py new file mode 100644 index 0000000..8322d15 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-uuid/test.py @@ -0,0 +1,32 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Verify that an pageableKC references the UUID of the base KC + +def check(kernel_cache): + # First build a kernel collection + kernel_cache.buildKernelCollection("arm64", "/pageablekc-uuid/main.kc", "/pageablekc-uuid/main.kernel", "/pageablekc-uuid/extensions", [], []) + kernel_cache.analyze("/pageablekc-uuid/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the kernel UUID + kernel_cache.analyze("/pageablekc-uuid/main.kc", ["-uuid", "-arch", "arm64"]) + kernelUUID = kernel_cache.dictionary()["uuid"] + assert kernelUUID != "00000000-0000-0000-0000-000000000000" + assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"] + + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("arm64", "/pageablekc-uuid/pageable.kc", "/pageablekc-uuid/main.kc", "/pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], []) + + # Check the pageable UUID + kernel_cache.analyze("/pageablekc-uuid/pageable.kc", ["-uuid", "-arch", "arm64"]) + assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000" + assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist new file mode 100644 index 0000000..57b9a95 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist @@ -0,0 +1,48 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + __ZN11OSMetaClass + + + SymbolPrefix + __ZNK11OSMetaClass + + + SymbolPrefix + __ZN15OSMetaClassBase + + + SymbolPrefix + __ZNK15OSMetaClassBase + + + SymbolPrefix + __ZN8OSObject + + + SymbolPrefix + __ZNK8OSObject + + + SymbolPrefix + __ZTV8OSObject + + + + + + diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp new file mode 100644 index 0000000..a87b825 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp @@ -0,0 +1,21 @@ + +#include "foo.h" + +class Bar : public Foo +{ + OSDeclareDefaultStructors( Bar ) + +public: + virtual int foo(); +}; + +OSDefineMetaClassAndStructors( Bar, Foo ) + +int Bar::foo() { + return 1; +} + +int bar() { + Bar* bar = new Bar(); + return bar->foo(); +} diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..911622b --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar new file mode 100755 index 0000000..0bdcc52 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo new file mode 100755 index 0000000..5fe8196 Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp new file mode 100644 index 0000000..83e5e67 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp @@ -0,0 +1,23 @@ + +#include "foo.h" + +OSDefineMetaClassAndStructors( Foo, OSObject ) + +// Index 0 has been replaced with a method +OSMetaClassDefineReservedUsed(Foo, 0) +OSMetaClassDefineReservedUnused( Foo, 1 ) +OSMetaClassDefineReservedUnused( Foo, 2 ) +OSMetaClassDefineReservedUnused( Foo, 3 ) + +int Foo::foo() { + return 0; +} + +int Foo::fooUsed0() { + return 0; +} + +int foo() { + Foo* foo = new Foo(); + return foo->foo() + foo->fooUsed0(); +} diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h new file mode 100644 index 0000000..31d6216 --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h @@ -0,0 +1,21 @@ + +#include +#include + +class Foo : public OSObject +{ + OSDeclareDefaultStructors( Foo ) + +public: + virtual int foo(); + +#ifdef FOO_USED + OSMetaClassDeclareReservedUsed(Foo, 0); + virtual int fooUsed0(); +#else + OSMetaClassDeclareReservedUnused(Foo, 0); +#endif + OSMetaClassDeclareReservedUnused(Foo, 1); + OSMetaClassDeclareReservedUnused(Foo, 2); + OSMetaClassDeclareReservedUnused(Foo, 3); +}; \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp new file mode 100644 index 0000000..b689f8a --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp @@ -0,0 +1,119 @@ + +#include +#include + +int __cxa_pure_virtual = 0; + +#if 0 +class OSObject : public OSMetaClassBase +{ + OSDeclareAbstractStructors(OSObject); + +public: + OSMetaClassDeclareReservedUnused(OSObject, 0); + OSMetaClassDeclareReservedUnused(OSObject, 1); + OSMetaClassDeclareReservedUnused(OSObject, 2); + OSMetaClassDeclareReservedUnused(OSObject, 3); + OSMetaClassDeclareReservedUnused(OSObject, 4); + OSMetaClassDeclareReservedUnused(OSObject, 5); + OSMetaClassDeclareReservedUnused(OSObject, 6); + OSMetaClassDeclareReservedUnused(OSObject, 7); + OSMetaClassDeclareReservedUnused(OSObject, 8); + OSMetaClassDeclareReservedUnused(OSObject, 9); + OSMetaClassDeclareReservedUnused(OSObject, 10); + OSMetaClassDeclareReservedUnused(OSObject, 11); + OSMetaClassDeclareReservedUnused(OSObject, 12); + OSMetaClassDeclareReservedUnused(OSObject, 13); + OSMetaClassDeclareReservedUnused(OSObject, 14); + OSMetaClassDeclareReservedUnused(OSObject, 15); +}; +#endif + +// OSDefineMetaClassAndAbstractStructors(OSObject, 0); +/* Class global data */ +OSObject::MetaClass OSObject::gMetaClass; +const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass; +const OSMetaClass * const OSObject::superClass = NULL; + +// Virtual Padding +OSMetaClassDefineReservedUnused(OSObject, 0); +OSMetaClassDefineReservedUnused(OSObject, 1); +OSMetaClassDefineReservedUnused(OSObject, 2); +OSMetaClassDefineReservedUnused(OSObject, 3); +OSMetaClassDefineReservedUnused(OSObject, 4); +OSMetaClassDefineReservedUnused(OSObject, 5); +OSMetaClassDefineReservedUnused(OSObject, 6); +OSMetaClassDefineReservedUnused(OSObject, 7); +OSMetaClassDefineReservedUnused(OSObject, 8); +OSMetaClassDefineReservedUnused(OSObject, 9); +OSMetaClassDefineReservedUnused(OSObject, 10); +OSMetaClassDefineReservedUnused(OSObject, 11); +OSMetaClassDefineReservedUnused(OSObject, 12); +OSMetaClassDefineReservedUnused(OSObject, 13); +OSMetaClassDefineReservedUnused(OSObject, 14); +OSMetaClassDefineReservedUnused(OSObject, 15); + +// struct IORPC { }; + +// OSMetaClassBase +OSMetaClassBase::OSMetaClassBase() { } +OSMetaClassBase::~OSMetaClassBase() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { } +void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { } +kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; } +bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; } + +// OSMetaClass +OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { } +OSMetaClass::~OSMetaClass() { } +void OSMetaClass::reservedCalled(int ind) const { } +void OSMetaClass::retain() const { } +void OSMetaClass::release() const { } +void OSMetaClass::release(int freeWhen) const { } +int OSMetaClass::getRetainCount() const { return 0; } +const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; } +void OSMetaClass::taggedRetain(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag) const { } +void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; } +void OSMetaClass::instanceConstructed() const { } + +// OSMetaClass reserved methods +void OSMetaClass::_RESERVEDOSMetaClass0() { } +void OSMetaClass::_RESERVEDOSMetaClass1() { } +void OSMetaClass::_RESERVEDOSMetaClass2() { } +void OSMetaClass::_RESERVEDOSMetaClass3() { } +void OSMetaClass::_RESERVEDOSMetaClass4() { } +void OSMetaClass::_RESERVEDOSMetaClass5() { } +void OSMetaClass::_RESERVEDOSMetaClass6() { } +void OSMetaClass::_RESERVEDOSMetaClass7() { } + +// OSObject::MetaClass +OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { } +OSObject *OSObject::MetaClass::alloc() const { return NULL; } +kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; } + +// OSObject +OSObject::OSObject(OSMetaClass const*) { } +OSObject::~OSObject() { } +const OSMetaClass* OSObject::getMetaClass() const { return NULL; } +void OSObject::free() { } +bool OSObject::init() { return false; } +void OSObject::retain() const { } +void OSObject::release() const { } +void OSObject::release(int freeWhen) const { } +int OSObject::getRetainCount() const { return 0; } +void OSObject::taggedRetain(const void * tag) const { } +void OSObject::taggedRelease(const void * tag) const { } +void OSObject::taggedRelease(const void * tag, const int freeWhen) const { } +bool OSObject::serialize(OSSerialize * serializer) const { return false; } +kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; } +void* OSObject::operator new(unsigned long) { return (void*)1; } +void OSObject::operator delete(void*, unsigned long) { return; } + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel new file mode 100755 index 0000000..119060f Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel differ diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/test.py b/testing/kernel-cache-tests/pageablekc-vtable-patching/test.py new file mode 100644 index 0000000..bcf847d --- /dev/null +++ b/testing/kernel-cache-tests/pageablekc-vtable-patching/test.py @@ -0,0 +1,69 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that a kext can patch vtables against another kext +# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/pageablekc-vtable-patching/main.kc", "/pageablekc-vtable-patching/main.kernel", "/pageablekc-vtable-patching/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"]) + + # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable + # Foo::foo() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0" + # Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0" + + + # Check the fixups + kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"]) + + # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup + assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0" + # Then the following fixup should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0" + + + # ----------------------------------------------------------- + # Now build an pageable cache using the baseline kernel collection + kernel_cache.buildPageableKernelCollection("x86_64", "/pageablekc-vtable-patching/pageable.kc", "/pageablekc-vtable-patching/main.kc", "/pageablekc-vtable-patching/extensions", ["com.apple.bar"], []) + kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + + + # Get the addresses for the symbols we are looking at. This will make it easier to work out the fixup slots + kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-symbols", "-arch", "x86_64"]) + + # From bar, find the vtable and its override of foo() + # Bar::foo() + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x4F10" + + + # Check the fixups + kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-fixups", "-arch", "x86_64"]) + + # In bar, again match the entry for its Bar::foo() symbol + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x8150"] == "kc(1) + 0x4F10" + # And if the patching was correct, then following entry should be to Foo::fooUsed0() + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x8158"] == "kc(0) + 0x12EF0" + + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/plugins/bar.c b/testing/kernel-cache-tests/plugins/bar.c new file mode 100644 index 0000000..691611e --- /dev/null +++ b/testing/kernel-cache-tests/plugins/bar.c @@ -0,0 +1,12 @@ + +int g = 0; + +int bar() { + return g; +} + +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist new file mode 100644 index 0000000..77b3de8 --- /dev/null +++ b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar new file mode 100755 index 0000000..4467cee Binary files /dev/null and b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo b/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo new file mode 100755 index 0000000..1e2e3c3 Binary files /dev/null and b/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/plugins/foo.c b/testing/kernel-cache-tests/plugins/foo.c new file mode 100644 index 0000000..43ab00e --- /dev/null +++ b/testing/kernel-cache-tests/plugins/foo.c @@ -0,0 +1,7 @@ + +int g = 0; +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/plugins/main.c b/testing/kernel-cache-tests/plugins/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/plugins/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/plugins/main.kernel b/testing/kernel-cache-tests/plugins/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/plugins/main.kernel differ diff --git a/testing/kernel-cache-tests/plugins/test.py b/testing/kernel-cache-tests/plugins/test.py new file mode 100644 index 0000000..1d788bd --- /dev/null +++ b/testing/kernel-cache-tests/plugins/test.py @@ -0,0 +1,22 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that we look in the foo.kext Plugins subdirectory for more kexts + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/plugins/main.kc", "/plugins/main.kernel", "/plugins/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/plugins/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/foo.kext/PlugIns/bar.kext/bar +# [~]> rm -r extensions/foo.kext/*.ld +# [~]> rm -r extensions/foo.kext/PlugIns/bar.kext/*.ld + diff --git a/testing/kernel-cache-tests/ppl-data-const/bar.c b/testing/kernel-cache-tests/ppl-data-const/bar.c new file mode 100644 index 0000000..3f90848 --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/bar.c @@ -0,0 +1,8 @@ + +int pack = 0; + +extern int foo(); + +int bar() { + return foo() + pack; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar new file mode 100755 index 0000000..5d8a757 Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo new file mode 100755 index 0000000..af1710e Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/ppl-data-const/foo.c b/testing/kernel-cache-tests/ppl-data-const/foo.c new file mode 100644 index 0000000..acd9563 --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/foo.c @@ -0,0 +1,6 @@ + +int pack; + +int foo() { + return pack; +} diff --git a/testing/kernel-cache-tests/ppl-data-const/main.c b/testing/kernel-cache-tests/ppl-data-const/main.c new file mode 100644 index 0000000..5bdda59 --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/main.c @@ -0,0 +1,7 @@ + +int x = 0; +int* g = &x; + +int _start() { + return *g; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/ppl-data-const/main.kernel b/testing/kernel-cache-tests/ppl-data-const/main.kernel new file mode 100755 index 0000000..b6b70b1 Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/main.kernel differ diff --git a/testing/kernel-cache-tests/ppl-data-const/test.py b/testing/kernel-cache-tests/ppl-data-const/test.py new file mode 100644 index 0000000..7a5c775 --- /dev/null +++ b/testing/kernel-cache-tests/ppl-data-const/test.py @@ -0,0 +1,49 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# PPL data const should be folded in under data const and allowed to have fixups + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/ppl-data-const/main.kc", "/ppl-data-const/main.kernel", "/ppl-data-const/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/ppl-data-const/main.kc", ["-layout", "-arch", "arm64"]) + + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000" + assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__PPLDATA_CONST" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + + # Check we have the correct addresses of the symbols being bound to + kernel_cache.analyze("/ppl-data-const/main.kc", ["-symbols", "-arch", "arm64"]) + # main.kernel + # int g = &x; + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000" + # foo.kext + # foo() + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030" + + kernel_cache.analyze("/ppl-data-const/main.kc", ["-fixups", "-arch", "arm64"]) + # main.kernel + # int g = &x; + assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000" + # foo.kext + # foo() + assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__PPLDATA_CONST,__data -Wl,-segprot,__PPLDATA_CONST,r--,r-- main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/rwx-permissions-error/main.c b/testing/kernel-cache-tests/rwx-permissions-error/main.c new file mode 100644 index 0000000..f6263e6 --- /dev/null +++ b/testing/kernel-cache-tests/rwx-permissions-error/main.c @@ -0,0 +1,7 @@ + +__attribute__((section(("__RWX, __data")))) +int data = 1; + +int _start() { + return data; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/rwx-permissions-error/main.kernel b/testing/kernel-cache-tests/rwx-permissions-error/main.kernel new file mode 100755 index 0000000..d6e4643 Binary files /dev/null and b/testing/kernel-cache-tests/rwx-permissions-error/main.kernel differ diff --git a/testing/kernel-cache-tests/rwx-permissions-error/test.py b/testing/kernel-cache-tests/rwx-permissions-error/test.py new file mode 100644 index 0000000..22a7bf0 --- /dev/null +++ b/testing/kernel-cache-tests/rwx-permissions-error/test.py @@ -0,0 +1,19 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check errors from canBePlacedInKernelCollection() +# All arm64* binaries cannot use RWX permissions + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/rwx-permissions-error/main.kc", "/rwx-permissions-error/main.kernel", "", [], ["-json-errors"]) + assert len(kernel_cache.dictionary()) == 1 + # kernel + assert kernel_cache.dictionary()[0]["id"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()[0]["errors"]) == 1 + assert kernel_cache.dictionary()[0]["errors"][0] == "cannot be placed in kernel collection because: Segments are not allowed to be both writable and executable" + + +# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -segprot __RWX rwx rwx main.c -o main.kernel + diff --git a/testing/kernel-cache-tests/strip-all-kexts/bar.c b/testing/kernel-cache-tests/strip-all-kexts/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-all-kexts/foo.c b/testing/kernel-cache-tests/strip-all-kexts/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/strip-all-kexts/main.c b/testing/kernel-cache-tests/strip-all-kexts/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-all-kexts/main.kernel b/testing/kernel-cache-tests/strip-all-kexts/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-all-kexts/test.py b/testing/kernel-cache-tests/strip-all-kexts/test.py new file mode 100644 index 0000000..b9b78f0 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all-kexts/test.py @@ -0,0 +1,35 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that we stripped all the symbols from kexts but the kernel is still not strippped + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-all-kexts/main.kc", "/strip-all-kexts/main.kernel", "/strip-all-kexts/extensions", ["com.apple.foo", "com.apple.bar"], ["-strip-all-kexts"]) + kernel_cache.analyze("/strip-all-kexts/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-all-kexts/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/strip-all/bar.c b/testing/kernel-cache-tests/strip-all/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-all/foo.c b/testing/kernel-cache-tests/strip-all/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/strip-all/main.c b/testing/kernel-cache-tests/strip-all/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-all/main.kernel b/testing/kernel-cache-tests/strip-all/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-all/test.py b/testing/kernel-cache-tests/strip-all/test.py new file mode 100644 index 0000000..da241ec --- /dev/null +++ b/testing/kernel-cache-tests/strip-all/test.py @@ -0,0 +1,33 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that we stripped all the symbols + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-all/main.kc", "/strip-all/main.kernel", "/strip-all/extensions", ["com.apple.foo", "com.apple.bar"], ["-strip-all"]) + kernel_cache.analyze("/strip-all/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-all/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/strip-kexts-all/bar.c b/testing/kernel-cache-tests/strip-kexts-all/bar.c new file mode 100644 index 0000000..d79e410 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/bar.c @@ -0,0 +1,7 @@ + +extern int foo(); +static int y; + +int bar() { + return foo() + y; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar new file mode 100755 index 0000000..df109bd Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo new file mode 100755 index 0000000..b374232 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-kexts-all/foo.c b/testing/kernel-cache-tests/strip-kexts-all/foo.c new file mode 100644 index 0000000..8470d7f --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/foo.c @@ -0,0 +1,6 @@ + +static int x; + +int foo() { + return x; +} diff --git a/testing/kernel-cache-tests/strip-kexts-all/main.c b/testing/kernel-cache-tests/strip-kexts-all/main.c new file mode 100644 index 0000000..b1f7ec6 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/main.c @@ -0,0 +1,6 @@ + +static int z; + +int _start() { + return z; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-all/main.kernel b/testing/kernel-cache-tests/strip-kexts-all/main.kernel new file mode 100755 index 0000000..f70d9d7 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-kexts-all/test.py b/testing/kernel-cache-tests/strip-kexts-all/test.py new file mode 100644 index 0000000..6f18849 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-all/test.py @@ -0,0 +1,38 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check fine grained stripping of locals and exports + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-kexts-all/main.kc", "/strip-kexts-all/main.kernel", "/strip-kexts-all/extensions", ["com.apple.foo:all", "com.apple.bar"], []) + kernel_cache.analyze("/strip-kexts-all/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-kexts-all/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/strip-kexts-exports/bar.c b/testing/kernel-cache-tests/strip-kexts-exports/bar.c new file mode 100644 index 0000000..d79e410 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/bar.c @@ -0,0 +1,7 @@ + +extern int foo(); +static int y; + +int bar() { + return foo() + y; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar new file mode 100755 index 0000000..df109bd Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo new file mode 100755 index 0000000..b374232 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-kexts-exports/foo.c b/testing/kernel-cache-tests/strip-kexts-exports/foo.c new file mode 100644 index 0000000..8470d7f --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/foo.c @@ -0,0 +1,6 @@ + +static int x; + +int foo() { + return x; +} diff --git a/testing/kernel-cache-tests/strip-kexts-exports/main.c b/testing/kernel-cache-tests/strip-kexts-exports/main.c new file mode 100644 index 0000000..b1f7ec6 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/main.c @@ -0,0 +1,6 @@ + +static int z; + +int _start() { + return z; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-exports/main.kernel b/testing/kernel-cache-tests/strip-kexts-exports/main.kernel new file mode 100755 index 0000000..f70d9d7 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-kexts-exports/test.py b/testing/kernel-cache-tests/strip-kexts-exports/test.py new file mode 100644 index 0000000..fe8998e --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-exports/test.py @@ -0,0 +1,39 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check fine grained stripping of exports + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-kexts-exports/main.kc", "/strip-kexts-exports/main.kernel", "/strip-kexts-exports/extensions", ["com.apple.foo:exports", "com.apple.bar"], []) + kernel_cache.analyze("/strip-kexts-exports/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-kexts-exports/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none" + assert len(kernel_cache.dictionary()["dylibs"][2]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"][0]["name"] == "_x" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/strip-kexts-locals/bar.c b/testing/kernel-cache-tests/strip-kexts-locals/bar.c new file mode 100644 index 0000000..d79e410 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/bar.c @@ -0,0 +1,7 @@ + +extern int foo(); +static int y; + +int bar() { + return foo() + y; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar new file mode 100755 index 0000000..df109bd Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo new file mode 100755 index 0000000..b374232 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-kexts-locals/foo.c b/testing/kernel-cache-tests/strip-kexts-locals/foo.c new file mode 100644 index 0000000..8470d7f --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/foo.c @@ -0,0 +1,6 @@ + +static int x; + +int foo() { + return x; +} diff --git a/testing/kernel-cache-tests/strip-kexts-locals/main.c b/testing/kernel-cache-tests/strip-kexts-locals/main.c new file mode 100644 index 0000000..b1f7ec6 --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/main.c @@ -0,0 +1,6 @@ + +static int z; + +int _start() { + return z; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-kexts-locals/main.kernel b/testing/kernel-cache-tests/strip-kexts-locals/main.kernel new file mode 100755 index 0000000..f70d9d7 Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-kexts-locals/test.py b/testing/kernel-cache-tests/strip-kexts-locals/test.py new file mode 100644 index 0000000..bfbe0dd --- /dev/null +++ b/testing/kernel-cache-tests/strip-kexts-locals/test.py @@ -0,0 +1,39 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check fine grained stripping of locals + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-kexts-locals/main.kc", "/strip-kexts-locals/main.kernel", "/strip-kexts-locals/extensions", ["com.apple.foo:locals", "com.apple.bar"], []) + kernel_cache.analyze("/strip-kexts-locals/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-kexts-locals/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/strip-none/bar.c b/testing/kernel-cache-tests/strip-none/bar.c new file mode 100644 index 0000000..2ad0d8d --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/bar.c @@ -0,0 +1,6 @@ + +extern int foo(); + +int bar() { + return foo(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar new file mode 100755 index 0000000..6288d91 Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo new file mode 100755 index 0000000..cd1fa31 Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/strip-none/foo.c b/testing/kernel-cache-tests/strip-none/foo.c new file mode 100644 index 0000000..5a6eec2 --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/foo.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/kernel-cache-tests/strip-none/main.c b/testing/kernel-cache-tests/strip-none/main.c new file mode 100644 index 0000000..7e49911 --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/main.c @@ -0,0 +1,4 @@ + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/strip-none/main.kernel b/testing/kernel-cache-tests/strip-none/main.kernel new file mode 100755 index 0000000..9f1a437 Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/main.kernel differ diff --git a/testing/kernel-cache-tests/strip-none/test.py b/testing/kernel-cache-tests/strip-none/test.py new file mode 100644 index 0000000..0d09db0 --- /dev/null +++ b/testing/kernel-cache-tests/strip-none/test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Check that the kernel and kexts still have all their symbols as we didn't strip them + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/strip-none/main.kc", "/strip-none/main.kernel", "/strip-none/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/strip-none/main.kc", ["-layout", "-arch", "arm64"]) + + # Check the symbols + kernel_cache.analyze("/strip-none/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none" + # bar + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar" + assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none" + # foo + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["global-symbols"]) == 1 + assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo" + assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none" + + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar +# [~]> rm -r extensions/*.kext/*.ld + diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist new file mode 100644 index 0000000..88c91cc --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist @@ -0,0 +1,30 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.bar + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _symbol_from_bar + AliasTarget + _symbol_from_xnu + + + SymbolName + _symbol_from_xnu_no_alias + + + + + + diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..77b3de8 --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist new file mode 100644 index 0000000..683493e --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + baz + CFBundleIdentifier + com.apple.baz + CFBundleName + baz + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..0865794 --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + com.apple.baz + 1.0 + + + diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo new file mode 100755 index 0000000..9bbe62d Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c new file mode 100644 index 0000000..50a560e --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c @@ -0,0 +1,7 @@ + +extern int symbol_from_bar(); +extern int symbol_from_xnu_no_alias(); + +int foo() { + return symbol_from_bar() + symbol_from_xnu_no_alias(); +} diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c new file mode 100644 index 0000000..5b3791c --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c @@ -0,0 +1,14 @@ + +// This will be re-exported from a symbol set in bar with an alias +int symbol_from_xnu() { + return 0; +} + +// This will be re-exported from a symbol set in bar without an alias +int symbol_from_xnu_no_alias() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel new file mode 100755 index 0000000..62811b7 Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel differ diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py new file mode 100644 index 0000000..b8ee22b --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py @@ -0,0 +1,41 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Test that the codeless bar.kext (with bundle id com.apple.bar) doesn't interfere with foo using the symbol set of the same bundle id + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/symbol-sets-and-codeless-kexts/main.kc", "/symbol-sets-and-codeless-kexts/main.kernel", "/symbol-sets-and-codeless-kexts/extensions", ["com.apple.foo", "com.apple.bar", "com.apple.baz"], []) + kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000" + + # Find the address of the symbols to bind to + kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu_no_alias" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C" + + # Check the fixups + kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000" + assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist new file mode 100644 index 0000000..e0fe9ad --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.symbols + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolPrefix + _symbol_from_xnu + + + + + + diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..237c06f --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.symbols + 1.0 + + + diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo new file mode 100755 index 0000000..ca3e060 Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/foo.c b/testing/kernel-cache-tests/symbol-sets-prefix/foo.c new file mode 100644 index 0000000..1ec0ceb --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-prefix/foo.c @@ -0,0 +1,9 @@ + +extern int symbol_from_xnu0(); +extern int symbol_from_xnu1(); +extern int symbol_from_xnu2(); +extern int symbol_from_xnu3(); + +int foo() { + return symbol_from_xnu0() + symbol_from_xnu1() + symbol_from_xnu2() + symbol_from_xnu3(); +} diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/main.c b/testing/kernel-cache-tests/symbol-sets-prefix/main.c new file mode 100644 index 0000000..389d828 --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-prefix/main.c @@ -0,0 +1,21 @@ + +int symbol_from_xnu0() { + return 0; +} + +int symbol_from_xnu1() { + return 0; +} + +int symbol_from_xnu2() { + return 0; +} + +int symbol_from_xnu3() { + return 0; +} + + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel b/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel new file mode 100755 index 0000000..a6f279b Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel differ diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/test.py b/testing/kernel-cache-tests/symbol-sets-prefix/test.py new file mode 100644 index 0000000..26ff1da --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets-prefix/test.py @@ -0,0 +1,49 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# Symbol sets can have a prefix with an implicit * wildcard on the end which re-exports anything from xnu with that name + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/symbol-sets-prefix/main.kc", "/symbol-sets-prefix/main.kernel", "/symbol-sets-prefix/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000" + + # Check the symbols + kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-symbols", "-arch", "arm64"]) + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 6 + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu0" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu1" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][4]["name"] == "_symbol_from_xnu2" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][4]["vmAddr"] == "0xC018" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][5]["name"] == "_symbol_from_xnu3" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][5]["vmAddr"] == "0xC024" + + # Check the fixups + kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 4 + assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000" + assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C" + assert kernel_cache.dictionary()["fixups"]["0x14010"] == "kc(0) + 0xC018" + assert kernel_cache.dictionary()["fixups"]["0x14018"] == "kc(0) + 0xC024" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/kernel-cache-tests/symbol-sets/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets/SymbolSets.plist new file mode 100644 index 0000000..88c91cc --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets/SymbolSets.plist @@ -0,0 +1,30 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.bar + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + _symbol_from_bar + AliasTarget + _symbol_from_xnu + + + SymbolName + _symbol_from_xnu_no_alias + + + + + + diff --git a/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..1111344 --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo new file mode 100755 index 0000000..9bbe62d Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/symbol-sets/foo.c b/testing/kernel-cache-tests/symbol-sets/foo.c new file mode 100644 index 0000000..50a560e --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets/foo.c @@ -0,0 +1,7 @@ + +extern int symbol_from_bar(); +extern int symbol_from_xnu_no_alias(); + +int foo() { + return symbol_from_bar() + symbol_from_xnu_no_alias(); +} diff --git a/testing/kernel-cache-tests/symbol-sets/main.c b/testing/kernel-cache-tests/symbol-sets/main.c new file mode 100644 index 0000000..5b3791c --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets/main.c @@ -0,0 +1,14 @@ + +// This will be re-exported from a symbol set in bar with an alias +int symbol_from_xnu() { + return 0; +} + +// This will be re-exported from a symbol set in bar without an alias +int symbol_from_xnu_no_alias() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/symbol-sets/main.kernel b/testing/kernel-cache-tests/symbol-sets/main.kernel new file mode 100755 index 0000000..62811b7 Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets/main.kernel differ diff --git a/testing/kernel-cache-tests/symbol-sets/test.py b/testing/kernel-cache-tests/symbol-sets/test.py new file mode 100644 index 0000000..f2cb88c --- /dev/null +++ b/testing/kernel-cache-tests/symbol-sets/test.py @@ -0,0 +1,42 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# This verifies that we use the symbol set when resolving symbols in to the kernel +# Note symbol sets are the plist which is embedded in to the kernel + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/symbol-sets/main.kc", "/symbol-sets/main.kernel", "/symbol-sets/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/symbol-sets/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000" + + # Find the address of the symbols to bind to + kernel_cache.analyze("/symbol-sets/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu_no_alias" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C" + + # Check the fixups + kernel_cache.analyze("/symbol-sets/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000" + assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/kernel-cache-tests/testall.py b/testing/kernel-cache-tests/testall.py new file mode 100755 index 0000000..fd7cfba --- /dev/null +++ b/testing/kernel-cache-tests/testall.py @@ -0,0 +1,47 @@ +#!/usr/bin/python2.7 + +import string +import os +import json +import sys +import imp +import os.path +import traceback + +sys.dont_write_bytecode = True + +import KernelCollection + + +if __name__ == "__main__": + test_dir = os.path.realpath(os.path.dirname(__file__)) + sys.path.append(test_dir) + all_tests = os.listdir(test_dir) + all_tests.sort() + test_to_run = "" + if len(sys.argv) == 2: + test_to_run = sys.argv[1] + all_tests = [ test_to_run ] + for f in all_tests: + test_case = test_dir + "/" + f + "/test.py" + if os.path.isfile(test_case): + py_mod = imp.load_source(f, test_case) + check_func = getattr(py_mod, "check", 0) + if check_func == 0: + print "FAIL: " + f + ", missing check() function"; + else: + try: + kernelCollection = KernelCollection.KernelCollection(test_to_run != "") + check_func(kernelCollection) + print "PASS: " + f + except AssertionError, e: + _, _, tb = sys.exc_info() + tb_info = traceback.extract_tb(tb) + filename, line, func, text = tb_info[-1] + print "FAIL: " + f + ", " + text + except KeyError, e: + _, _, tb = sys.exc_info() + tb_info = traceback.extract_tb(tb) + filename, line, func, text = tb_info[-1] + print "FAIL: " + f + ", " + text + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c new file mode 100644 index 0000000..2cf9969 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c @@ -0,0 +1,13 @@ + +int g = 0; + +int bar() { + return g; +} + +__attribute__((section(("__TEXT, __text")))) +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar new file mode 100755 index 0000000..7b04223 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo new file mode 100755 index 0000000..e333729 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c new file mode 100644 index 0000000..67edd8a --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c @@ -0,0 +1,9 @@ + +int g = 0; + +__attribute__((section(("__TEXT, __text")))) +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp new file mode 100644 index 0000000..b3811a2 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp @@ -0,0 +1,36 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + char k; + int *p2; // aligned to 1 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret"); + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel new file mode 100755 index 0000000..f2ebee2 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py new file mode 100644 index 0000000..07f44b8 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py @@ -0,0 +1,66 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name): + for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]: + if symbol_and_addr["name"] == symbol_name: + return symbol_and_addr["vmAddr"] + return None + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/text-fixups-x86_64-auxkc/main.kc", "/text-fixups-x86_64-auxkc/main.kernel", "/text-fixups-x86_64-auxkc/extensions", [], []) + kernel_cache.analyze("/text-fixups-x86_64-auxkc/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + + # Now build an aux cache using the baseline kernel collection + kernel_cache.buildAuxKernelCollection("x86_64", "/text-fixups-x86_64-auxkc/aux.kc", "/text-fixups-x86_64-auxkc/main.kc", "", "/text-fixups-x86_64-auxkc/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-layout", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x8000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x9000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xC000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xA000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xB000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xC000" + + # Find the sybmols the fixups point to + kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-symbols", "-arch", "x86_64"]) + barAddress = findGlobalSymbolVMAddr(kernel_cache, 0, "_bar") + gAddress = findGlobalSymbolVMAddr(kernel_cache, 1, "_g") + + # Check the fixups + kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + + # bar.kext + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1 + assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x4FF0"] == "kc(3) + " + barAddress + + # foo.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + + assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1 + assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x4FF0"] == "kc(3) + " + gAddress + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-read_only_relocs,suppress -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/bar.c b/testing/kernel-cache-tests/text-fixups-x86_64/bar.c new file mode 100644 index 0000000..2cf9969 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/bar.c @@ -0,0 +1,13 @@ + +int g = 0; + +int bar() { + return g; +} + +__attribute__((section(("__TEXT, __text")))) +__typeof(&bar) barPtr = &bar; + +int baz() { + return barPtr(); +} diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..2eee003 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.foo + 1.0 + + + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar new file mode 100755 index 0000000..7b04223 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..f379e8b --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + + diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo new file mode 100755 index 0000000..e333729 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/foo.c b/testing/kernel-cache-tests/text-fixups-x86_64/foo.c new file mode 100644 index 0000000..67edd8a --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/foo.c @@ -0,0 +1,9 @@ + +int g = 0; + +__attribute__((section(("__TEXT, __text")))) +int* gPtr = &g; + +int foo() { + return *gPtr; +} diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/main.cpp b/testing/kernel-cache-tests/text-fixups-x86_64/main.cpp new file mode 100644 index 0000000..b3811a2 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/main.cpp @@ -0,0 +1,36 @@ + +int g = 0; + +static int func() { + return g; +} + +struct S { + __typeof(&func) funcPtr; + __typeof(&func) funcPtr2; + int *p1; + __attribute__((aligned((16384)))) __typeof(&func) funcPtr3; + int *p2; +}; + +S s = { &func, &func, &g, &func, &g }; + +struct __attribute__((packed)) PackedS { + int i; + __typeof(&func) funcPtr; // aligned to 4 + __typeof(&func) funcPtr2; // aligned to 4 + int j; + int *p1; // aligned to 8 + char k; + int *p2; // aligned to 1 +}; + +__attribute__((aligned((16384)))) +PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + +__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret"); + +__attribute__((section(("__HIB, __text")))) +extern "C" int _start() { + return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr(); +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel b/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel new file mode 100755 index 0000000..f2ebee2 Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel differ diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/test.py b/testing/kernel-cache-tests/text-fixups-x86_64/test.py new file mode 100644 index 0000000..412f8a2 --- /dev/null +++ b/testing/kernel-cache-tests/text-fixups-x86_64/test.py @@ -0,0 +1,102 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + + +def check(kernel_cache): + kernel_cache.buildKernelCollection("x86_64", "/text-fixups-x86_64/main.kc", "/text-fixups-x86_64/main.kernel", "/text-fixups-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], []) + kernel_cache.analyze("/text-fixups-x86_64/main.kc", ["-layout", "-arch", "x86_64"]) + + assert len(kernel_cache.dictionary()["cache-segments"]) == 11 + assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000" + assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT" + assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC" + assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO" + assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000" + assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA" + assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB" + assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__REGION0" + assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000" + assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__REGION1" + assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0xFFFFFF8000219000" + assert kernel_cache.dictionary()["cache-segments"][8]["name"] == "__REGION2" + assert kernel_cache.dictionary()["cache-segments"][8]["vmAddr"] == "0xFFFFFF800021A000" + assert kernel_cache.dictionary()["cache-segments"][9]["name"] == "__REGION3" + assert kernel_cache.dictionary()["cache-segments"][9]["vmAddr"] == "0xFFFFFF800021B000" + assert kernel_cache.dictionary()["cache-segments"][10]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["cache-segments"][10]["vmAddr"] == "0xFFFFFF800021C000" + + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + # main.kernel + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4 + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF800021C000" + # bar.kext + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000218000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000219000" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF800021C000" + # foo.kext + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3 + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF800021A000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF800021B000" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT" + assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF800021C000" + + # Check the fixups + kernel_cache.analyze("/text-fixups-x86_64/main.kc", ["-fixups", "-arch", "x86_64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 12 + # main.kernel: S s = { &func, &func, &g, &func, &g }; + # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g }; + # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64" + # bar.kext: __typeof(&bar) barPtr = &bar; + # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x118FF0"] == "kc(0) + 0xFFFFFF8000218FD0" + # foo.kext: int* gPtr = &g; + # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x11AFF0"] == "kc(0) + 0xFFFFFF800021B000" + # main.kernel: movl _foo, %esp + # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB + assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32" + assert len(kernel_cache.dictionary()["dylibs"]) == 3 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none" + +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-read_only_relocs,suppress -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo +# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar + diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist new file mode 100644 index 0000000..e8397f4 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist @@ -0,0 +1,24 @@ + + + + + SymbolsSets + + + CFBundleIdentifier + com.apple.bar + CFBundleVersion + ###KERNEL_VERSION_LONG### + OSBundleCompatibleVersion + 8.0.0b1 + Symbols + + + SymbolName + __ZN15OSMetaClassBase8DispatchE5IORPC + + + + + + diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..1111344 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist @@ -0,0 +1,19 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + foo + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + + diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo new file mode 100755 index 0000000..5bed34f Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo differ diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist new file mode 100644 index 0000000..29f0ea3 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist @@ -0,0 +1 @@ +files/foo-f6e015.o diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o new file mode 100644 index 0000000..6f02100 Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o differ diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command new file mode 100644 index 0000000..2f98bd6 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command @@ -0,0 +1,10 @@ +/Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/bin/ld +-Z +-demangle +-lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/lib/libLTO.dylib +-no_deduplicate +-dynamic +-arch arm64 +-iphoneos_version_min 14.0.0 +-kext +-add_split_seg_info diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line new file mode 100644 index 0000000..1643a33 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line @@ -0,0 +1,2 @@ +cd /Users/pete/dyld/testing/kernel-cache-tests/vtable-patching-metaclass-hack +/Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/bin/ld -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch arm64 -iphoneos_version_min 14.0.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.Internal.sdk -o extensions/foo.kext/foo -kext -add_split_seg_info /var/folders/m5/2d3gpq6x53bfdgfshsfxqxv40000gn/T/foo-f6e015.o diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c new file mode 100644 index 0000000..5b7b9be --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c @@ -0,0 +1,10 @@ + +// This is the symbol xnu now exports +extern int symbol_from_xnu() __asm("__ZN15OSMetaClassBase8DispatchE5IORPC"); + +// And this is the old symbol it needs to implicitly alias to the above symbol +extern int symbol_from_xnu_implicit_alias() __asm("__ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev"); + +int foo() { + return symbol_from_xnu() + symbol_from_xnu_implicit_alias(); +} diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c new file mode 100644 index 0000000..e1e3eb5 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c @@ -0,0 +1,10 @@ + +// This will be re-exported from a symbol set in bar with an alias +int symbol_from_xnu() __asm("__ZN15OSMetaClassBase8DispatchE5IORPC"); +int symbol_from_xnu() { + return 0; +} + +int _start() { + return 0; +} \ No newline at end of file diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel new file mode 100755 index 0000000..a99ed2a Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel differ diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py new file mode 100644 index 0000000..78d4645 --- /dev/null +++ b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py @@ -0,0 +1,39 @@ +#!/usr/bin/python2.7 + +import os +import KernelCollection + +# kxld has an implicit alias for a metaclass vtable entry. Test that we also rewrite that alias + +def check(kernel_cache): + kernel_cache.buildKernelCollection("arm64", "/vtable-patching-metaclass-alias/main.kc", "/vtable-patching-metaclass-alias/main.kernel", "/vtable-patching-metaclass-alias/extensions", ["com.apple.foo"], []) + kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-layout", "-arch", "arm64"]) + + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST" + assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000" + + # Find the address of the symbols to bind to + kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-symbols", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__ZN15OSMetaClassBase8DispatchE5IORPC" + assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["vmAddr"] == "0xC000" + + # Check the fixups + kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-fixups", "-arch", "arm64"]) + assert len(kernel_cache.dictionary()["fixups"]) == 2 + assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000" + assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC000" + assert len(kernel_cache.dictionary()["dylibs"]) == 2 + assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel" + assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none" + assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo" + assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none" + +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- +# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo +# [~]> rm -r extensions/foo.kext/*.ld + diff --git a/testing/nocr/execserver.defs b/testing/lib/execserver.defs similarity index 96% rename from testing/nocr/execserver.defs rename to testing/lib/execserver.defs index e528df4..69468ba 100644 --- a/testing/nocr/execserver.defs +++ b/testing/lib/execserver.defs @@ -1 +1,2 @@ #include + diff --git a/testing/lib/test_support.cpp b/testing/lib/test_support.cpp new file mode 100644 index 0000000..f4b9bff --- /dev/null +++ b/testing/lib/test_support.cpp @@ -0,0 +1,754 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +extern "C" { +#include "execserverServer.h" + +union catch_mach_exc_request_reply { + union __RequestUnion__catch_mach_exc_subsystem request; + union __ReplyUnion__catch_mach_exc_subsystem reply; +}; +}; + +#include "test_support.h" + +extern const int NXArgc; +extern const char** NXArgv; +extern const char** environ; +extern char* __progname; +#if __x86_64__ +static const cpu_type_t currentArch = CPU_TYPE_X86_64; +#elif __i386__ +static const cpu_type_t currentArch = CPU_TYPE_I386; +#elif __arm64__ +static const cpu_type_t currentArch = CPU_TYPE_ARM64; +#elif __arm__ +static const cpu_type_t currentArch = CPU_TYPE_ARM; +#endif + +namespace { +struct ScopedLock { + ScopedLock() : _lock(OS_UNFAIR_LOCK_INIT) {} + template + void withLock(F f) { + os_unfair_lock_lock(&_lock); + f(); + os_unfair_lock_unlock(&_lock); + } +private: + os_unfair_lock _lock; +}; + +template +class 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 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]; } + 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 +inline void GrowableArray::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 +inline void GrowableArray::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; +} + +struct TestState { + TestState(); + static TestState* getState(); + void _PASSV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn)); + void _FAILV(const char* file, unsigned line, const char* format, va_list args) __attribute__ ((noreturn)); + void _LOGV(const char* file, unsigned line, const char* format, va_list args); + GrowableArray>& getCrashHandlers(); +private: + enum OutputStyle { + None, + BATS, + Console, + XCTest + }; + void emitBegin(); + void runLeaks(); + void dumpLogs(); + void getLogsString(char** buffer); + static uint8_t hexCharToUInt(const char hexByte, uint8_t* value); + static uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte); + + ScopedLock _IOlock; + GrowableArray logs; + const char *testName; + bool logImmediate; + bool logOnSuccess; + bool checkForLeaks; + OutputStyle output; + GrowableArray> crashHandlers; +}; + +// Okay, this is tricky. We need something with roughly he semantics of a weak def, but without using weak defs as their presence +// may impact certain tests. Instead we do the following: +// +// 1. Embed a stuct containing a lock and a pointer to our global state object in each binary +// 2. Once per binary we walk the entire image list looking for the first entry that also has state data +// 3. If it has state we lock its initializaion lock, and if it is not initialized we initialize it +// 4. We then copy the initalized pointer into our own state, and unlock the initializer lock +// +// This should work because the image list forms a stable ordering. The one loose end is if an executable is running where logging +// is only used in dylibs that are all being dlopned() and dlclosed. Since many dylibs cannot be dlclosed that should be a non-issue +// in practice. +}; + +__attribute__((section("__DATA,__dyld_test"))) +static std::atomic sState; + +kern_return_t +catch_mach_exception_raise(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt) +{ + _dyld_test_crash_handler_t crashHandler = NULL; + for (const auto& handler : TestState::getState()->getCrashHandlers()) { + if (handler.first == exception_port) { + crashHandler = handler.second; + } + } + if (crashHandler) { + if (exception == EXC_CORPSE_NOTIFY) { + crashHandler(task); + } else { + return KERN_FAILURE; + } + } + return KERN_SUCCESS; +} + +kern_return_t +catch_mach_exception_raise_state(mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + return KERN_NOT_SUPPORTED; +} + +kern_return_t +catch_mach_exception_raise_state_identity(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + return KERN_NOT_SUPPORTED; +} + +_process::_process() : executablePath(nullptr), args(nullptr), env(nullptr), stdoutHandler(nullptr), stderrHandler(nullptr), + crashHandler(nullptr), exitHandler(nullptr), pid(0), arch(currentArch), suspended(false) {} +_process::~_process() { + if (stdoutHandler) { Block_release(stdoutHandler);} + if (stderrHandler) { Block_release(stderrHandler);} + if (crashHandler) { Block_release(crashHandler);} + if (exitHandler) { Block_release(exitHandler);} +} + +void _process::set_executable_path(const char* EP) { executablePath = EP; } +void _process::set_args(const char** A) { args = A; } +void _process::set_env(const char** E) { env = E; } +void _process::set_stdout_handler(_dyld_test_reader_t SOH) { stdoutHandler = Block_copy(SOH); }; +void _process::set_stderr_handler(_dyld_test_reader_t SEH) { stderrHandler = Block_copy(SEH); } +void _process::set_exit_handler(_dyld_test_exit_handler_t EH) { exitHandler = Block_copy(EH); } +void _process::set_crash_handler(_dyld_test_crash_handler_t CH) { crashHandler = Block_copy(CH); } +void _process::set_launch_suspended(bool S) { suspended = S; } +void _process::set_launch_arch(cpu_type_t A) { arch = A; } + +pid_t _process::launch() { + dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.launch", NULL); + posix_spawn_file_actions_t fileActions = NULL; + posix_spawnattr_t attr = NULL; + dispatch_source_t stdoutSource = NULL; + dispatch_source_t stderrSource = NULL; + int stdoutPipe[2]; + int stderrPipe[2]; + + if (posix_spawn_file_actions_init(&fileActions) != 0) { + FAIL("Setting up spawn filea actions"); + } + if (posix_spawnattr_init(&attr) != 0) { FAIL("Setting up spawn attr"); } + if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0) { + FAIL("Setting up spawn attr: POSIX_SPAWN_START_SUSPENDED"); + } + + if (pipe(stdoutPipe) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[0]) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_adddup2(&fileActions, stdoutPipe[1], STDOUT_FILENO) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_addclose(&fileActions, stdoutPipe[1]) != 0) { FAIL("Setting up pipe"); } + fcntl((int)stdoutPipe[0], F_SETFL, O_NONBLOCK); + stdoutSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stdoutPipe[0], 0, queue); + dispatch_source_set_event_handler(stdoutSource, ^{ + int fd = (int)dispatch_source_get_handle(stdoutSource); + if (stdoutHandler) { + stdoutHandler(fd); + } else { + char buffer[16384]; + ssize_t size = 0; + do { + size = read(fd, &buffer[0], 16384); + } while (size > 0); + } + }); + dispatch_source_set_cancel_handler(stdoutSource, ^{ + dispatch_release(stdoutSource); + }); + dispatch_resume(stdoutSource); + + if (pipe(stderrPipe) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[0]) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_adddup2(&fileActions, stderrPipe[1], STDERR_FILENO) != 0) { FAIL("Setting up pipe"); } + if (posix_spawn_file_actions_addclose(&fileActions, stderrPipe[1]) != 0) { FAIL("Setting up pipe"); } + fcntl((int)stderrPipe[0], F_SETFL, O_NONBLOCK); + stderrSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, (uintptr_t)stderrPipe[0], 0, queue); + dispatch_source_set_event_handler(stderrSource, ^{ + int fd = (int)dispatch_source_get_handle(stderrSource); + if (stderrHandler) { + stderrHandler(fd); + } else { + char buffer[16384]; + ssize_t size = 0; + do { + size = read(fd, &buffer[0], 16384); + } while (size > 0); + } + }); + dispatch_source_set_cancel_handler(stderrSource, ^{ + dispatch_release(stderrSource); + }); + dispatch_resume(stderrSource); + + if (crashHandler) { + auto& crashHandlers = TestState::getState()->getCrashHandlers(); + mach_port_t exceptionPort = MACH_PORT_NULL; + mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT | MPO_INSERT_SEND_RIGHT, .mpl = { 1 }}; + if ( mach_port_construct(mach_task_self(), &options, (mach_port_context_t)exceptionPort, &exceptionPort) != KERN_SUCCESS ) { + FAIL("Could not construct port"); + } + if (posix_spawnattr_setexceptionports_np(&attr, EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exceptionPort, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0) != 0) { + FAIL("posix_spawnattr_setexceptionports_np failed"); + } + crashHandlers.push_back(std::make_pair(exceptionPort, crashHandler)); + dispatch_source_t crashSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, exceptionPort, 0, queue); + dispatch_source_set_event_handler(crashSource, ^{ + dispatch_mig_server(crashSource, sizeof(union catch_mach_exc_request_reply), ::mach_exc_server); + }); + dispatch_source_set_cancel_handler(crashSource, ^{ + mach_port_destruct(mach_task_self(), exceptionPort, 0, (mach_port_context_t)exceptionPort); + }); + dispatch_resume(crashSource); + } + + pid_t pid; + uint32_t argc = 0; + if (args) { + for (argc = 0; args[argc] != NULL; ++argc) {} + } + ++argc; + const char *argv[argc+1]; + argv[0] = executablePath; + for (uint32_t i = 1; i < argc; ++i) { + argv[i] = args[i-1]; + } + argv[argc] = NULL; + + int result = posix_spawn(&pid, executablePath, &fileActions, &attr, (char **)argv, (char **)env); + if ( result != 0 ) { + FAIL("posix_spawn(%s) failed, err=%d", executablePath, result); + } + dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (pid_t)pid, + DISPATCH_PROC_EXIT, queue); + dispatch_source_set_event_handler(exitSource, ^{ + if (exitHandler) { + exitHandler((pid_t)dispatch_source_get_handle(exitSource)); + } + dispatch_source_cancel(exitSource); + if (stdoutSource) { + dispatch_source_cancel(stdoutSource); + } + if (stderrSource) { + dispatch_source_cancel(stderrSource); + } + dispatch_source_cancel(exitSource); + }); + dispatch_resume(exitSource); + + if (stdoutHandler) { + close(stdoutPipe[1]); + } + if (stderrHandler) { + close(stderrPipe[1]); + } + if (fileActions) { + posix_spawn_file_actions_destroy(&fileActions); + } + posix_spawnattr_destroy(&attr); + if (!suspended) { + kill(pid, SIGCONT); + } + dispatch_release(queue); + return pid; +} + +void *_process::operator new(size_t size) { + return malloc(size); +} + +void _process::operator delete(void *ptr) { + free(ptr); +} + +// MARK: Private implementation details + +template +static +void forEachEnvVar(const char* envp[], F&& f) { + for (uint32_t i = 0; envp[i] != nullptr; ++i) { + const char* envBegin = envp[i]; + const char* envEnd = strchr(envp[i], '='); + if (!envEnd) { continue; } + size_t envSize = (envEnd-envBegin)+1; + const char* valBegin = envEnd+1; + const char* valEnd = strchr(envp[i], '\0'); + if (!valEnd) { continue; } + size_t valSize = (valEnd-valBegin)+1; + char env[envSize]; + char val[valSize]; + strlcpy(&env[0], envBegin, envSize); + strlcpy(&val[0], valBegin, valSize); + f(&env[0], &val[0]); + } +} + +uint8_t TestState::hexCharToUInt(const char hexByte, uint8_t* value) { + if (hexByte >= '0' && hexByte <= '9') { + *value = hexByte - '0'; + return true; + } else if (hexByte >= 'A' && hexByte <= 'F') { + *value = hexByte - 'A' + 10; + return true; + } else if (hexByte >= 'a' && hexByte <= 'f') { + *value = hexByte - 'a' + 10; + return true; + } + + return false; +} + +uint64_t TestState::hexToUInt64(const char* startHexByte, const char** endHexByte) { + const char* scratch; + if (endHexByte == NULL) { + endHexByte = &scratch; + } + if (startHexByte == NULL) + return 0; + uint64_t retval = 0; + if (startHexByte[0] == '0' && startHexByte[1] == 'x') { + startHexByte +=2; + } + *endHexByte = startHexByte + 16; + + //FIXME overrun? + for (uint32_t i = 0; i < 16; ++i) { + uint8_t value; + if (!hexCharToUInt(startHexByte[i], &value)) { + *endHexByte = &startHexByte[i]; + break; + } + retval = (retval << 4) + value; + } + return retval; +} + +void TestState::getLogsString(char** buffer) +{ + char *logBuf = NULL; + if ( logs.count() ) { + size_t idx = 0; + size_t bufSize = 0; + for (const auto& log : logs) { + size_t logSize = strlen(log); + bufSize += logSize + 2; // \t and \n + logBuf = (char*)realloc(logBuf, bufSize); + strncpy(logBuf+idx, "\t", 1); + idx++; + strncpy(logBuf+idx, log, logSize); + idx += logSize; + strncpy(logBuf+idx, "\n", 1); + idx++; + } + logBuf = (char*)realloc(logBuf, bufSize + 1); + logBuf[bufSize] = '\0'; + *buffer = logBuf; + } +} + +TestState::TestState() : testName(__progname), logImmediate(false), logOnSuccess(false), checkForLeaks(false), output(Console) { + forEachEnvVar(environ, [this](const char* env, const char* val) { + if (strcmp(env, "TEST_LOG_IMMEDIATE") == 0) { + logImmediate = true; + } + if (strcmp(env, "TEST_LOG_ON_SUCCESS") == 0) { + logOnSuccess = true; + } + if (strcmp(env, "MallocStackLogging") == 0) { + checkForLeaks = true; + } + if (strcmp(env, "TEST_OUTPUT") == 0) { + if (strcmp(val, "BATS") == 0) { + output = BATS; + } else if (strcmp(val, "XCTest") == 0) { + output = XCTest; + } + } + }); +} + +void TestState::emitBegin() { + if (output == BATS) { + printf("[BEGIN]"); + if (checkForLeaks) { + printf(" MallocStackLogging=1 MallocDebugReport=none"); + } + forEachEnvVar(environ, [this](const char* env, const char* val) { + if ((strncmp(env, "DYLD_", 5) == 0) || (strncmp(env, "TEST_", 5) == 0)) { + printf(" %s=%s", env, val); + } + }); + printf(" %s", testName); + for (uint32_t i = 1; i < NXArgc; ++i) { + printf(" %s", NXArgv[i]); + } + printf("\n"); + } +} + +GrowableArray>& TestState::getCrashHandlers() { + return crashHandlers; +} + +TestState* TestState::getState() { + if (!sState) { + uint32_t imageCnt = _dyld_image_count(); + for (uint32_t i = 0; i < imageCnt; ++i) { + #if __LP64__ + const struct mach_header_64* mh = (const struct mach_header_64*)_dyld_get_image_header(i); + #else + const struct mach_header* mh = _dyld_get_image_header(i); + #endif + if (mh->filetype != MH_EXECUTE) { + continue; + } + size_t size = 0; + auto state = (std::atomic*)getsectiondata(mh, "__DATA", "__dyld_test", &size); +// fprintf(stderr, "__dyld_test -> 0x%llx\n", state); + if (!state) { + fprintf(stderr, "Could not find test state in main executable TestState\n"); + exit(0); + } + if (*state == nullptr) { + void *temp = malloc(sizeof(TestState)); + auto newState = new (temp) TestState(); + TestState* expected = nullptr; + if(!state->compare_exchange_strong(expected, newState)) { + newState->~TestState(); + free(temp); + } else { + newState->emitBegin(); + } + } + sState.store(*state); + } + } + assert(sState != nullptr); + return sState; +} + +__attribute__((noreturn)) +void TestState::runLeaks(void) { + auto testState = TestState::getState(); + pid_t pid = getpid(); + char pidString[32]; + sprintf(&pidString[0], "%d", pid); + if (getuid() != 0) { + printf("Insufficient priviledges, skipping Leak check: %s\n", testState->testName); + exit(0); + } + const char *args[] = { pidString, NULL }; + // We do this instead of using a dispatch_semaphore to prevent priority inversions + __block dispatch_data_t leaksOutput = NULL; + _process process; + process.set_executable_path("/usr/bin/leaks"); + process.set_args(args); + process.set_stdout_handler(^(int fd) { + ssize_t size = 0; + do { + char buffer[16384]; + size = read(fd, &buffer[0], 16384); + if (size == -1) { break; } + dispatch_data_t data = dispatch_data_create(&buffer[0], size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + if (!leaksOutput) { + leaksOutput = data; + } else { + leaksOutput = dispatch_data_create_concat(leaksOutput, data); + } + } while (size > 0); + }); + process.set_exit_handler(^(pid_t pid) { + int status = 0; + (void)waitpid(pid, &status, 0); + + int exitStatus = WEXITSTATUS(status); + if (exitStatus == 0) { + PASS("No leaks"); + } else { + if (leaksOutput) { + const void * buffer; + size_t size; + __unused dispatch_data_t map = dispatch_data_create_map(leaksOutput, &buffer, &size); + FAIL("Found Leaks:\n\n%s", buffer); + } + } + }); + + testState->checkForLeaks = false; + (void)process.launch(); + exit(0); +} + +void TestState::_PASSV(const char* file, unsigned line, const char* format, va_list args) { + if (output == None) { + exit(0); + } + if (checkForLeaks) { + runLeaks(); + } else { + _IOlock.withLock([this,&format,&args,&file,&line](){ + if (output == Console) { + printf("[\033[0;32mPASS\033[0m] %s: ", testName); + vprintf(format, args); + printf("\n"); + if (logOnSuccess && logs.count()) { + printf("[\033[0;33mLOG\033[0m]\n"); + for (const auto& log : logs) { + printf("\t%s\n", log); + } + } + } else if (output == BATS) { + printf("[PASS] %s: ", testName); + vprintf(format, args); + printf("\n"); + if (logOnSuccess && logs.count()) { + printf("[LOG]\n"); + for (const auto& log : logs) { + printf("\t%s\n", log); + } + } + } else if (output == XCTest) { + printf(""); + printf(""); + printf(""); + printf(""); + printf("PASS"); + if (logOnSuccess) { + char *logBuffer = NULL; + getLogsString(&logBuffer); + if ( logBuffer != NULL ) { + printf("LOGS%s", logBuffer); + free(logBuffer); + } + } + printf(""); + printf(""); + } + exit(0); + }); + } + __builtin_unreachable(); +} + +void _PASS(const char* file, unsigned line, const char* format, ...) { + va_list args; + va_start (args, format); + TestState::getState()->_PASSV(file, line, format, args); + va_end (args); +} + +void TestState::_FAILV(const char* file, unsigned line, const char* format, va_list args) { + if (output == None) { + exit(0); + } + _IOlock.withLock([this,&format,&args,&file,&line](){ + if (output == Console) { + printf("[\033[0;31mFAIL\033[0m] %s: ", testName); + vprintf(format, args); + printf("\n"); + printf("[\033[0;33mLOG\033[0m]\n"); + if (logs.count()) { + for (const auto& log : logs) { + printf("\t%s\n", log); + } + } + } else if (output == BATS) { + printf("[FAIL] %s: ", testName); + vprintf(format, args); + printf("\n"); + if (logs.count()) { + printf("[LOG]\n"); + for (const auto& log : logs) { + printf("\t%s\n", log); + } + } + } else if (output == XCTest) { + printf(""); + printf(""); + printf(""); + printf(""); + printf("PASS"); + printf("FILE%s", file); + printf("LINE%u", line); + char *buffer; + vasprintf(&buffer, format, args); + printf("INFO%s", buffer); + free(buffer); + char *logBuffer = NULL; + getLogsString(&logBuffer); + if ( logBuffer != NULL ) { + printf("LOGS%s", logBuffer); + free(logBuffer); + } + printf(""); + printf(""); + } + exit(0); + }); + __builtin_unreachable(); +} + +void _FAIL(const char* file, unsigned line, const char* format, ...) { + va_list args; + va_start (args, format); + TestState::getState()->_FAILV(file, line, format, args); + va_end (args); +} + +void TestState::_LOGV(const char* file, unsigned line, const char* format, va_list args) { + _IOlock.withLock([this,&format,&args](){ + if (logImmediate) { + vprintf(format, args); + printf("\n"); + } else { + char *str; + vasprintf(&str, format, args); + logs.push_back(str); + } + }); +} + +void _LOG(const char* file, unsigned line, const char* format, ...) { + va_list args; + va_start (args, format); + TestState::getState()->_LOGV(file, line, format, args); + va_end (args); +} + +void _TIMEOUT(const char* file, unsigned line, uint64_t seconds) { + _LOG(file, line, "Registering %llu second test timeout", seconds); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT); + dispatch_time_t milestone = dispatch_time(DISPATCH_WALLTIME_NOW, seconds * NSEC_PER_SEC); + dispatch_source_set_timer(source, milestone, 0, 0); + dispatch_source_set_event_handler(source, ^{ + FAIL("Test timed out"); + }); + dispatch_resume(source); +} diff --git a/testing/lib/test_support.exp b/testing/lib/test_support.exp new file mode 100644 index 0000000..83a6dbf --- /dev/null +++ b/testing/lib/test_support.exp @@ -0,0 +1,6 @@ +__PASS +__FAIL +__LOG +__TIMEOUT +__ZN8_process* +__process* diff --git a/testing/nocr/nocr.c b/testing/nocr/nocr.c deleted file mode 100644 index 74c1e88..0000000 --- a/testing/nocr/nocr.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "execserverServer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -static pid_t sChildPid; -static dispatch_semaphore_t sServerRunning; -static bool sChildCrashed = false; -static bool sChildTerminatedByDyld = false; - -/* - * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY. - * runs mach_msg_server once for receiving exception messages from kernel. - */ -static void* serverCode(void* arg) -{ - mach_port_t exception_port; - - kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); - if (kret != KERN_SUCCESS) - errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret); - - kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); - if (kret != KERN_SUCCESS) - errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret); - - kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port, - EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0); - if (kret != KERN_SUCCESS) - errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret); - - dispatch_semaphore_signal(sServerRunning); - - kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0); - if (kret != KERN_SUCCESS) - errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret); - - return NULL; -} - - -static void childDied(int sig) -{ - struct proc_exitreasoninfo info; - bzero(&info, sizeof(info)); - uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; - bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); - info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; - info.eri_kcd_buf = (user_addr_t)packReasonData; - //fprintf(stderr, "info=%p\n", &info); - if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) { - printf("bad return size from proc_pidinfo()\n"); - return; - } - sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD); - } - - -int main(int argc, const char* argv[]) -{ - if ( argc < 2 ) { - fprintf(stderr, "usage: nocr [-require_crash] prog args...\n"); - return EXIT_FAILURE; - } - unsigned progArgIndex = 1; - bool requireCrash = false; - const char* testName = NULL; - if ( strcmp(argv[1], "-require_crash") == 0 ) { - progArgIndex = 2; - requireCrash = true; - testName = getenv("NOCR_TEST_NAME"); - if ( testName ) - printf("[BEGIN] %s\n", testName); - } - - signal(SIGCHLD, childDied); - - sServerRunning = dispatch_semaphore_create(0); - - // start up thread for mach server which handles mach exception ports - pthread_t serverThread; - int result = pthread_create(&serverThread, NULL, serverCode, NULL); - if ( result ) - err(EXIT_FAILURE, "pthread_create"); - - // wait until server is up before starting child - dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER); - - // fork and exec child - sChildPid = fork(); - if ( sChildPid < 0 ) - err(EXIT_FAILURE, "fork"); - if ( sChildPid == 0 ) { - // child side - result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]); - err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]); - } - - // wait for child to finish (including crash) - int status; - int waitResult; - int childResult = EXIT_FAILURE; - do { - waitResult = waitpid(sChildPid, &status, 0); - } while ( (waitResult == -1) && (errno == EINTR) ); - if ( waitResult != -1 ) { - if ( WIFEXITED(status) ) { - childResult = WEXITSTATUS(status); - } - } - - if ( requireCrash ) { - if ( testName ) { - if ( sChildCrashed || sChildTerminatedByDyld ) - printf("[PASS] %s\n", testName); - else - printf("[FAIL] %s\n", testName); - } - return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE; - } - else - return childResult; -} - - - - -// Mach exception handler routines needed by execserverServer.c - -kern_return_t -catch_mach_exception_raise(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - mach_exception_data_t code, - mach_msg_type_number_t codeCnt) -{ - //fprintf(stderr, "child crashed\n"); - sChildCrashed = true; - return KERN_SUCCESS; -} - -kern_return_t -catch_mach_exception_raise_state(mach_port_t exception_port, - exception_type_t exception, - const mach_exception_data_t code, - mach_msg_type_number_t codeCnt, - int * flavor, - const thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t * new_stateCnt) -{ - errx(1, "Unsupported catch_mach_exception_raise_state"); - return KERN_NOT_SUPPORTED; -} - -kern_return_t -catch_mach_exception_raise_state_identity(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - mach_exception_data_t code, - mach_msg_type_number_t codeCnt, - int * flavor, - thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t * new_stateCnt) -{ - errx(1, "Unsupported catch_mach_exception_raise_state_identity"); - return KERN_NOT_SUPPORTED; -} - - - diff --git a/testing/nocr/nocr.cpp b/testing/nocr/nocr.cpp new file mode 100644 index 0000000..dd08d40 --- /dev/null +++ b/testing/nocr/nocr.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include + +#include "test_support.h" + +extern const char** environ; + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( argc < 2 ) { + fprintf(stderr, "usage: nocr prog args...\n"); + return EXIT_FAILURE; + } + + _process process; + process.set_executable_path(argv[1]); + process.set_args(&argv[2]); + process.set_env(environ); + process.set_crash_handler(^(task_t task) { + exit(0); + }); + process.set_exit_handler(^(pid_t pid) { + int status = 0; + (void)waitpid(pid, &status, 0); + + // Only call exit if the child exited normally, otherwise keep running to consume the crash + if (WIFEXITED(status)) { + exit(0); + } + }); + process.launch(); + dispatch_main(); +} diff --git a/testing/run-static/jit_entitlement.plist b/testing/run-static/jit_entitlement.plist new file mode 100644 index 0000000..9a1d0fb --- /dev/null +++ b/testing/run-static/jit_entitlement.plist @@ -0,0 +1,8 @@ + + + + + dynamic-codesigning + + + diff --git a/testing/run-static/run-static.cpp b/testing/run-static/run-static.cpp new file mode 100644 index 0000000..a06ea63 --- /dev/null +++ b/testing/run-static/run-static.cpp @@ -0,0 +1,230 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "ClosureFileSystemPhysical.h" +#include "MachOAnalyzer.h" +#include "MachOFile.h" + +#include "../testing/test-cases/kernel-test-runner.h" + +const bool isLoggingEnabled = false; + +int entryFunc(const TestRunnerFunctions* funcs); +typedef __typeof(&entryFunc) EntryFuncTy; + +TestRunnerFunctions testFuncs = { + .version = 1, + .mhs = { nullptr, nullptr, nullptr, nullptr }, + .basePointers = { nullptr, nullptr, nullptr, nullptr }, + .printf = &::printf, + .exit = &::exit, + .testPass = &_PASS, + .testFail = &_FAIL, + .testLog = &_LOG, + .testTimeout = &_TIMEOUT, +}; + +struct LoadedMachO { + const dyld3::MachOAnalyzer* ma = nullptr; + // base pointer is the same as 'ma' when the binary has __TEXT first, + // but will point at where we mapped __DATA if building a reverse auxKC. + const void* basePointer = nullptr; +}; + +LoadedMachO loadPath(const char* binaryPath) { + __block Diagnostics diag; + dyld3::closure::FileSystemPhysical fileSystem; + dyld3::closure::LoadedFileInfo info; + char realerPath[MAXPATHLEN]; + __block bool printedError = false; + if (!fileSystem.loadFile(binaryPath, info, realerPath, ^(const char* format, ...) { + fprintf(stderr, "run-static: "); + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + printedError = true; + })) { + if (!printedError ) + fprintf(stderr, "run-static: %s: file not found\n", binaryPath); + exit(1); + } + + const char* currentArchName = dyld3::MachOFile::currentArchName(); + const dyld3::GradedArchs& currentArchs = dyld3::GradedArchs::forName(currentArchName); + __block const dyld3::MachOFile* mf = nullptr; + __block uint64_t sliceOffset = 0; + if ( dyld3::FatFile::isFatFile(info.fileContent) ) { + const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent; + ff->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, + const void* sliceStart, uint64_t sliceSize, bool& stop) { + const dyld3::MachOFile* sliceMF = (dyld3::MachOFile*)sliceStart; + if ( currentArchs.grade(sliceMF->cputype, sliceMF->cpusubtype, false) != 0 ) { + mf = sliceMF; + sliceOffset = (uint64_t)mf - (uint64_t)ff; + stop = true; + return; + } + }); + + if ( diag.hasError() ) { + fprintf(stderr, "Error: %s\n", diag.errorMessage()); + return { nullptr, nullptr }; + } + + if ( mf == nullptr ) { + fprintf(stderr, "Could not use binary '%s' because it does not contain a slice compatible with host '%s'\n", + binaryPath, currentArchName); + return { nullptr, nullptr }; + } + } else { + mf = (dyld3::MachOFile*)info.fileContent; + if ( !mf->isMachO(diag, info.sliceLen) ) { + fprintf(stderr, "Could not use binary '%s' because '%s'\n", binaryPath, diag.errorMessage()); + return { nullptr, nullptr }; + } + + if ( currentArchs.grade(mf->cputype, mf->cpusubtype, false) == 0 ) { + fprintf(stderr, "Could not use binary '%s' because 'incompatible arch'\n", binaryPath); + return { nullptr, nullptr }; + } + } + + if ( !mf->isFileSet() ) { + fprintf(stderr, "Could not use binary '%s' because 'it is not a static executable'\n", binaryPath); + return { nullptr, nullptr }; + } + + uint64_t mappedSize = ((dyld3::MachOAnalyzer*)mf)->mappedSize(); + vm_address_t mappedAddr; + if ( ::vm_allocate(mach_task_self(), &mappedAddr, (size_t)mappedSize, VM_FLAGS_ANYWHERE) != 0 ) { + fprintf(stderr, "Could not use binary '%s' because 'vm allocation failure'\n", binaryPath); + return { nullptr, nullptr }; + } + + int fd = open(binaryPath, O_RDONLY); + if ( fd == 0 ) { + fprintf(stderr, "Could not open binary '%s' because '%s'\n", binaryPath, strerror(errno)); + return { nullptr, nullptr }; + } + + __block uint64_t baseAddress = ~0ULL; + __block uint64_t textSegVMAddr = ~0ULL; + mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) { + baseAddress = std::min(baseAddress, info.vmAddr); + if ( strcmp(info.segName, "__TEXT") == 0 ) { + textSegVMAddr = info.vmAddr; + } + }); + + uint64_t loadAddress = (uint64_t)mappedAddr; + if ( isLoggingEnabled ) { + fprintf(stderr, "Mapping binary built at 0x%llx to 0x%llx\n", baseAddress, loadAddress); + } + mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) { + uint64_t requestedLoadAddress = info.vmAddr - baseAddress + loadAddress; + if ( isLoggingEnabled ) + fprintf(stderr, "Mapping %p: %s with perms %d\n", (void*)requestedLoadAddress, info.segName, info.protections); + if ( info.vmSize == 0 ) + return; + size_t readBytes = pread(fd, (void*)requestedLoadAddress, (uintptr_t)info.fileSize, sliceOffset + info.fileOffset); + if ( readBytes != info.fileSize ) { + fprintf(stderr, "Didn't read enough bytes\n"); + exit(1); + } + // __DATA_CONST is read-only when we actually run live, but this test runner fixes up __DATA_CONST after this vm_protect + // For now just don't make __DATA_CONST read only + uint32_t protections = info.protections; + if ( !strcmp(info.segName, "__DATA_CONST") ) + protections = VM_PROT_READ | VM_PROT_WRITE; + const bool setCurrentPermissions = false; + kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)requestedLoadAddress, (uintptr_t)info.vmSize, setCurrentPermissions, protections); + if ( r != KERN_SUCCESS ) { + diag.error("vm_protect didn't work because %d", r); + stop = true; + return; + } + }); + + if ( diag.hasError() ) { + fprintf(stderr, "Error: %s\n", diag.errorMessage()); + return { nullptr, nullptr }; + } + + if ( textSegVMAddr != baseAddress ) { + // __DATA is first. ma should still point to __TEXT + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)(mappedAddr + textSegVMAddr - baseAddress); + if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) { + fprintf(stderr, "Error: %s\n", diag.errorMessage()); + exit(1); + } + return { ma, (const void*)mappedAddr }; + } + + // __TEXT is first, so ma and base address are the same + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mappedAddr; + if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) { + fprintf(stderr, "Error: %s\n", diag.errorMessage()); + exit(1); + } + return { ma, (const void*)mappedAddr }; +} + +int main(int argc, const char * argv[]) { + bool unsupported = false; +#if TARGET_OS_WATCH + // HACK: Watch archs are not supported right now, so just return + unsupported = true; +#endif + if ( unsupported ) { + funcs = &testFuncs; + PASS("Success"); + } + + if ( (argc < 2) || (argc > 5) ) { + fprintf(stderr, "Usage: run-static *path to static binary* [- - *path to auc kc*]\n"); + return 1; + } + + for (unsigned i = 1; i != argc; ++i) { + if ( !strcmp(argv[i], "-") ) + continue; + LoadedMachO macho = loadPath(argv[i]); + if ( macho.ma == nullptr ) + return 1; + testFuncs.mhs[i - 1] = macho.ma; + testFuncs.basePointers[i - 1] = macho.basePointer; + } + + uint64_t entryOffset = 0; + bool usesCRT = false; + const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)testFuncs.mhs[0]; + if ( !ma->getEntry(entryOffset, usesCRT) ) { + fprintf(stderr, "Could not use binary '%s' because 'no entry defined'\n", argv[1]); + return 1; + } + + EntryFuncTy entryFunc = (EntryFuncTy)((uint8_t*)testFuncs.mhs[0] + entryOffset); +#if __has_feature(ptrauth_calls) + entryFunc = (EntryFuncTy)__builtin_ptrauth_sign_unauthenticated((void*)entryFunc, 0, 0); +#endif + fprintf(stderr, "Entering static binary at %p\n", entryFunc); + //kill(getpid(), SIGSTOP); + int returnCode = entryFunc(&testFuncs); + if ( returnCode != 0 ) { + fprintf(stderr, "Binary '%s' returned non-zero value %d\n", argv[1], returnCode); + return returnCode; + } + return 0; +} diff --git a/testing/task_read_for_pid_entitlement.plist b/testing/task_read_for_pid_entitlement.plist new file mode 100644 index 0000000..cdf0e9e --- /dev/null +++ b/testing/task_read_for_pid_entitlement.plist @@ -0,0 +1,8 @@ + + + + + com.apple.system-task-ports.read + + + diff --git a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c index 4c58f79..4d9ff34 100644 --- a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c @@ -1,5 +1,4 @@ -// 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/LC_DYLD_ENV-DYLD_LIBRARY_PATH-main1.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@executable_path/hideyhole @@ -14,21 +13,18 @@ #include #include +#include "test_support.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); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void*h = dlopen("/other/path/libfoo2.dylib", 0); if ( h != NULL ) - printf("[PASS] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname); + PASS("Success"); else - printf("[FAIL] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname); - - return 0; + FAIL("Could not load libfoo2.dylib via LC_DYLD_ENVIRONMENT -> DYLD_LIBRARY_PATH"); } diff --git a/testing/test-cases/NSAddImage-basic.dtest/main.c b/testing/test-cases/NSAddImage-basic.dtest/main.c index 5c870c4..771cb46 100644 --- a/testing/test-cases/NSAddImage-basic.dtest/main.c +++ b/testing/test-cases/NSAddImage-basic.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations -// BUILD: $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib -// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSAddImage-basic.exe $RUN_DIR/libzzz.dylib // RUN: ./NSAddImage-basic.exe libzzz.dylib @@ -11,18 +11,15 @@ #include #include +#include "test_support.h" -int main(int arg, const char* argv[]) -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { const char* path = argv[1]; - printf("[BEGIN] NSAddImage-basic %s\n", path); const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING); if ( mh == NULL ) - printf("[FAIL] NSAddImage-basic %s\n", path); + FAIL("Could not load \"%s\"", path); else - printf("[PASS] NSAddImage-basic %s\n", path); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/NSAddImage-fail.dtest/main.c b/testing/test-cases/NSAddImage-fail.dtest/main.c deleted file mode 100644 index 74bf245..0000000 --- a/testing/test-cases/NSAddImage-fail.dtest/main.c +++ /dev/null @@ -1,35 +0,0 @@ -// BUILD_ONLY: MacOSX - -// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations - -// RUN: ./NSAddImage-fail.exe return -// RUN: NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH ./NSAddImage-fail.exe abort - - - -#include -#include -#include -#include - - -int main(int argc, const char* argv[]) -{ - const char* arg = argv[1]; - - if ( strcmp(arg, "return") == 0 ) { - printf("[BEGIN] NSAddImage-fail %s\n", arg); - const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); - if ( mh == NULL ) - printf("[PASS] NSAddImage-fail %s\n", arg); - else - printf("[FAIL] NSAddImage-fail %s\n", arg); - } - else { - // run with nocr which print BEGIN/PASS/FAIL - NSAddImage("/xqz/42/libnotfound.xxx", 0); - } - - return 0; -} - diff --git a/testing/test-cases/NSAddImage-fail.dtest/main.cpp b/testing/test-cases/NSAddImage-fail.dtest/main.cpp new file mode 100644 index 0000000..d970b06 --- /dev/null +++ b/testing/test-cases/NSAddImage-fail.dtest/main.cpp @@ -0,0 +1,95 @@ +// BUILD(macos): $CXX main.cpp -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations -DRUN_DIR="$RUN_DIR" + +// BUILD(ios,tvos,watchos,bridgeos): + +// NO_CRASH_LOG: NSAddImage-fail.exe + +// RUN: ./NSAddImage-fail.exe return +// RUN: ./NSAddImage-fail.exe abort + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + +//FIXME: + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + const char* arg = argv[1]; + + if ( strcmp(arg, "return") == 0 ) { + const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); + if ( mh == NULL ) + PASS("Success"); + else + FAIL("Got mh non-existent image"); + } else if (strcmp(arg, "abort-child") == 0) { + // run with nocr which print BEGIN/PASS/FAIL + NSAddImage("/xqz/42/libnotfound.xxx", 0); + } else if (strcmp(arg, "abort") == 0) { + _process process; + process.set_executable_path(RUN_DIR "/NSAddImage-fail.exe"); + const char* args[] = {"abort-child", NULL }; + process.set_args(args); + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + process.set_crash_handler(^(task_t task) { + LOG("Crash for task=%u", task); + mach_vm_address_t corpse_data; + mach_vm_size_t corpse_size; + if (task_map_corpse_info_64(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) { + FAIL("Could not read corpse data"); + } + kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size); + if (!kcdata_iter_valid(autopsyData)) { + FAIL("Corpse Data Invalid"); + } + kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT); + if (!kcdata_iter_valid(exitReasonData)) { + FAIL("Could not find exit data"); + } + struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData); + + if ( ers->ers_namespace != OS_REASON_DYLD ) { + FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace); + } + if ( ers->ers_code != DYLD_EXIT_REASON_OTHER ) { + FAIL("eri_code (%lld) != DYLD_EXIT_REASON_OTHER", ers->ers_code); + } + kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size); + + KCDATA_ITER_FOREACH(iter) { + if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) { + kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter)); + if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){ + return; + } + kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD); + if ( !kcdata_iter_valid(payloadIter) ) { + FAIL("invalid kcdata payload iterator from payload data"); + } + const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter); + + if ( dyldInfo->version != 1 ) { + FAIL("dyld payload is not version 1"); + } + PASS("Success"); + } + } + FAIL("Did not find EXIT_REASON_USER_PAYLOAD"); + }); + process.launch(); + dispatch_main(); + } + return 0; +} + diff --git a/testing/test-cases/NSAddImage-loaded.dtest/main.c b/testing/test-cases/NSAddImage-loaded.dtest/main.c index db7ca4b..d9aff16 100644 --- a/testing/test-cases/NSAddImage-loaded.dtest/main.c +++ b/testing/test-cases/NSAddImage-loaded.dtest/main.c @@ -1,6 +1,6 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations -// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSAddImage-loaded.exe return @@ -11,23 +11,19 @@ #include #include +#include "test_support.h" -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSAddImage-loaded\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify value is returned for image already loaded const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); if ( mh == NULL ) - printf("[FAIL] NSAddImage-loaded\n"); + FAIL("Could not find mh for libSystem.B.dylib"); // verify existing dylib is not loaded if it is not already loaded mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); if ( mh != NULL ) - printf("[FAIL] NSAddImage-loaded\n"); + FAIL("Found mh for unloaded dylib libz.dylib"); - printf("[PASS] NSAddImage-loaded\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c b/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c index ddd9c74..e393f28 100644 --- a/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c +++ b/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c @@ -1,6 +1,6 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations -// BUILD: $CC main.c -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSAddressOfSymbol-basic.exe @@ -11,30 +11,44 @@ #include #include +#include "test_support.h" + extern struct mach_header __dso_handle; -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSAddressOfSymbol-basic\n"); +int patatino(void) { + return 666; +} +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); if ( sym == NULL ) { - printf("[FAIL] NSAddressOfSymbol-basic can't find main\n"); - return 0; + FAIL("can't find main"); } void* mainAddr = NSAddressOfSymbol(sym); if ( mainAddr != &main ) { - printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main); - return 0; + FAIL("address returned %p is not &main=%p", mainAddr, &main); + } + + NSSymbol sym2 = NSLookupSymbolInImage(&__dso_handle, "_patatino", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + if ( sym2 == NULL ) { + FAIL("cant' find patatino"); + } + void* funcAddr = NSAddressOfSymbol(sym2); + if ( funcAddr == NULL ) { + FAIL("address returned for patatino is NULL"); + } + // This returns a signed pointer, so we want to make sure we can call it without crashing. + int (*func_ptr)(void) = funcAddr; + int result = (*func_ptr)(); + if ( result != 666 ) { + FAIL("can't call the function correctly"); } // verify NULL works if ( NSAddressOfSymbol(NULL) != NULL ) { - printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n"); - return 0; + FAIL("NULL not handle"); } - printf("[PASS] NSAddressOfSymbol-basic\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c index 49341b3..aced13b 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c +++ b/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations +// BUILD(macos): $CC foo.c -o $BUILD_DIR/foo.bundle -bundle -// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle @@ -11,60 +11,49 @@ #include #include +#include "test_support.h" -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { const char* path = argv[1]; - NSObjectFileImage ofi; - if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { - printf("[FAIL] NSCreateObjectFileImageFromFile failed\n"); - return 0; - } - - NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); - if ( mod == NULL ) { - printf("[FAIL] NSLinkModule failed\n"); - return 0; - } - - NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); - if ( sym == NULL ) { - printf("[FAIL] NSLookupSymbolInModule failed\n"); - return 0; - } + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + } - void* func = NSAddressOfSymbol(sym); - if ( func == NULL ) { - printf("[FAIL] NSAddressOfSymbol failed\n"); - return 0; - } + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + } + + void* func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + FAIL("NSAddressOfSymbol failed"); + } Dl_info info; if ( dladdr(func, &info) == 0 ) { - printf("[FAIL] dladdr(&p, xx) failed"); - return 0; + FAIL("dladdr(&p, xx) fail"); } - //printf("_fooInBundle found in %s\n", info.dli_fname); + LOG("_fooInBundle found in %s", info.dli_fname); - if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { - printf("[FAIL] NSUnLinkModule failed\n"); - return 0; - } + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + } if ( dladdr(func, &info) != 0 ) { - printf("[FAIL] dladdr(&p, xx) found but should not have\n"); - return 0; + FAIL("dladdr(&p, xx) found but should not have"); } if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - return 0; - } + FAIL("NSDestroyObjectFileImage failed"); + } - printf("[PASS] NSCreateObjectFileImageFromFile-basic\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp index 54c676d..91fd66c 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp +++ b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CXX main.cpp -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations +// BUILD(macos): $CC foo.c -o $BUILD_DIR/foo.bundle -bundle -// BUILD: $CXX main.cpp -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle @@ -12,69 +12,59 @@ #include #include -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n"); +#include "test_support.h" +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { const char* path = argv[1]; std::vector 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; - } + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + } + ofis.push_back(ofi); + } - void* func = NSAddressOfSymbol(sym); - if ( func == NULL ) { - printf("[FAIL] NSAddressOfSymbol failed\n"); - return 0; - } + for(unsigned i = 0; i != 32; ++i) { + NSObjectFileImage ofi = ofis[i]; + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + } - 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); + NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + } - if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { - printf("[FAIL] NSUnLinkModule failed\n"); - return 0; - } + void* func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + FAIL("NSAddressOfSymbol failed"); + } - if ( dladdr(func, &info) != 0 ) { - printf("[FAIL] dladdr(&p, xx) found but should not have\n"); - return 0; - } - } + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + FAIL("dladdr(&p, xx) fail"); + } + LOG("_fooInBundle found in %s", info.dli_fname); - for (unsigned i = 0; i != 32; ++i) { - NSObjectFileImage ofi = ofis[i]; - if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - return 0; - } - } + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + } - printf("[PASS] NSCreateObjectFileImageFromFile-basic\n"); - return 0; + if ( dladdr(func, &info) != 0 ) { + FAIL("dladdr(&p, xx) found but should not have"); + } + } + + for (unsigned i = 0; i != 32; ++i) { + NSObjectFileImage ofi = ofis[i]; + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + } + } + + PASS("Success"); } diff --git a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c index ca41cb7..bbc9717 100644 --- a/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c +++ b/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations +// BUILD(macos): $CC foo.c -o $BUILD_DIR/foo.bundle -bundle -// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle @@ -18,91 +18,77 @@ #include #include +#include "test_support.h" static void checkBundle(const char* path, bool unlinkBeforeDestroy) { int fd = open(path, O_RDONLY, 0); if ( fd == -1 ) { - printf("[FAIL] open(%s) failed", path); - exit(0); + FAIL("open(%s) failed", path); } struct stat stat_buf; if ( fstat(fd, &stat_buf) == -1) { - printf("[FAIL] fstat() failed\n"); - exit(0); + FAIL("fstat() failed"); } void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( loadAddress == ((void*)(-1)) ) { - printf("[FAIL] mmap() failed\n"); - exit(0); + FAIL("mmap() failed"); } close(fd); NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { - printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n"); - exit(0); + FAIL("NSCreateObjectFileImageFromMemory failed"); } NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); if ( mod == NULL ) { - printf("[FAIL] NSLinkModule failed\n"); - exit(0); + FAIL("NSLinkModule failed"); } if ( !unlinkBeforeDestroy ) { // API lets you destroy ofi and NSModule lives on if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - exit(0); + FAIL("NSDestroyObjectFileImage failed"); } } NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); if ( sym == NULL ) { - printf("[FAIL] NSLookupSymbolInModule failed\n"); - exit(0); + FAIL("NSLookupSymbolInModule failed"); } void* func = NSAddressOfSymbol(sym); if ( func == NULL ) { - printf("[FAIL] NSAddressOfSymbol failed\n"); - exit(0); + FAIL("NSAddressOfSymbol failed"); } Dl_info info; if ( dladdr(func, &info) == 0 ) { - printf("[FAIL] dladdr(&p, xx) failed\n"); - exit(0); + FAIL("dladdr(&p, xx) failed"); } - //printf("_fooInBundle found in %s\n", info.dli_fname); + LOG("_fooInBundle found in %s", info.dli_fname); if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { - printf("[FAIL] NSUnLinkModule failed\n"); - exit(0); + FAIL("NSUnLinkModule failed"); } if ( dladdr(func, &info) != 0 ) { - printf("[FAIL] dladdr(&p, xx) found but should not have\n"); - exit(0); + FAIL("dladdr(&p, xx) found but should not have"); } if ( unlinkBeforeDestroy ) { if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - exit(0); + FAIL("NSDestroyObjectFileImage failed"); } } } -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { checkBundle(argv[1], true); checkBundle(argv[1], false); @@ -110,7 +96,6 @@ int main(int argc, const char* argv[]) for (unsigned i = 0; i != 255; ++i) checkBundle(argv[1], false); - printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c b/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c index 1adab58..4ef6ce3 100644 --- a/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c +++ b/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c @@ -1,6 +1,6 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations -// BUILD: $CC main.c -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./NSLookupSymbolInImage-basic.exe @@ -10,30 +10,26 @@ #include #include +#include "test_support.h" + extern struct mach_header __dso_handle; -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] NSLookupSymbolInImage-basic\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify known symbol works NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); if ( sym == NULL ) { - printf("[FAIL] NSLookupSymbolInImage-basic _main\n"); - return 0; + FAIL("Did not find symnbol _main"); } // verify mode where NSLookupSymbolInImage() returns NULL if symbol not found sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); if ( sym != NULL ) { - printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n"); - return 0; + FAIL("Did not find symnbol _42hhg"); } // Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used, // but dyld 2 just returned NULL, so no need to test that. - printf("[PASS] NSLookupSymbolInImage-basic\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m index 33f114b..3d74b42 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m @@ -11,6 +11,19 @@ #import +#include "test_support.h" + + +static bool objcOptimizedByDyld() { + extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo"); + return (objcInfo[1] & 0x80); +} + +static bool haveDyldCache() { + size_t unusedCacheLen; + return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL); +} + // All the libraries have a copy of NSString @interface NSString : NSObject @end @@ -57,8 +70,6 @@ Class getMainNSString() { return (Class)&OBJC_CLASS_$_NSString; } -extern int printf(const char*, ...); - extern id objc_getClass(const char *name); // Get the NSString from liblinked1.dylib @@ -72,20 +83,18 @@ static bool gotNSStringLinked = false; static bool gotNSStringLinked2 = false; static bool gotNSStringFoundation = false; -bool testDuplicate(const char* className, Class nonCacheClass) { +void testDuplicate(const char* className, Class nonCacheClass) { // Walk all the implementations of the class. There should be 2. One in the executable and one in the shared cache // The shared cache one should be returned first. // The objc runtime should have chosen the Foundation one as the canonical definition. Class objcRuntimeClassImpl = (Class)objc_getClass(className); if (objcRuntimeClassImpl == nil) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found via runtime\n", className); - return false; + FAIL("class %s not found via runtime", className); } if (objcRuntimeClassImpl == nonCacheClass) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s from runtime should not match main exexutable\n", className); - return false; + FAIL("class %s from runtime should not match main exexutable", className); } __block bool foundSharedCacheImpl = false; @@ -98,90 +107,63 @@ bool testDuplicate(const char* className, Class nonCacheClass) { // We should walk these in the order Foundation, main exe if (!foundSharedCacheImpl) { if (classPtr != objcRuntimeClassImpl) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from Foundation\n", className); - *stop = true; - return; + FAIL("Optimized class %s should have come from Foundation", className); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on Foundation\n", className); - *stop = true; - return; + FAIL("Optimized class %s isLoaded should have been set on Foundation", className); } foundSharedCacheImpl = true; return; } if (!foundMainExecutableImpl) { if (classPtr != nonCacheClass) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s should have come from main executable\n", className); - *stop = true; - return; + FAIL("Optimized class %s should have come from main executable", className); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized class %s isLoaded should have been set on main executable\n", className); - *stop = true; - return; + FAIL("Optimized class %s isLoaded should have been set on main executable", className); } foundMainExecutableImpl = true; return; } foundTooManyClasses = true; - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found somewhere other than main executable and Foundation\n", className); - *stop = true; - return; + FAIL("class %s found somewhere other than main executable and Foundation", className); }); if (!foundAnyClass) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found\n", className); - return false; + FAIL("class %s not found", className); } if (foundTooManyClasses) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s found too many times\n", className); - return false; + FAIL("class %s found too many times", className); } if (!foundSharedCacheImpl || !foundMainExecutableImpl) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: class %s not found for shared cache or main executable\n", className); - return false; + FAIL("class %s not found for shared cache or main executable", className); } - - return true; } -int main() { - printf("[BEGIN] _dyld_for_each_objc_class-duplicates\n"); - - // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything - const char* testDyldMode = getenv("TEST_DYLD_MODE"); - assert(testDyldMode); - - size_t sharedCacheLen = 0; - const void* sharedCacheStart = 0; - sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen); - bool haveSharedCache = sharedCacheStart != NULL; - if (!strcmp(testDyldMode, "2") || !haveSharedCache) { +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + size_t sharedCacheLen = 0; + const void* sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen); + if (!objcOptimizedByDyld() || (sharedCacheStart==NULL)) { __block bool sawClass = false; - _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) { + _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) { sawClass = true; }); if (sawClass) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: dyld2 shouldn't see any classes\n"); - return 0; + FAIL("dyld2 shouldn't see any classes"); } - printf("[PASS] _dyld_for_each_objc_class-duplicates (dyld2 or no shared cache)\n"); - return 0; + PASS("no shared cache or no dyld optimized objc"); } // Check that NSString comes from Foundation as the shared cache should win here. id runtimeNSString = objc_getClass("NSString"); if ( (uint64_t)runtimeNSString < (uint64_t)sharedCacheStart ) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString); - return 0; + FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString); } if ( (uint64_t)runtimeNSString >= ((uint64_t)sharedCacheStart + sharedCacheLen) ) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: NSString should have come from Foundation but instead was %p\n", runtimeNSString); - return 0; + FAIL("NSString should have come from Foundation but instead was %p", runtimeNSString); } // Walk all the implementations of "NSString" @@ -189,67 +171,49 @@ int main() { // We should walk these in the order Foundation, liblinked2, liblinked, main exe if (!gotNSStringFoundation) { if (classPtr != runtimeNSString) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from Foundation\n"); - *stop = true; - return; + FAIL("Optimized NSString should have come from Foundation"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on Foundation\n"); - *stop = true; - return; + FAIL("Optimized NSString isLoaded should have been set on Foundation"); } gotNSStringFoundation = true; return; } if (!gotNSStringLinked2) { if (classPtr != getLinked2NSString()) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked2\n"); - *stop = true; - return; + FAIL("Optimized NSString should have come from liblinked2"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked2\n"); - *stop = true; - return; + FAIL("Optimized NSString isLoaded should have been set on liblinked2"); } gotNSStringLinked2 = true; return; } if (!gotNSStringLinked) { if (classPtr != getLinked1NSString()) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from liblinked\n"); - *stop = true; - return; + FAIL("Optimized NSString should have come from liblinked"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on liblinked\n"); - *stop = true; - return; + FAIL("Optimized NSString isLoaded should have been set on liblinked"); } gotNSStringLinked = true; return; } if (!gotNSStringMain) { if (classPtr != getMainNSString()) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString should have come from main exe\n"); - *stop = true; - return; + FAIL("Optimized NSString should have come from main exe"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Optimized NSString isLoaded should have been set on main exe\n"); - *stop = true; - return; + FAIL("Optimized NSString isLoaded should have been set on main exe"); } gotNSStringMain = true; return; } - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Unexpected Optimized NSString\n"); - return; + FAIL("Unexpected Optimized NSString"); }); if ( !gotNSStringFoundation || !gotNSStringLinked2 || !gotNSStringLinked || !gotNSStringMain) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: Failed to find all duplicates of 'NSString'\n"); - return 0; + FAIL("Failed to find all duplicates of 'NSString'"); } // Visit again, and return Foundation's NSString @@ -259,23 +223,13 @@ int main() { *stop = true; }); if (NSStringImpl != runtimeNSString) { - printf("[FAIL] _dyld_for_each_objc_class-duplicates: _dyld_for_each_objc_class should have returned NSString from Foundation\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned NSString from Foundation"); } - if (!testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary)) - return 0; + testDuplicate("NSDictionary", (Class)&OBJC_CLASS_$_NSDictionary); + testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError); + testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet); + testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray); - if (!testDuplicate("NSError", (Class)&OBJC_CLASS_$_NSError)) - return 0; - - if (!testDuplicate("NSSet", (Class)&OBJC_CLASS_$_NSSet)) - return 0; - - if (!testDuplicate("NSArray", (Class)&OBJC_CLASS_$_NSArray)) - return 0; - - printf("[PASS] _dyld_for_each_objc_class-duplicates\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm b/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm index 709050b..02aca79 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm +++ b/testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm @@ -1,9 +1,11 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains +// BUILD(macos): $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains +// BUILD(macos): $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains +// BUILD(macos): $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++ -// BUILD: $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains -// BUILD: $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains -// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains -// BUILD: $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -Wl,-fixup_chains -lc++ +// BUILD(macos): $SKIP_INSTALL $BUILD_DIR/libmissing.dylib + +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./_dyld_for_each_objc_class-missing-weak-chained.exe @@ -18,6 +20,18 @@ #import +#include "test_support.h" + +static bool objcOptimizedByDyld() { + extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo"); + return (objcInfo[1] & 0x80); +} + +static bool haveDyldCache() { + size_t unusedCacheLen; + return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL); +} + // All the libraries have a copy of DyldClass @interface DyldClass : NSObject @end @@ -43,8 +57,6 @@ Class getMainDyldMainClass() { return (Class)&OBJC_CLASS_$_DyldMainClass; } -extern "C" int printf(const char*, ...); - extern "C" id objc_getClass(const char *name); // Get the DyldClass from liblinked1.dylib @@ -64,64 +76,48 @@ extern "C" id getMissingDyldClass(); static bool gotDyldClassMain = false; static bool gotDyldClassLinked1 = false; -int main() { - printf("[BEGIN] _dyld_for_each_objc_class-missing-weak-chained\n"); - - // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything - const char* testDyldMode = getenv("TEST_DYLD_MODE"); - assert(testDyldMode); - - size_t unusedCacheLen; - bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0; - if (!strcmp(testDyldMode, "2") || !haveSharedCache) { +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if (!objcOptimizedByDyld() || !haveDyldCache()) { __block bool sawClass = false; _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) { sawClass = true; }); if (sawClass) { - printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n"); - return 0; + FAIL("dyld2 shouldn't see any classes"); } - printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n"); - return 0; + PASS("no shared cache or no dyld optimized objc"); } // Make sure libmissing.dylib is actually missing if (&getMissingDyldClass != nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: libmissing needs to be missing\n"); - return 0; + FAIL("libmissing needs to be missing"); } // DyldClass in liblinked1 should exist as its superclass is just NSObject if (getLinked1DyldClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldClass should exist\n"); - return 0; + FAIL("liblinked1 DyldClass should exist"); } // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject if (getLinked1DyldLinkedClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked1 DyldLinkedClass should exist\n"); - return 0; + FAIL("liblinked1 DyldLinkedClass should exist"); } // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject if (getLinked2DyldLinkedClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: liblinked2 DyldLinkedClass should exist\n"); - return 0; + FAIL("liblinked2 DyldLinkedClass should exist"); } // Check that DyldMainClass comes main.exe as that is its only definition id runtimeDyldMainClass = objc_getClass("DyldMainClass"); if (runtimeDyldMainClass != getMainDyldMainClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldMainClass should have come from main.exe\n"); - return 0; + FAIL("DyldMainClass should have come from main.exe"); } // Check that DyldClass comes liblinked1 as it should be missing from liblinked2 id runtimeDyldClass = objc_getClass("DyldClass"); if (runtimeDyldClass != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n"); - return 0; + FAIL("DyldClass should have come from liblinked1"); } // Check that DyldLinkedClass comes from liblinked2 @@ -129,8 +125,7 @@ int main() { #if 0 id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass"); if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldLinkedClass should have come from liblinked2\n"); - return 0; + FAIL("DyldLinkedClass should have come from liblinked2"); } #endif @@ -138,44 +133,33 @@ int main() { // We should walk these in the order liblinked, main exe if (!gotDyldClassLinked1) { if (classPtr != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from liblinked1\n"); - *stop = true; - return; + FAIL("DyldClass should have come from liblinked1"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on liblinked1\n"); - *stop = true; - return; + FAIL("DyldClass isLoaded should have been set on liblinked1"); } gotDyldClassLinked1 = true; return; } if (!gotDyldClassMain) { if (classPtr != getMainDyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass should have come from main exe\n"); - *stop = true; - return; + FAIL("DyldClass should have come from main exe"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: DyldClass isLoaded should have been set on main exe\n"); - *stop = true; - return; + FAIL("DyldClass isLoaded should have been set on main exe"); } gotDyldClassMain = true; return; } - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: Unexpected DyldClass\n"); - return; + FAIL("Unexpected DyldClass"); }); if (!gotDyldClassLinked1) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1"); } if (!gotDyldClassMain) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe"); } // Visit again, and return liblinked1's DyldClass @@ -186,8 +170,7 @@ int main() { *stop = true; }); if (dyldClassImpl != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1"); } // Visit again, and return liblinked1's DyldClass @@ -201,11 +184,8 @@ int main() { *stop = true; }); if (dyldClassImpl != getMainDyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak-chained: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe"); } - printf("[PASS] _dyld_for_each_objc_class-missing-weak-chained\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm b/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm index ce4b38e..1612129 100644 --- a/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm +++ b/testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm @@ -1,8 +1,10 @@ -// BUILD: $CC missing.m -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc +// BUILD: $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libmissing.dylib -lobjc // BUILD: $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $TEMP_DIR/libmissing.dylib -// BUILD: $CC main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $TEMP_DIR/libmissing.dylib -lc++ +// BUILD: $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib +// BUILD: $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -lc++ + +// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib // RUN: ./_dyld_for_each_objc_class-missing-weak.exe @@ -17,6 +19,18 @@ #import +#include "test_support.h" + +static bool objcOptimizedByDyld() { + extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo"); + return (objcInfo[1] & 0x80); +} + +static bool haveDyldCache() { + size_t unusedCacheLen; + return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL); +} + // All the libraries have a copy of DyldClass @interface DyldClass : NSObject @end @@ -42,8 +56,6 @@ Class getMainDyldMainClass() { return (Class)&OBJC_CLASS_$_DyldMainClass; } -extern "C" int printf(const char*, ...); - extern "C" id objc_getClass(const char *name); // Get the DyldClass from liblinked1.dylib @@ -63,64 +75,48 @@ extern "C" id getMissingDyldClass(); static bool gotDyldClassMain = false; static bool gotDyldClassLinked1 = false; -int main() { - printf("[BEGIN] _dyld_for_each_objc_class-missing-weak\n"); - - // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything - const char* testDyldMode = getenv("TEST_DYLD_MODE"); - assert(testDyldMode); - - size_t unusedCacheLen; - bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0; - if (!strcmp(testDyldMode, "2") || !haveSharedCache) { +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if (!objcOptimizedByDyld() || !haveDyldCache()) { __block bool sawClass = false; _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) { sawClass = true; }); if (sawClass) { - printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n"); - return 0; + FAIL("dyld2 shouldn't see any classes"); } - printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n"); - return 0; + PASS("no shared cache or no dyld optimized objc"); } // Make sure libmissing.dylib is actually missing if (&getMissingDyldClass != nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: libmissing needs to be missing\n"); - return 0; + FAIL("libmissing needs to be missing"); } // DyldClass in liblinked1 should exist as its superclass is just NSObject if (getLinked1DyldClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked1 DyldClass should exist\n"); - return 0; + FAIL("liblinked1 DyldClass should exist"); } // DyldLinkedClass in liblinked1 should exist as its superclass is just NSObject if (getLinked1DyldLinkedClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked1 DyldLinkedClass should exist\n"); - return 0; + FAIL("liblinked1 DyldLinkedClass should exist"); } // DyldLinkedClass in liblinked2 should exist as its superclass is just NSObject if (getLinked2DyldLinkedClass() == nil) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: liblinked2 DyldLinkedClass should exist\n"); - return 0; + FAIL("liblinked2 DyldLinkedClass should exist"); } // Check that DyldMainClass comes main.exe as that is its only definition id runtimeDyldMainClass = objc_getClass("DyldMainClass"); if (runtimeDyldMainClass != getMainDyldMainClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldMainClass should have come from main.exe\n"); - return 0; + FAIL("DyldMainClass should have come from main.exe"); } // Check that DyldClass comes liblinked1 as it should be missing from liblinked2 id runtimeDyldClass = objc_getClass("DyldClass"); if (runtimeDyldClass != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from liblinked1\n"); - return 0; + FAIL("DyldClass should have come from liblinked1"); } // Check that DyldLinkedClass comes from liblinked2 @@ -128,8 +124,7 @@ int main() { #if 0 id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass"); if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldLinkedClass should have come from liblinked2\n"); - return 0; + FAIL("DyldLinkedClass should have come from liblinked2"); } #endif @@ -137,44 +132,33 @@ int main() { // We should walk these in the order liblinked, main exe if (!gotDyldClassLinked1) { if (classPtr != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from liblinked1\n"); - *stop = true; - return; + FAIL("DyldClass should have come from liblinked1"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass isLoaded should have been set on liblinked1\n"); - *stop = true; - return; + FAIL("DyldClass isLoaded should have been set on liblinked1"); } gotDyldClassLinked1 = true; return; } if (!gotDyldClassMain) { if (classPtr != getMainDyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass should have come from main exe\n"); - *stop = true; - return; + FAIL("DyldClass should have come from main exe"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: DyldClass isLoaded should have been set on main exe\n"); - *stop = true; - return; + FAIL("DyldClass isLoaded should have been set on main exe"); } gotDyldClassMain = true; return; } - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: Unexpected DyldClass\n"); - return; + FAIL("Unexpected DyldClass"); }); if (!gotDyldClassLinked1) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have seen DyldClass in liblinked1\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have seen DyldClass in liblinked1"); } if (!gotDyldClassMain) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have seen DyldClass in main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have seen DyldClass in main.exe"); } // Visit again, and return liblinked1's DyldClass @@ -185,8 +169,7 @@ int main() { *stop = true; }); if (dyldClassImpl != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have returned DyldClass from liblinked1\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked1"); } // Visit again, and return liblinked1's DyldClass @@ -200,11 +183,8 @@ int main() { *stop = true; }); if (dyldClassImpl != getMainDyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class-missing-weak: _dyld_for_each_objc_class should have returned DyldClass from main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldClass from main.exe"); } - printf("[PASS] _dyld_for_each_objc_class-missing-weak\n"); - - return 0; + PASS("SUCCESS"); } diff --git a/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m index 517dfde..6aa6c18 100644 --- a/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_class.dtest/main.m @@ -14,6 +14,8 @@ #import +#include "test_support.h" + // All the libraries have a copy of DyldClass @interface DyldClass : NSObject @end @@ -39,8 +41,6 @@ Class getMainDyldMainClass() { return (Class)&OBJC_CLASS_$_DyldMainClass; } -extern int printf(const char*, ...); - extern id objc_getClass(const char *name); // Get the DyldClass from liblinked1.dylib @@ -56,40 +56,39 @@ static bool gotDyldClassMain = false; static bool gotDyldClassLinked = false; static bool gotDyldClassLinked2 = false; -int main() { - printf("[BEGIN] _dyld_for_each_objc_class\n"); - // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything - const char* testDyldMode = getenv("TEST_DYLD_MODE"); - assert(testDyldMode); +static bool objcOptimizedByDyld() { + extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo"); + return (objcInfo[1] & 0x80); +} - size_t unusedCacheLen; - bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0; - if (!strcmp(testDyldMode, "2") || !haveSharedCache) { +static bool haveDyldCache() { + size_t unusedCacheLen; + return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL); +} + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if (!objcOptimizedByDyld() || !haveDyldCache()) { __block bool sawClass = false; _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) { sawClass = true; }); if (sawClass) { - printf("[FAIL] _dyld_for_each_objc_class: dyld2 shouldn't see any classes\n"); - return 0; + FAIL("dyld2 shouldn't see any classes"); } - printf("[PASS] _dyld_for_each_objc_class (dyld2 or no shared cache)\n"); - return 0; + PASS("no shared cache or no dyld optimized objc"); } // Check that DyldClass comes from liblinked2 as it is last in load order id runtimeDyldClass = objc_getClass("DyldClass"); if (runtimeDyldClass != getLinked2DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class: DyldClass should have come from liblinked2\n"); - return 0; + FAIL("DyldClass should have come from liblinked2"); } // Check that DyldLinkedClass comes from liblinked2 as it is last in load order id runtimeDyldLinkedClass = objc_getClass("DyldLinkedClass"); if (runtimeDyldLinkedClass != getLinked2DyldLinkedClass()) { - printf("[FAIL] _dyld_for_each_objc_class: DyldLinkedClass should have come from liblinked2\n"); - return 0; + FAIL("DyldLinkedClass should have come from liblinked2"); } // Walk all the implementations of "DyldClass" @@ -97,53 +96,39 @@ int main() { // We should walk these in the order liblinked2, liblinked, main exe if (!gotDyldClassLinked2) { if (classPtr != getLinked2DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from liblinked2\n"); - *stop = true; - return; + FAIL("Optimized DyldClass should have come from liblinked2"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on liblinked2\n"); - *stop = true; - return; + FAIL("Optimized DyldClass isLoaded should have been set on liblinked2"); } gotDyldClassLinked2 = true; return; } if (!gotDyldClassLinked) { if (classPtr != getLinked1DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from liblinked\n"); - *stop = true; - return; + FAIL("Optimized DyldClass should have come from liblinked"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on liblinked\n"); - *stop = true; - return; + FAIL("Optimized DyldClass isLoaded should have been set on liblinked"); } gotDyldClassLinked = true; return; } if (!gotDyldClassMain) { if (classPtr != getMainDyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass should have come from main exe\n"); - *stop = true; - return; + FAIL("Optimized DyldClass should have come from main exe"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_class: Optimized DyldClass isLoaded should have been set on main exe\n"); - *stop = true; - return; + FAIL("Optimized DyldClass isLoaded should have been set on main exe"); } gotDyldClassMain = true; return; } - printf("[FAIL] _dyld_for_each_objc_class: Unexpected Optimized DyldClass\n"); - return; + FAIL("Unexpected Optimized DyldClass"); }); if ( !gotDyldClassLinked2 || !gotDyldClassLinked || !gotDyldClassMain) { - printf("[FAIL] _dyld_for_each_objc_class: Failed to find all duplicates of 'DyldClass'\n"); - return 0; + FAIL("Failed to find all duplicates of 'DyldClass'"); } // Visit again, and return liblinked2's DyldClass @@ -153,8 +138,7 @@ int main() { *stop = true; }); if (dyldClassImpl != getLinked2DyldClass()) { - printf("[FAIL] _dyld_for_each_objc_class: _dyld_for_each_objc_class should have returned DyldClass from liblinked2\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldClass from liblinked2"); } // Visit DyldMainClass and make sure it makes the callback for just the result from main.exe @@ -164,11 +148,8 @@ int main() { *stop = true; }); if (dyldMainClassImpl != getMainDyldMainClass()) { - printf("[FAIL] _dyld_for_each_objc_class: _dyld_for_each_objc_class should have returned DyldMainClass from main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_class should have returned DyldMainClass from main.exe"); } - printf("[PASS] _dyld_for_each_objc_class\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m b/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m index eea4adf..02f2ad6 100644 --- a/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m +++ b/testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m @@ -17,6 +17,18 @@ #include #include +#include "test_support.h" + +static bool objcOptimizedByDyld() { + extern const uint32_t objcInfo[] __asm("section$start$__DATA_CONST$__objc_imageinfo"); + return (objcInfo[1] & 0x80); +} + +static bool haveDyldCache() { + size_t unusedCacheLen; + return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL); +} + // All the libraries have a copy of DyldProtocol @protocol DyldProtocol @end @@ -35,8 +47,6 @@ static void* useDyldMainProtocol() { return (void*)@protocol(DyldMainProtocol); } -extern int printf(const char*, ...); - extern id objc_getProtocol(const char *name); static bool gotDyldProtocolMain = false; @@ -46,46 +56,34 @@ static bool gotDyldProtocolLinked2 = false; static bool isInImage(void* ptr, const char* name) { Dl_info info; if ( dladdr(ptr, &info) == 0 ) { - printf("[FAIL] _dyld_for_each_objc_protocol dladdr(protocol, xx) failed\n"); + FAIL("dladdr(protocol, xx) failed"); return false; } return strstr(info.dli_fname, name) != NULL; } -int main() { - printf("[BEGIN] _dyld_for_each_objc_protocol\n"); - - // This API is only available with dyld3 and shared caches. If we have dyld2 then don't do anything - const char* testDyldMode = getenv("TEST_DYLD_MODE"); - assert(testDyldMode); - - size_t unusedCacheLen; - bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0; - if (!strcmp(testDyldMode, "2") || !haveSharedCache) { - __block bool sawProtocol = false; - _dyld_for_each_objc_protocol("DyldProtocol", ^(void* protocolPtr, bool isLoaded, bool* stop) { - sawProtocol = true; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if (!objcOptimizedByDyld() || !haveDyldCache()) { + __block bool sawClass = false; + _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) { + sawClass = true; }); - if (sawProtocol) { - printf("[FAIL] _dyld_for_each_objc_protocol: dyld2 shouldn't see any protocols\n"); - return 0; + if (sawClass) { + FAIL("dyld2 shouldn't see any classes"); } - printf("[PASS] _dyld_for_each_objc_protocol (dyld2 or no shared cache)\n"); - return 0; + PASS("no shared cache or no dyld optimized objc"); } // Check that DyldProtocol comes from liblinked2 as it is last in load order id runtimeDyldProtocol = objc_getProtocol("DyldProtocol"); if (!isInImage(runtimeDyldProtocol, "liblinked2")) { - printf("[FAIL] _dyld_for_each_objc_protocol: DyldProtocol should have come from liblinked2\n"); - return 0; + FAIL("DyldProtocol should have come from liblinked2"); } // Check that DyldLinkedProtocol comes from liblinked2 as it is last in load order id runtimeDyldLinkedProtocol = objc_getProtocol("DyldLinkedProtocol"); if (!isInImage(runtimeDyldLinkedProtocol, "liblinked2")) { - printf("[FAIL] _dyld_for_each_objc_protocol: DyldLinkedProtocol should have come from liblinked2\n"); - return 0; + FAIL("DyldLinkedProtocol should have come from liblinked2"); } // Walk all the implementations of "DyldProtocol" @@ -93,47 +91,35 @@ int main() { // We should walk these in the order liblinked2, liblinked, main exe if (!gotDyldProtocolLinked2) { if (!isInImage(protocolPtr, "liblinked2")) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from liblinked2\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol should have come from liblinked2"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on liblinked2\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked2"); } gotDyldProtocolLinked2 = true; return; } if (!gotDyldProtocolLinked) { if (!isInImage(protocolPtr, "liblinked1")) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from liblinked\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol should have come from liblinked"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on liblinked\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol isLoaded should have been set on liblinked"); } gotDyldProtocolLinked = true; return; } if (!gotDyldProtocolMain) { if (!isInImage(protocolPtr, "_dyld_for_each_objc_protocol.exe")) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol should have come from main exe\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol should have come from main exe"); } if (!isLoaded) { - printf("[FAIL] _dyld_for_each_objc_protocol: Optimized DyldProtocol isLoaded should have been set on main exe\n"); - *stop = true; - return; + FAIL("Optimized DyldProtocol isLoaded should have been set on main exe"); } gotDyldProtocolMain = true; return; } - printf("[FAIL] _dyld_for_each_objc_protocol: Unexpected Optimized DyldProtocol\n"); + FAIL("Unexpected Optimized DyldProtocol"); return; }); @@ -144,8 +130,7 @@ int main() { *stop = true; }); if (!isInImage(DyldProtocolImpl, "liblinked2")) { - printf("[FAIL] _dyld_for_each_objc_protocol: _dyld_for_each_objc_protocol should have returned DyldProtocol from liblinked2\n"); - return 0; + FAIL("_dyld_for_each_objc_protocol should have returned DyldProtocol from liblinked2"); } // Visit DyldMainProtocol and make sure it makes the callback for just the result from main.exe @@ -155,11 +140,21 @@ int main() { *stop = true; }); if (!isInImage(DyldMainProtocolImpl, "_dyld_for_each_objc_protocol.exe")) { - printf("[FAIL] _dyld_for_each_objc_protocol: _dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe\n"); - return 0; + FAIL("_dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe"); } - printf("[PASS] _dyld_for_each_objc_protocol\n"); +#if __has_feature(ptrauth_calls) + // Check the ISA was signed correctly on arm64e + id dyldMainProtocol = @protocol(DyldMainProtocol); + void* originalISA = *(void **)dyldMainProtocol; + void* strippedISA = __builtin_ptrauth_strip(originalISA, ptrauth_key_asda); + uint64_t discriminator = __builtin_ptrauth_blend_discriminator((void*)dyldMainProtocol, 27361); + void* signedISA = __builtin_ptrauth_sign_unauthenticated((void*)strippedISA, 2, discriminator); + if ( originalISA != signedISA ) { + FAIL("_dyld_for_each_objc_protocol DyldMainProtocol ISA is not signed correctly: %p vs %p", + originalISA, signedISA); + } +#endif - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/foo.c b/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/main.c b/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/main.c new file mode 100644 index 0000000..4d065e6 --- /dev/null +++ b/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/main.c @@ -0,0 +1,41 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name /System/Library/Frameworks/IOKit.framework/IOKit -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/_dyld_get_image_name-cache-symlink.exe $BUILD_DIR/libfoo.dylib + +// RUN: DYLD_LIBRARY_PATH=/usr/lib/system/introspection ./_dyld_get_image_name-cache-symlink.exe + + +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // walk images to see if path was converted to real path + const char* foundPath = NULL; + int count = _dyld_image_count(); + for (int i=0; i < count; ++i) { + const char* path = _dyld_get_image_name(i); + LOG("path[%2d]=%s", i, path); + if ( strstr(path, "/IOKit") != NULL ) { + if ( foundPath == NULL ) { + foundPath = path; + } + else { + FAIL("More than one libfoo found"); + } + } + } + if ( foundPath == NULL ) { + FAIL("No IOKit found"); + } + if ( strcmp(foundPath, "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") != 0 ) { + FAIL("Path is symlink not real path: %s", foundPath); + } + + PASS("Success"); +} + diff --git a/testing/test-cases/_dyld_get_image_slide.dtest/main.c b/testing/test-cases/_dyld_get_image_slide.dtest/main.c index e4f1b9d..ec3e198 100644 --- a/testing/test-cases/_dyld_get_image_slide.dtest/main.c +++ b/testing/test-cases/_dyld_get_image_slide.dtest/main.c @@ -10,11 +10,9 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] _dyld_get_image_slide-test\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { int count = _dyld_image_count(); for (int i=0; i < count; ++i) { const struct mach_header* mh = _dyld_get_image_header(i); @@ -22,8 +20,7 @@ int main() intptr_t slide = _dyld_get_image_slide(mh); intptr_t vmaddrSlide = _dyld_get_image_vmaddr_slide(i); if ( slide != vmaddrSlide ) { - printf("[FAIL] _dyld_get_image_slide-test: %lld != %lld in %s\n", - (uint64_t)slide, (uint64_t)vmaddrSlide, name); + FAIL("%lld != %lld in %s", (uint64_t)slide, (uint64_t)vmaddrSlide, name); return 0; } } @@ -32,17 +29,14 @@ int main() uintptr_t notMagic = 0; intptr_t slide = _dyld_get_image_slide((const struct mach_header*)¬Magic); if (slide != 0) { - printf("[FAIL] _dyld_get_image_slide-test: slide value %lld for bad magic\n", - (uint64_t)slide); + FAIL("slide value %lld for bad magic", (uint64_t)slide); } intptr_t vmaddrSlide = _dyld_get_image_vmaddr_slide(count + 1); if (vmaddrSlide != 0) { - printf("[FAIL] _dyld_get_image_slide-test: vmaddr slide value %lld for index %d\n", - (uint64_t)vmaddrSlide, count + 1); + FAIL("vmaddr slide value %lld for index %d", (uint64_t)vmaddrSlide, count + 1); } - printf("[PASS] _dyld_get_image_slide-test\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m b/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m index 2d19a99..2a0872e 100644 --- a/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m +++ b/testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m @@ -1,6 +1,6 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains -// BUILD: $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./_dyld_get_objc_selector-chained.exe @@ -8,6 +8,8 @@ #import +#include "test_support.h" + @interface DyldClass : NSObject @end @@ -32,41 +34,32 @@ } @end -extern int printf(const char*, ...); - extern id objc_getClass(const char *name); -int main() { - printf("[BEGIN] _dyld_get_objc_selector-chained\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // dyldClassFoo + const char* sel = _dyld_get_objc_selector("dyldClassFoo"); + if (sel) { + if ((SEL)sel != @selector(dyldClassFoo)) { + FAIL("dyldClassFoo is wrong"); + } + } - // dyldClassFoo - const char* sel = _dyld_get_objc_selector("dyldClassFoo"); - if (sel) { - if ((SEL)sel != @selector(dyldClassFoo)) { - printf("[FAIL] _dyld_get_objc_selector-chained: dyldClassFoo is wrong\n"); - return 0; - } - } + // dyldMainClassFoo + sel = _dyld_get_objc_selector("dyldMainClassFoo"); + if (sel) { + if ((SEL)sel != @selector(dyldMainClassFoo)) { + FAIL("dyldMainClassFoo is wrong"); + } + } - // dyldMainClassFoo - sel = _dyld_get_objc_selector("dyldMainClassFoo"); - if (sel) { - if ((SEL)sel != @selector(dyldMainClassFoo)) { - printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo is wrong\n"); - return 0; - } - } + // dyldMainClassFoo2 + sel = _dyld_get_objc_selector("dyldMainClassFoo2"); + if (sel) { + if ((SEL)sel != @selector(dyldMainClassFoo2)) { + FAIL("dyldMainClassFoo2 is wrong"); + } + } - // dyldMainClassFoo2 - sel = _dyld_get_objc_selector("dyldMainClassFoo2"); - if (sel) { - if ((SEL)sel != @selector(dyldMainClassFoo2)) { - printf("[FAIL] _dyld_get_objc_selector-chained: dyldMainClassFoo2 is wrong\n"); - return 0; - } - } - - printf("[PASS] _dyld_get_objc_selector-chained\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c b/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c index d11e442..e698fbf 100644 --- a/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c +++ b/testing/test-cases/_dyld_get_objc_selector-shared-cache.dtest/main.c @@ -9,11 +9,9 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] _dyld_get_objc_selector-shared-cache\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { size_t cacheLen; uintptr_t cacheStart = (uintptr_t)_dyld_get_shared_cache_range(&cacheLen); @@ -22,24 +20,20 @@ int main() if ( cacheStart != 0 ) { // We have a shared cache, so the selector should be there if ( selName == NULL ) { - printf("[FAIL] _dyld_get_objc_selector() returned null for selector in shared cache\n"); - return 0; + FAIL("_dyld_get_objc_selector() returned null for selector in shared cache"); } if ( ((uintptr_t)selName < cacheStart) || ((uintptr_t)selName >= (cacheStart + cacheLen)) ) { - printf("[FAIL] _dyld_get_objc_selector() pointer outside of shared cache range\n"); - return 0; + FAIL("_dyld_get_objc_selector() pointer outside of shared cache range"); } } else { // No shared cache, so the selector should not be found. // FIXME: This assumption may be false once the selectors are in the closure. if ( selName != NULL ) { - printf("[FAIL] _dyld_get_objc_selector() returned non-null for selector without shared cache\n"); - return 0; + FAIL("_dyld_get_objc_selector() returned non-null for selector without shared cache"); } } - printf("[PASS] _dyld_get_objc_selector-shared-cache\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_get_objc_selector.dtest/main.m b/testing/test-cases/_dyld_get_objc_selector.dtest/main.m index 9845721..8375d3d 100644 --- a/testing/test-cases/_dyld_get_objc_selector.dtest/main.m +++ b/testing/test-cases/_dyld_get_objc_selector.dtest/main.m @@ -7,65 +7,52 @@ #import +#include "test_support.h" + @interface DyldClass : NSObject @end @implementation DyldClass --(void) dyldClassFoo { - -} -+(void) dyldClassFoo { - -} +-(void) dyldClassFoo {} ++(void) dyldClassFoo {} @end @interface DyldMainClass : NSObject @end @implementation DyldMainClass --(void) dyldMainClassFoo { - -} --(void) dyldMainClassFoo2 { - -} +-(void) dyldMainClassFoo {} +-(void) dyldMainClassFoo2 {} @end -extern int printf(const char*, ...); - extern id objc_getClass(const char *name); -int main() { - printf("[BEGIN] _dyld_get_objc_selector\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // dyldClassFoo + const char* sel = _dyld_get_objc_selector("dyldClassFoo"); + if (sel) { + if ((SEL)sel != @selector(dyldClassFoo)) { + FAIL("dyldClassFoo is wrong"); + } + } + + // dyldMainClassFoo + sel = _dyld_get_objc_selector("dyldMainClassFoo"); + if (sel) { + if ((SEL)sel != @selector(dyldMainClassFoo)) { + FAIL("dyldMainClassFoo is wrong"); + } + } - // dyldClassFoo - const char* sel = _dyld_get_objc_selector("dyldClassFoo"); - if (sel) { - if ((SEL)sel != @selector(dyldClassFoo)) { - printf("[FAIL] _dyld_get_objc_selector: dyldClassFoo is wrong\n"); - return 0; - } - } + // dyldMainClassFoo2 + sel = _dyld_get_objc_selector("dyldMainClassFoo2"); + if (sel) { + if ((SEL)sel != @selector(dyldMainClassFoo2)) { + FAIL("dyldMainClassFoo2 is wrong"); + } + } - // dyldMainClassFoo - sel = _dyld_get_objc_selector("dyldMainClassFoo"); - if (sel) { - if ((SEL)sel != @selector(dyldMainClassFoo)) { - printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo is wrong\n"); - return 0; - } - } + PASS("_dyld_get_objc_selector"); - // dyldMainClassFoo2 - sel = _dyld_get_objc_selector("dyldMainClassFoo2"); - if (sel) { - if ((SEL)sel != @selector(dyldMainClassFoo2)) { - printf("[FAIL] _dyld_get_objc_selector: dyldMainClassFoo2 is wrong\n"); - return 0; - } - } - - printf("[PASS] _dyld_get_objc_selector\n"); - - return 0; -} \ No newline at end of file + return 0; +} diff --git a/testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c b/testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c new file mode 100644 index 0000000..0f6c13b --- /dev/null +++ b/testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/testing/test-cases/_dyld_get_prog_image_header.dtest/main.c b/testing/test-cases/_dyld_get_prog_image_header.dtest/main.c new file mode 100644 index 0000000..f372a89 --- /dev/null +++ b/testing/test-cases/_dyld_get_prog_image_header.dtest/main.c @@ -0,0 +1,26 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/_dyld_get_prog_image_header.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/_dyld_get_prog_image_header.exe + +// RUN: ./_dyld_get_prog_image_header.exe +// RUN: DYLD_INSERT_LIBRARIES=libfoo.dylib ./_dyld_get_prog_image_header.exe + + +#include + +#include "test_support.h" + +int main(int argc, const char* argv[]) { + uint32_t i = 0; + const struct mach_header* mhA = NULL; + do { + mhA = _dyld_get_image_header(i++); + } + while (mhA->filetype != MH_EXECUTE); + + const struct mach_header* mhB = _dyld_get_prog_image_header(); + if ( mhA != mhB ) + FAIL("Incorrect mach header address (%p)", mhA); + + PASS("Success"); +} diff --git a/testing/test-cases/_dyld_images_for_addresses.dtest/main.c b/testing/test-cases/_dyld_images_for_addresses.dtest/main.c index c44d1f2..5ffcf9a 100644 --- a/testing/test-cases/_dyld_images_for_addresses.dtest/main.c +++ b/testing/test-cases/_dyld_images_for_addresses.dtest/main.c @@ -10,6 +10,8 @@ #include #include +#include "test_support.h" + extern void* __dso_handle; extern int foo1(); @@ -36,9 +38,7 @@ int mydata = 5; int myarray[10]; -int main() -{ - printf("[BEGIN] _dyld_images_for_addresses\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { int mylocal; const void* addresses[12]; @@ -61,51 +61,50 @@ int main() for (int i=0; i < 12; ++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); + LOG("0x%09llX 0x%08llX %s", (long long)infos[i].image, infos[i].offsetInImage, str); } if ( infos[0].image != infos[1].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 1\n"); + FAIL("0 vs 1"); else if ( infos[0].image != infos[2].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 2\n"); + FAIL("0 vs 2"); else if ( infos[0].image != infos[3].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 3\n"); + FAIL("0 vs 3"); else if ( infos[0].image != infos[4].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 4\n"); + FAIL("0 vs 4"); else if ( infos[0].image != infos[5].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 5\n"); + FAIL("0 vs 5"); else if ( infos[6].image != NULL ) - printf("[FAIL] _dyld_images_for_addresses 6 vs null \n"); + FAIL("6 vs null "); else if ( infos[7].image != infos[8].image ) - printf("[FAIL] _dyld_images_for_addresses 7 vs 8\n"); + FAIL("7 vs 8"); else if ( infos[7].image != infos[9].image ) - printf("[FAIL] _dyld_images_for_addresses 7 vs 9\n"); + FAIL("7 vs 9"); else if ( infos[0].image == infos[7].image ) - printf("[FAIL] _dyld_images_for_addresses 0 vs 7\n"); + FAIL("0 vs 7"); else if ( infos[10].image != infos[11].image ) - printf("[FAIL] _dyld_images_for_addresses 10 vs 11\n"); + FAIL("10 vs 11"); else if ( uuid_compare(infos[0].uuid, infos[1].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 1\n"); + FAIL("uuid 0 vs 1"); else if ( uuid_compare(infos[0].uuid, infos[2].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 2\n"); + FAIL("uuid 0 vs 2"); else if ( uuid_compare(infos[0].uuid, infos[3].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 3\n"); + FAIL("uuid 0 vs 3"); else if ( uuid_compare(infos[0].uuid, infos[4].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 4\n"); + FAIL("uuid 0 vs 4"); else if ( uuid_compare(infos[0].uuid, infos[5].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 5\n"); + FAIL("uuid 0 vs 5"); else if ( uuid_is_null(infos[6].uuid) == 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 6 vs null\n"); + FAIL("uuid 6 vs null"); else if ( uuid_compare(infos[7].uuid, infos[8].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 7 vs 8\n"); + FAIL("uuid 7 vs 8"); else if ( uuid_compare(infos[7].uuid, infos[9].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 7 vs 9\n"); + FAIL("uuid 7 vs 9"); else if ( uuid_compare(infos[0].uuid, infos[7].uuid) == 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 0 vs 7\n"); + FAIL("uuid 0 vs 7"); else if ( uuid_compare(infos[10].uuid, infos[11].uuid) != 0 ) - printf("[FAIL] _dyld_images_for_addresses uuid 10 vs 11\n"); + FAIL("uuid 10 vs 11"); else - printf("[PASS] _dyld_images_for_addresses\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c b/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c index e35435f..85c3be6 100644 --- a/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c +++ b/testing/test-cases/_dyld_is_memory_immutable-lock.dtest/main.c @@ -11,6 +11,7 @@ #include #include +#include "test_support.h" static void* work(void* mh) { @@ -27,8 +28,7 @@ static void notify(const struct mach_header* mh, intptr_t vmaddr_slide) // for each image notified that is not in the dyld cache, spin up a thread which calls _dyld_is_memory_immutable() pthread_t workerThread; if ( pthread_create(&workerThread, NULL, &work, (void*)mh) != 0 ) { - printf("[FAIL] _dyld_is_memory_immutable-lock, pthread_create\n"); - exit(0); + FAIL("pthread_create"); } void* dummy; pthread_join(workerThread, &dummy); @@ -36,20 +36,14 @@ static void notify(const struct mach_header* mh, intptr_t vmaddr_slide) -int main() -{ - printf("[BEGIN] _dyld_is_memory_immutable-lock\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { _dyld_register_func_for_add_image(¬ify); void* h = dlopen(RUN_DIR "/lock.bundle", 0); if ( h == NULL ) { - printf("dlopen(lock.bundle) failed with error: %s\n", dlerror()); - printf("[FAIL] _dyld_is_memory_immutable-lock, lock.bundle not loaded\n"); - return 0; + FAIL("lock.bundle not loaded, dlopen(lock.bundle) failed with error: %s", dlerror()); } - printf("[PASS] _dyld_is_memory_immutable-lock\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c b/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c index a162cfa..94e4a0a 100644 --- a/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c +++ b/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c @@ -16,6 +16,8 @@ #include #endif +#include "test_support.h" + static const void* stripPointer(const void* ptr) { #if __has_feature(ptrauth_calls) @@ -36,59 +38,44 @@ const char* myStr = "myStr"; int myInt; -int main() -{ - printf("[BEGIN] _dyld_is_memory_immutable\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( !_dyld_is_memory_immutable(myStr, 6) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned false for string in main executable\n"); - return 0; + FAIL("returned false for string in main executable"); } if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned true for result from strdup()\n"); - return 0; + FAIL("returned true for result from strdup()"); } if ( _dyld_is_memory_immutable(&myInt, 4) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned true for global variabe in main executable\n"); - return 0; + FAIL("returned true for global variabe in main executable"); } if ( !_dyld_is_memory_immutable(foo(), 4) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned false for string in statically linked dylib\n"); - return 0; + FAIL("returned false for string in statically linked dylib"); } 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; + FAIL("returned false for strcpy function in dyld shared cache"); } if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned true for global variable in shared cache\n"); - return 0; + FAIL("returned true for global variable in shared cache"); } - void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST); + void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST); if ( handle == NULL ) { - printf("[FAIL] dlopen(libbar.dylib) failed"); - return 0; - } + FAIL("dlopen(libbar.dylib) failed"); } BarProc proc = dlsym(handle, "bar"); if ( proc == NULL ) { - printf("[FAIL] dlsym(bar) failed\n"); - return 0; + FAIL("dlsym(bar) failed"); } const char* barStr = (*proc)(); if ( _dyld_is_memory_immutable(barStr, 4) ) { - printf("[FAIL] _dyld_is_memory_immutable() returned true for string in unloadable dylib\n"); - return 0; + FAIL("returned true for string in unloadable dylib"); } - - printf("[PASS] _dyld_is_memory_immutable\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_launch_mode.dtest/main.c b/testing/test-cases/_dyld_launch_mode.dtest/main.c new file mode 100644 index 0000000..097ae7c --- /dev/null +++ b/testing/test-cases/_dyld_launch_mode.dtest/main.c @@ -0,0 +1,46 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dyld_launch_mode.exe + +// RUN: ./dyld_launch_mode.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + const char* modeStr = getenv("DYLD_USE_CLOSURES"); + if ( modeStr == NULL ) { + FAIL("dyld_launch_mode: DYLD_USE_CLOSURES env var not set"); + } + + uint32_t expectedFlags; + uint32_t launchFlags = _dyld_launch_mode(); + fprintf(stderr, "launchFlags=0x%08x\n", launchFlags); + + if ( strcmp(modeStr, "0") == 0 ) { + expectedFlags = 0; + } + else if ( strcmp(modeStr, "1") == 0 ) { + expectedFlags = DYLD_LAUNCH_MODE_USING_CLOSURE | DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; + } + else if ( strcmp(modeStr, "2") == 0 ) { + expectedFlags = DYLD_LAUNCH_MODE_USING_CLOSURE | DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH | DYLD_LAUNCH_MODE_MINIMAL_CLOSURE; + } + else { + FAIL("dyld_launch_mode: DYLD_USE_CLOSURES value unknown"); + } + + + if ( launchFlags == expectedFlags ) + PASS("dyld_launch_mode"); + else + FAIL("dyld_launch_mode: expected flags to be 0x%08X but were 0x%08X", expectedFlags, launchFlags); + + return 0; +} + diff --git a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c index e3b00af..29b6521 100644 --- a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c +++ b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/bar.c @@ -1,13 +1,12 @@ -#include -#include #include +#include "test_support.h" + __attribute__((constructor)) -void bar() { +void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST); if ( handle == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror()); - exit(0); + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror()); } } diff --git a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx index c56126c..0cc7e5c 100644 --- a/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_for_bulk_image_loads.dtest/main.cxx @@ -16,6 +16,8 @@ #include +#include "test_support.h" + extern "C" void foo(); extern mach_header __dso_handle; @@ -24,13 +26,13 @@ static std::unordered_set sCurrentImages; static void notify(unsigned count, const mach_header* mhs[], const char* paths[]) { - fprintf(stderr, "notification:\n"); + LOG("notification:"); for (unsigned i=0; i < count; ++i) { const mach_header* mh = mhs[i]; const char* path = paths[i]; - fprintf(stderr, " %3d mh=%p, path=%s\n", i, mh, path); + LOG(" %3d mh=%p, path=%s", i, mh, path); if ( sCurrentImages.count(mh) != 0 ) { - printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh); + FAIL("notified twice about %p", mh); exit(0); } sCurrentImages.insert(mh); @@ -38,49 +40,39 @@ static void notify(unsigned count, const mach_header* mhs[], const char* paths[] } -int main() -{ - printf("[BEGIN] _dyld_register_for_bulk_image_loads\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { _dyld_register_for_bulk_image_loads(¬ify); // verify we were notified about already loaded images if ( sCurrentImages.count(&__dso_handle) == 0 ) { - printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about main executable\n"); - exit(0); + FAIL("did not notify us about main executable"); } const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf); if ( sCurrentImages.count(libSysMH) == 0 ) { - printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libsystem_c.dylib\n"); - exit(0); + FAIL("did not notify us about libsystem_c.dylib"); } const mach_header* libFoo = dyld_image_header_containing_address((void*)&foo); if ( sCurrentImages.count(libFoo) == 0 ) { - printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libfoo.dylib\n"); - exit(0); + FAIL("did not notify us about libfoo.dylib"); } // verify we were notified about load of libfoo2.dylib and libz.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); - } + void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); + } 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_bulk_image_loads() did not notify us about libfoo2.dylib\n"); - exit(0); + FAIL("did not notify us about libfoo2.dylib"); } const void* inflateSym = dlsym(RTLD_DEFAULT, "inflate"); if ( inflateSym == NULL ) { - printf("[FAIL] _dyld_register_for_bulk_image_loads() did not load libz.dylib\n"); - exit(0); + FAIL("did not load libz.dylib"); } const mach_header* libzMH = dyld_image_header_containing_address(inflateSym); if ( sCurrentImages.count(libzMH) == 0 ) { - printf("[FAIL] _dyld_register_for_bulk_image_loads() did not notify us about libz.dylib\n"); - exit(0); + FAIL("did not notify us about libz.dylib"); } // Upward linking with dlopen may confuse the notifier as we may try to notify twice on the upwardly linked image @@ -91,11 +83,9 @@ int main() // We should not get a duplicate notification on libup void* handleBar = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST); if ( handleBar == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbar.dylib", dlerror()); - exit(0); + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbar.dylib", dlerror()); } - printf("[PASS] _dyld_register_for_bulk_image_loads\n"); - return 0; + PASS("SUuccess"); } diff --git a/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c b/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c index e3b00af..5613fa7 100644 --- a/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c +++ b/testing/test-cases/_dyld_register_for_image_loads.dtest/bar.c @@ -3,11 +3,13 @@ #include #include +#include "test_support.h" + __attribute__((constructor)) -void bar() { +void bar(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* handle = dlopen(RUN_DIR "/libbaz.dylib", RTLD_FIRST); if ( handle == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbaz.dylib", dlerror()); + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbaz.dylib", dlerror()); exit(0); } } diff --git a/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx b/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx index c4ec3b1..ed5a9c6 100644 --- a/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx @@ -17,6 +17,8 @@ #include +#include "test_support.h" + extern "C" void foo(); extern mach_header __dso_handle; @@ -26,73 +28,61 @@ static bool expectedUnloadableState = false; static void notify(const mach_header* mh, const char* path, bool unloadable) { - fprintf(stderr, "mh=%p, path=%s, unloadable=%d\n", mh, path, unloadable); + LOG("mh=%p, path=%s, unloadable=%d", mh, path, unloadable); if ( sCurrentImages.count(mh) != 0 ) { - printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh); - exit(0); + FAIL("notified twice about %p", mh); } sCurrentImages.insert(mh); const char* leaf = strrchr(path, '/'); if ( unloadable != expectedUnloadableState ) { - printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked unloadable(%s) but expected unloadable(%s) %p %s\n", + FAIL("image incorrectly marked unloadable(%s) but expected unloadable(%s) %p %s", unloadable ? "true" : "true", expectedUnloadableState ? "true" : "false", mh, path); - exit(0); } } -int main() -{ - printf("[BEGIN] _dyld_register_for_image_loads\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // Initially all images must not be unloadable as they are directly linked to the main executable expectedUnloadableState = false; _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); + FAIL("did not notify us about main executable"); } 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); + FAIL("did not notify us about libsystem_c.dylib"); } 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); + FAIL("did not notify us about libfoo.dylib"); } // These dlopen's can be unloaded expectedUnloadableState = true; // 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); - } + void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); + } 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); + FAIL("did not notify us about libfoo2.dylib"); } // 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); + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/foo.bundle", dlerror()); } 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); + FAIL("_dyld_register_for_image_loads() did not notify us about foo.bundle"); } // Upward linking with dlopen may confuse the notifier as we may try to notify twice on the upwardly linked image @@ -103,11 +93,9 @@ int main() // We should not get a duplicate notification on libup void* handleBar = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST); if ( handleBar == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libbar.dylib", dlerror()); - exit(0); + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libbar.dylib", dlerror()); } - printf("[PASS] _dyld_register_for_image_loads\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx b/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx index 905a153..eb1a05e 100644 --- a/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx +++ b/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx @@ -12,66 +12,54 @@ #include +#include "test_support.h" + extern mach_header __dso_handle; static std::unordered_set sCurrentImages; static void notify(const mach_header* mh, intptr_t vmaddr_slide) { - //fprintf(stderr, "mh=%p\n", mh); + LOG("mh=%p", mh); if ( sCurrentImages.count(mh) != 0 ) { - printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh); - exit(0); + FAIL("notified twice about %p", mh); } sCurrentImages.insert(mh); } -int main() -{ - printf("[BEGIN] _dyld_register_func_for_add_image\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { _dyld_register_func_for_add_image(¬ify); // verify we were notified about already loaded images if ( sCurrentImages.count(&__dso_handle) == 0 ) { - printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about main executable"); - exit(0); + FAIL("did not notify us about main executable"); } const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf); if ( sCurrentImages.count(libSysMH) == 0 ) { - printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libsystem_c.dylib"); - exit(0); + FAIL("did not notify us about libsystem_c.dylib"); } + + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); } - void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); - if ( handle == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); - exit(0); - } - - void* sym = dlsym(handle, "foo"); - if ( sym == NULL ) { - printf("[FAIL] dlsym(handle, \"foo\") failed"); - exit(0); - } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + } // verify we were notified about load of libfoo.dylib const mach_header* libfooMH = dyld_image_header_containing_address(sym); if ( sCurrentImages.count(libfooMH) == 0 ) { - printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libfoo.dylib"); - exit(0); + FAIL("_dyld_register_func_for_add_image() did not notify us about libfoo.dylib"); } - int result = dlclose(handle); if ( result != 0 ) { - printf("[FAIL] dlclose(handle) returned %d", result); - exit(0); + FAIL("dlclose(handle) returned %d", result); } - - printf("[PASS] _dyld_register_func_for_add_image\n"); - return 0; + PASS("_dyld_register_func_for_add_image"); } diff --git a/testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c b/testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c new file mode 100644 index 0000000..36c3e17 --- /dev/null +++ b/testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c @@ -0,0 +1,45 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/_dyld_shared_cache_contains_path.exe + +// RUN: ./_dyld_shared_cache_contains_path.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + size_t length; + bool hasCache = ( _dyld_get_shared_cache_range(&length) != NULL ); + + if ( _dyld_shared_cache_contains_path("/usr/lib/libSystem.B.dylib") != hasCache ) { + if ( hasCache ) + FAIL("libSystem.B.dylib is not in dyld cache"); + else + FAIL("no cache, but libSystem.B.dylib is in dyld cache"); + } + + if ( _dyld_shared_cache_contains_path("/System/Library/Frameworks/Foundation.framework/Foundation") != hasCache ) { + if ( hasCache ) + FAIL("Foundation.framework is not in dyld cache"); + else + FAIL("no cache, but Foundation.framework is in dyld cache"); + } + +#if TARGET_OS_OSX + if ( _dyld_shared_cache_contains_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") != hasCache ) { + if ( hasCache ) + FAIL("Current/Foundation.framework is not in dyld cache"); + else + FAIL("no cache, but Current/Foundation.framework is in dyld cache"); + } +#endif + + + PASS("SUCCESS"); +} + diff --git a/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c b/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c index 8672bb8..838dda8 100644 --- a/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c +++ b/testing/test-cases/_dyld_shared_cache_is_locally_built.dtest/main.c @@ -9,16 +9,12 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] _dyld_shared_cache_is_locally_built\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // We can't reliably test the result of this function, but it shouldn't crash _dyld_shared_cache_is_locally_built(); - printf("[PASS] _dyld_shared_cache_is_locally_built\n"); - - return 0; + PASS("SUCCESS"); } diff --git a/testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c b/testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c new file mode 100644 index 0000000..55ec8d2 --- /dev/null +++ b/testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c @@ -0,0 +1,81 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/_dyld_shared_cache_real_path.exe + +// RUN: ./_dyld_shared_cache_real_path.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + size_t length; + bool hasCache = ( _dyld_get_shared_cache_range(&length) != NULL ); + if ( hasCache ) { + const char* path = _dyld_shared_cache_real_path("/usr/lib/libSystem.dylib"); + if ( path == NULL ) + FAIL("libSystem.dylib is not in dyld cache"); + else if ( strcmp(path, "/usr/lib/libSystem.B.dylib") != 0 ) + FAIL("libSystem.B.dylib != %s", path); + +#if TARGET_OS_OSX + // actual path + path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation"); + if ( path == NULL ) + FAIL("Foundation is not in dyld cache"); + else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 ) + FAIL("Foundation != %s", path); + + // symlink inside the shared cache + path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Foundation"); + if ( path == NULL ) + FAIL("Foundation is not in dyld cache"); + else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 ) + FAIL("Foundation != %s", path); + + // symlink not in the shared cache (as we don't handle directory symlinks today) + path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation"); + if ( path == NULL ) + FAIL("Foundation is not in dyld cache"); + else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 ) + FAIL("Foundation != %s", path); +#endif + } else { + const char* path = _dyld_shared_cache_real_path("/usr/lib/libSystem.B.dylib"); + if ( path != NULL ) + FAIL("no cache, but libSystem.B.dylib is in dyld cache"); + } + +#if 0 + if ( != hasCache ) { + if ( hasCache ) + FAIL("libSystem.B.dylib is not in dyld cache"); + else + FAIL("no cache, but libSystem.B.dylib is in dyld cache"); + } + + if ( _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Foundation") != hasCache ) { + if ( hasCache ) + FAIL("Foundation.framework is not in dyld cache"); + else + FAIL("no cache, but Foundation.framework is in dyld cache"); + } + +#if TARGET_OS_OSX + if ( _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") != hasCache ) { + if ( hasCache ) + FAIL("Current/Foundation.framework is not in dyld cache"); + else + FAIL("no cache, but Current/Foundation.framework is in dyld cache"); + } +#endif + +#endif + + PASS("SUCCESS"); +} + diff --git a/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c b/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c index 5b999d2..d069f1d 100644 --- a/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c +++ b/testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c @@ -1,10 +1,10 @@ -// BUILD_ONLY: MacOSX - // BOOT_ARGS: dyld_flags=2 -// BUILD: $CC my.c -dynamiclib -o $BUILD_DIR/libmy.dylib -install_name $RUN_DIR/libmy.dylib -// BUILD: $CC main.c -o $BUILD_DIR/amfi-hardened-dlopen-leaf.exe -DHARDENED=1 -// BUILD: $CC main.c -o $BUILD_DIR/amfi-not-hardened-dlopen-leaf.exe +// BUILD(macos): $CC my.c -dynamiclib -o $BUILD_DIR/libmy.dylib -install_name $RUN_DIR/libmy.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/amfi-hardened-dlopen-leaf.exe -DHARDENED=1 +// BUILD(macos): $CC main.c -o $BUILD_DIR/amfi-not-hardened-dlopen-leaf.exe + +// BUILD(ios,tvos,watchos,bridgeos): // RUN: DYLD_AMFI_FAKE=0x14 ./amfi-hardened-dlopen-leaf.exe // RUN: DYLD_AMFI_FAKE=0x3F ./amfi-not-hardened-dlopen-leaf.exe @@ -18,42 +18,37 @@ #include #include +#include "test_support.h" + void tryPath(const char* prog, const char* path) { void* handle = dlopen(path, RTLD_LAZY); #if HARDENED if ( handle != NULL ) { - printf("[FAIL] %s dlopen(%s) unexpectedly succeeded\n", prog, path); + FAIL("dlopen(%s) unexpectedly succeeded", path); exit(0); } #else if ( handle == NULL ) { - printf("[FAIL] %s dlopen(%s) - %s\n", prog, path, dlerror()); + FAIL("dlopen(%s) - %s", path, dlerror()); exit(0); } #endif } -int main(int arg, const char* argv[]) -{ - printf("[BEGIN] %s\n", argv[0]); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify leaf name leads to dylib in /usr/lib/ void* handle = dlopen("libc.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] %s dlopen - %s\n", argv[0], dlerror()); - return 0; + FAIL("dlopen - %s", dlerror()); } // verify file system relative paths: hardened should fail tryPath(argv[0], "libmy.dylib"); tryPath(argv[0], "./libmy.dylib"); tryPath(argv[0], "../amfi-hardened-dlopen-leaf/libmy.dylib"); - - printf("[PASS] %s\n", argv[0]); - - return 0; + PASS("Succcess"); } diff --git a/testing/test-cases/amfi-interpose.dtest/interposer.c b/testing/test-cases/amfi-interpose.dtest/interposer.c new file mode 100644 index 0000000..a9e5c36 --- /dev/null +++ b/testing/test-cases/amfi-interpose.dtest/interposer.c @@ -0,0 +1,21 @@ +#include +#include +#include + + + +void* mymalloc(size_t size) +{ + // 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) +DYLD_INTERPOSE(myfree, free) diff --git a/testing/test-cases/amfi-interpose.dtest/main.c b/testing/test-cases/amfi-interpose.dtest/main.c new file mode 100644 index 0000000..6baea87 --- /dev/null +++ b/testing/test-cases/amfi-interpose.dtest/main.c @@ -0,0 +1,45 @@ +// BOOT_ARGS: dyld_flags=2 + +// BUILD: $CC interposer.c -dynamiclib -o $BUILD_DIR/libmyalloc.dylib -install_name $RUN_DIR/libmyalloc.dylib +// BUILD: $CC main.c $BUILD_DIR/libmyalloc.dylib -o $BUILD_DIR/amfi-interpose.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/amfi-interpose.exe + +// RUN: DYLD_AMFI_FAKE=0x7F ./amfi-interpose.exe +// RUN: DYLD_AMFI_FAKE=0x3F ./amfi-interpose.exe + +// +// Tests that AMFI_DYLD_OUTPUT_ALLOW_LIBRARY_INTERPOSING bit from AMFI blocks interposing +// + +#include +#include +#include +#include + +#include + +#include "test_support.h" + +int main() +{ + // interposed malloc() doubles alloction size and prefills allocation with '#' + char* p1 = malloc(10); + bool interposed = (strncmp(p1, "####################", 20) == 0); + + const char* amfiBits = getenv("DYLD_AMFI_FAKE"); + if ( amfiBits == NULL ) { + FAIL("amfi-interpose: DYLD_AMFI_FAKE not set"); + } +#ifdef AMFI_RETURNS_INTERPOSING_FLAG + bool allowInterposing = (strcmp(amfiBits, "0x7F") == 0); +#else + bool allowInterposing = true; +#endif + + if ( interposed == allowInterposing ) + PASS("Success"); + else if ( interposed ) + FAIL("amfi-interpose: malloc interposed, but amfi said to block it"); + else + FAIL("amfi-interpose: malloc not interposed, but amfi said to allow it"); +} diff --git a/testing/test-cases/bind-addend.dtest/main.c b/testing/test-cases/bind-addend.dtest/main.c index a059637..f5a917f 100644 --- a/testing/test-cases/bind-addend.dtest/main.c +++ b/testing/test-cases/bind-addend.dtest/main.c @@ -7,12 +7,18 @@ #include +#include "test_support.h" + // Note this is weak so that we have a bind __attribute__((weak)) void* p = 0; // Choose a large enough negative offset to be before the shared cache or the image +#if __LP64__ const uintptr_t offset = 1ULL << 36; +#else +const uintptr_t offset = 1ULL << 28; +#endif void* pMinus = (void*)((uintptr_t)&p - offset); // Get a pointer to something we assume is in the shared cache @@ -20,21 +26,15 @@ void* pMinus = (void*)((uintptr_t)&p - offset); extern int objc_msgSend; void* msgSendMinus = (void*)((uintptr_t)&objc_msgSend - offset); -int main() -{ - printf("[BEGIN] bind-addend\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( pMinus != (void*)((uintptr_t)&p - offset) ) { - printf("[FAIL] bind-addend: %p != %p\n", pMinus, (void*)((uintptr_t)&p - offset)); - return 0; + FAIL("bind-addend: %p != %p", pMinus, (void*)((uintptr_t)&p - offset)); } if ( msgSendMinus != (void*)((uintptr_t)&objc_msgSend - offset) ) { - printf("[FAIL] bind-addend: %p != %p\n", msgSendMinus, (void*)((uintptr_t)&objc_msgSend - offset)); - return 0; + FAIL("bind-addend: %p != %p", msgSendMinus, (void*)((uintptr_t)&objc_msgSend - offset)); } - printf("[PASS] bind-addend\n"); - return 0; + PASS("bind-addend"); } diff --git a/testing/test-cases/bind-rebase.dtest/main.c b/testing/test-cases/bind-rebase.dtest/main.c index 015bee1..b5498c8 100644 --- a/testing/test-cases/bind-rebase.dtest/main.c +++ b/testing/test-cases/bind-rebase.dtest/main.c @@ -9,15 +9,21 @@ #include #include +#include "test_support.h" + extern char tzname[]; // a char array in libSystem.dylib -#define VERIFY(a,b) assert(a==b) +#define VERIFY(a,b) assert(a==(b)) static uint8_t a; uint8_t* const rebasedPtrs[] = { NULL, NULL, &a, &a+1, &a+16, &a+1023, NULL, &a-1 }; +#if __LP64__ +uint8_t* const tbiPointers[] = { &a+0x8000000000000000, &a, &a+0x9000000000000000 }; +#endif + void verifyRebases() { VERIFY(rebasedPtrs[0], NULL); @@ -28,6 +34,10 @@ void verifyRebases() VERIFY(rebasedPtrs[5], &a+1023); VERIFY(rebasedPtrs[6], NULL); VERIFY(rebasedPtrs[7], &a-1); +#if __LP64__ + VERIFY(tbiPointers[0], &a + 0x8000000000000000); + VERIFY(tbiPointers[2], &a + 0x9000000000000000); +#endif } @@ -91,9 +101,7 @@ void verifyLongChains() } #endif -int main() -{ - printf("[BEGIN] bind-rebase\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { verifyRebases(); verifyBinds(); @@ -101,7 +109,6 @@ int main() verifyLongChains(); #endif - printf("[PASS] bind-rebase\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/chained-fixups-many-binds.dtest/foo.c b/testing/test-cases/chained-fixups-many-binds.dtest/foo.c index ea16a47..4100bde 100644 --- a/testing/test-cases/chained-fixups-many-binds.dtest/foo.c +++ b/testing/test-cases/chained-fixups-many-binds.dtest/foo.c @@ -64998,3 +64998,5003 @@ void foo64997() { } void foo64998() { } void foo64999() { } void foo65000() { } +void foo65001() { } +void foo65002() { } +void foo65003() { } +void foo65004() { } +void foo65005() { } +void foo65006() { } +void foo65007() { } +void foo65008() { } +void foo65009() { } +void foo65010() { } +void foo65011() { } +void foo65012() { } +void foo65013() { } +void foo65014() { } +void foo65015() { } +void foo65016() { } +void foo65017() { } +void foo65018() { } +void foo65019() { } +void foo65020() { } +void foo65021() { } +void foo65022() { } +void foo65023() { } +void foo65024() { } +void foo65025() { } +void foo65026() { } +void foo65027() { } +void foo65028() { } +void foo65029() { } +void foo65030() { } +void foo65031() { } +void foo65032() { } +void foo65033() { } +void foo65034() { } +void foo65035() { } +void foo65036() { } +void foo65037() { } +void foo65038() { } +void foo65039() { } +void foo65040() { } +void foo65041() { } +void foo65042() { } +void foo65043() { } +void foo65044() { } +void foo65045() { } +void foo65046() { } +void foo65047() { } +void foo65048() { } +void foo65049() { } +void foo65050() { } +void foo65051() { } +void foo65052() { } +void foo65053() { } +void foo65054() { } +void foo65055() { } +void foo65056() { } +void foo65057() { } +void foo65058() { } +void foo65059() { } +void foo65060() { } +void foo65061() { } +void foo65062() { } +void foo65063() { } +void foo65064() { } +void foo65065() { } +void foo65066() { } +void foo65067() { } +void foo65068() { } +void foo65069() { } +void foo65070() { } +void foo65071() { } +void foo65072() { } +void foo65073() { } +void foo65074() { } +void foo65075() { } +void foo65076() { } +void foo65077() { } +void foo65078() { } +void foo65079() { } +void foo65080() { } +void foo65081() { } +void foo65082() { } +void foo65083() { } +void foo65084() { } +void foo65085() { } +void foo65086() { } +void foo65087() { } +void foo65088() { } +void foo65089() { } +void foo65090() { } +void foo65091() { } +void foo65092() { } +void foo65093() { } +void foo65094() { } +void foo65095() { } +void foo65096() { } +void foo65097() { } +void foo65098() { } +void foo65099() { } +void foo65100() { } +void foo65101() { } +void foo65102() { } +void foo65103() { } +void foo65104() { } +void foo65105() { } +void foo65106() { } +void foo65107() { } +void foo65108() { } +void foo65109() { } +void foo65110() { } +void foo65111() { } +void foo65112() { } +void foo65113() { } +void foo65114() { } +void foo65115() { } +void foo65116() { } +void foo65117() { } +void foo65118() { } +void foo65119() { } +void foo65120() { } +void foo65121() { } +void foo65122() { } +void foo65123() { } +void foo65124() { } +void foo65125() { } +void foo65126() { } +void foo65127() { } +void foo65128() { } +void foo65129() { } +void foo65130() { } +void foo65131() { } +void foo65132() { } +void foo65133() { } +void foo65134() { } +void foo65135() { } +void foo65136() { } +void foo65137() { } +void foo65138() { } +void foo65139() { } +void foo65140() { } +void foo65141() { } +void foo65142() { } +void foo65143() { } +void foo65144() { } +void foo65145() { } +void foo65146() { } +void foo65147() { } +void foo65148() { } +void foo65149() { } +void foo65150() { } +void foo65151() { } +void foo65152() { } +void foo65153() { } +void foo65154() { } +void foo65155() { } +void foo65156() { } +void foo65157() { } +void foo65158() { } +void foo65159() { } +void foo65160() { } +void foo65161() { } +void foo65162() { } +void foo65163() { } +void foo65164() { } +void foo65165() { } +void foo65166() { } +void foo65167() { } +void foo65168() { } +void foo65169() { } +void foo65170() { } +void foo65171() { } +void foo65172() { } +void foo65173() { } +void foo65174() { } +void foo65175() { } +void foo65176() { } +void foo65177() { } +void foo65178() { } +void foo65179() { } +void foo65180() { } +void foo65181() { } +void foo65182() { } +void foo65183() { } +void foo65184() { } +void foo65185() { } +void foo65186() { } +void foo65187() { } +void foo65188() { } +void foo65189() { } +void foo65190() { } +void foo65191() { } +void foo65192() { } +void foo65193() { } +void foo65194() { } +void foo65195() { } +void foo65196() { } +void foo65197() { } +void foo65198() { } +void foo65199() { } +void foo65200() { } +void foo65201() { } +void foo65202() { } +void foo65203() { } +void foo65204() { } +void foo65205() { } +void foo65206() { } +void foo65207() { } +void foo65208() { } +void foo65209() { } +void foo65210() { } +void foo65211() { } +void foo65212() { } +void foo65213() { } +void foo65214() { } +void foo65215() { } +void foo65216() { } +void foo65217() { } +void foo65218() { } +void foo65219() { } +void foo65220() { } +void foo65221() { } +void foo65222() { } +void foo65223() { } +void foo65224() { } +void foo65225() { } +void foo65226() { } +void foo65227() { } +void foo65228() { } +void foo65229() { } +void foo65230() { } +void foo65231() { } +void foo65232() { } +void foo65233() { } +void foo65234() { } +void foo65235() { } +void foo65236() { } +void foo65237() { } +void foo65238() { } +void foo65239() { } +void foo65240() { } +void foo65241() { } +void foo65242() { } +void foo65243() { } +void foo65244() { } +void foo65245() { } +void foo65246() { } +void foo65247() { } +void foo65248() { } +void foo65249() { } +void foo65250() { } +void foo65251() { } +void foo65252() { } +void foo65253() { } +void foo65254() { } +void foo65255() { } +void foo65256() { } +void foo65257() { } +void foo65258() { } +void foo65259() { } +void foo65260() { } +void foo65261() { } +void foo65262() { } +void foo65263() { } +void foo65264() { } +void foo65265() { } +void foo65266() { } +void foo65267() { } +void foo65268() { } +void foo65269() { } +void foo65270() { } +void foo65271() { } +void foo65272() { } +void foo65273() { } +void foo65274() { } +void foo65275() { } +void foo65276() { } +void foo65277() { } +void foo65278() { } +void foo65279() { } +void foo65280() { } +void foo65281() { } +void foo65282() { } +void foo65283() { } +void foo65284() { } +void foo65285() { } +void foo65286() { } +void foo65287() { } +void foo65288() { } +void foo65289() { } +void foo65290() { } +void foo65291() { } +void foo65292() { } +void foo65293() { } +void foo65294() { } +void foo65295() { } +void foo65296() { } +void foo65297() { } +void foo65298() { } +void foo65299() { } +void foo65300() { } +void foo65301() { } +void foo65302() { } +void foo65303() { } +void foo65304() { } +void foo65305() { } +void foo65306() { } +void foo65307() { } +void foo65308() { } +void foo65309() { } +void foo65310() { } +void foo65311() { } +void foo65312() { } +void foo65313() { } +void foo65314() { } +void foo65315() { } +void foo65316() { } +void foo65317() { } +void foo65318() { } +void foo65319() { } +void foo65320() { } +void foo65321() { } +void foo65322() { } +void foo65323() { } +void foo65324() { } +void foo65325() { } +void foo65326() { } +void foo65327() { } +void foo65328() { } +void foo65329() { } +void foo65330() { } +void foo65331() { } +void foo65332() { } +void foo65333() { } +void foo65334() { } +void foo65335() { } +void foo65336() { } +void foo65337() { } +void foo65338() { } +void foo65339() { } +void foo65340() { } +void foo65341() { } +void foo65342() { } +void foo65343() { } +void foo65344() { } +void foo65345() { } +void foo65346() { } +void foo65347() { } +void foo65348() { } +void foo65349() { } +void foo65350() { } +void foo65351() { } +void foo65352() { } +void foo65353() { } +void foo65354() { } +void foo65355() { } +void foo65356() { } +void foo65357() { } +void foo65358() { } +void foo65359() { } +void foo65360() { } +void foo65361() { } +void foo65362() { } +void foo65363() { } +void foo65364() { } +void foo65365() { } +void foo65366() { } +void foo65367() { } +void foo65368() { } +void foo65369() { } +void foo65370() { } +void foo65371() { } +void foo65372() { } +void foo65373() { } +void foo65374() { } +void foo65375() { } +void foo65376() { } +void foo65377() { } +void foo65378() { } +void foo65379() { } +void foo65380() { } +void foo65381() { } +void foo65382() { } +void foo65383() { } +void foo65384() { } +void foo65385() { } +void foo65386() { } +void foo65387() { } +void foo65388() { } +void foo65389() { } +void foo65390() { } +void foo65391() { } +void foo65392() { } +void foo65393() { } +void foo65394() { } +void foo65395() { } +void foo65396() { } +void foo65397() { } +void foo65398() { } +void foo65399() { } +void foo65400() { } +void foo65401() { } +void foo65402() { } +void foo65403() { } +void foo65404() { } +void foo65405() { } +void foo65406() { } +void foo65407() { } +void foo65408() { } +void foo65409() { } +void foo65410() { } +void foo65411() { } +void foo65412() { } +void foo65413() { } +void foo65414() { } +void foo65415() { } +void foo65416() { } +void foo65417() { } +void foo65418() { } +void foo65419() { } +void foo65420() { } +void foo65421() { } +void foo65422() { } +void foo65423() { } +void foo65424() { } +void foo65425() { } +void foo65426() { } +void foo65427() { } +void foo65428() { } +void foo65429() { } +void foo65430() { } +void foo65431() { } +void foo65432() { } +void foo65433() { } +void foo65434() { } +void foo65435() { } +void foo65436() { } +void foo65437() { } +void foo65438() { } +void foo65439() { } +void foo65440() { } +void foo65441() { } +void foo65442() { } +void foo65443() { } +void foo65444() { } +void foo65445() { } +void foo65446() { } +void foo65447() { } +void foo65448() { } +void foo65449() { } +void foo65450() { } +void foo65451() { } +void foo65452() { } +void foo65453() { } +void foo65454() { } +void foo65455() { } +void foo65456() { } +void foo65457() { } +void foo65458() { } +void foo65459() { } +void foo65460() { } +void foo65461() { } +void foo65462() { } +void foo65463() { } +void foo65464() { } +void foo65465() { } +void foo65466() { } +void foo65467() { } +void foo65468() { } +void foo65469() { } +void foo65470() { } +void foo65471() { } +void foo65472() { } +void foo65473() { } +void foo65474() { } +void foo65475() { } +void foo65476() { } +void foo65477() { } +void foo65478() { } +void foo65479() { } +void foo65480() { } +void foo65481() { } +void foo65482() { } +void foo65483() { } +void foo65484() { } +void foo65485() { } +void foo65486() { } +void foo65487() { } +void foo65488() { } +void foo65489() { } +void foo65490() { } +void foo65491() { } +void foo65492() { } +void foo65493() { } +void foo65494() { } +void foo65495() { } +void foo65496() { } +void foo65497() { } +void foo65498() { } +void foo65499() { } +void foo65500() { } +void foo65501() { } +void foo65502() { } +void foo65503() { } +void foo65504() { } +void foo65505() { } +void foo65506() { } +void foo65507() { } +void foo65508() { } +void foo65509() { } +void foo65510() { } +void foo65511() { } +void foo65512() { } +void foo65513() { } +void foo65514() { } +void foo65515() { } +void foo65516() { } +void foo65517() { } +void foo65518() { } +void foo65519() { } +void foo65520() { } +void foo65521() { } +void foo65522() { } +void foo65523() { } +void foo65524() { } +void foo65525() { } +void foo65526() { } +void foo65527() { } +void foo65528() { } +void foo65529() { } +void foo65530() { } +void foo65531() { } +void foo65532() { } +void foo65533() { } +void foo65534() { } +void foo65535() { } +void foo65536() { } +void foo65537() { } +void foo65538() { } +void foo65539() { } +void foo65540() { } +void foo65541() { } +void foo65542() { } +void foo65543() { } +void foo65544() { } +void foo65545() { } +void foo65546() { } +void foo65547() { } +void foo65548() { } +void foo65549() { } +void foo65550() { } +void foo65551() { } +void foo65552() { } +void foo65553() { } +void foo65554() { } +void foo65555() { } +void foo65556() { } +void foo65557() { } +void foo65558() { } +void foo65559() { } +void foo65560() { } +void foo65561() { } +void foo65562() { } +void foo65563() { } +void foo65564() { } +void foo65565() { } +void foo65566() { } +void foo65567() { } +void foo65568() { } +void foo65569() { } +void foo65570() { } +void foo65571() { } +void foo65572() { } +void foo65573() { } +void foo65574() { } +void foo65575() { } +void foo65576() { } +void foo65577() { } +void foo65578() { } +void foo65579() { } +void foo65580() { } +void foo65581() { } +void foo65582() { } +void foo65583() { } +void foo65584() { } +void foo65585() { } +void foo65586() { } +void foo65587() { } +void foo65588() { } +void foo65589() { } +void foo65590() { } +void foo65591() { } +void foo65592() { } +void foo65593() { } +void foo65594() { } +void foo65595() { } +void foo65596() { } +void foo65597() { } +void foo65598() { } +void foo65599() { } +void foo65600() { } +void foo65601() { } +void foo65602() { } +void foo65603() { } +void foo65604() { } +void foo65605() { } +void foo65606() { } +void foo65607() { } +void foo65608() { } +void foo65609() { } +void foo65610() { } +void foo65611() { } +void foo65612() { } +void foo65613() { } +void foo65614() { } +void foo65615() { } +void foo65616() { } +void foo65617() { } +void foo65618() { } +void foo65619() { } +void foo65620() { } +void foo65621() { } +void foo65622() { } +void foo65623() { } +void foo65624() { } +void foo65625() { } +void foo65626() { } +void foo65627() { } +void foo65628() { } +void foo65629() { } +void foo65630() { } +void foo65631() { } +void foo65632() { } +void foo65633() { } +void foo65634() { } +void foo65635() { } +void foo65636() { } +void foo65637() { } +void foo65638() { } +void foo65639() { } +void foo65640() { } +void foo65641() { } +void foo65642() { } +void foo65643() { } +void foo65644() { } +void foo65645() { } +void foo65646() { } +void foo65647() { } +void foo65648() { } +void foo65649() { } +void foo65650() { } +void foo65651() { } +void foo65652() { } +void foo65653() { } +void foo65654() { } +void foo65655() { } +void foo65656() { } +void foo65657() { } +void foo65658() { } +void foo65659() { } +void foo65660() { } +void foo65661() { } +void foo65662() { } +void foo65663() { } +void foo65664() { } +void foo65665() { } +void foo65666() { } +void foo65667() { } +void foo65668() { } +void foo65669() { } +void foo65670() { } +void foo65671() { } +void foo65672() { } +void foo65673() { } +void foo65674() { } +void foo65675() { } +void foo65676() { } +void foo65677() { } +void foo65678() { } +void foo65679() { } +void foo65680() { } +void foo65681() { } +void foo65682() { } +void foo65683() { } +void foo65684() { } +void foo65685() { } +void foo65686() { } +void foo65687() { } +void foo65688() { } +void foo65689() { } +void foo65690() { } +void foo65691() { } +void foo65692() { } +void foo65693() { } +void foo65694() { } +void foo65695() { } +void foo65696() { } +void foo65697() { } +void foo65698() { } +void foo65699() { } +void foo65700() { } +void foo65701() { } +void foo65702() { } +void foo65703() { } +void foo65704() { } +void foo65705() { } +void foo65706() { } +void foo65707() { } +void foo65708() { } +void foo65709() { } +void foo65710() { } +void foo65711() { } +void foo65712() { } +void foo65713() { } +void foo65714() { } +void foo65715() { } +void foo65716() { } +void foo65717() { } +void foo65718() { } +void foo65719() { } +void foo65720() { } +void foo65721() { } +void foo65722() { } +void foo65723() { } +void foo65724() { } +void foo65725() { } +void foo65726() { } +void foo65727() { } +void foo65728() { } +void foo65729() { } +void foo65730() { } +void foo65731() { } +void foo65732() { } +void foo65733() { } +void foo65734() { } +void foo65735() { } +void foo65736() { } +void foo65737() { } +void foo65738() { } +void foo65739() { } +void foo65740() { } +void foo65741() { } +void foo65742() { } +void foo65743() { } +void foo65744() { } +void foo65745() { } +void foo65746() { } +void foo65747() { } +void foo65748() { } +void foo65749() { } +void foo65750() { } +void foo65751() { } +void foo65752() { } +void foo65753() { } +void foo65754() { } +void foo65755() { } +void foo65756() { } +void foo65757() { } +void foo65758() { } +void foo65759() { } +void foo65760() { } +void foo65761() { } +void foo65762() { } +void foo65763() { } +void foo65764() { } +void foo65765() { } +void foo65766() { } +void foo65767() { } +void foo65768() { } +void foo65769() { } +void foo65770() { } +void foo65771() { } +void foo65772() { } +void foo65773() { } +void foo65774() { } +void foo65775() { } +void foo65776() { } +void foo65777() { } +void foo65778() { } +void foo65779() { } +void foo65780() { } +void foo65781() { } +void foo65782() { } +void foo65783() { } +void foo65784() { } +void foo65785() { } +void foo65786() { } +void foo65787() { } +void foo65788() { } +void foo65789() { } +void foo65790() { } +void foo65791() { } +void foo65792() { } +void foo65793() { } +void foo65794() { } +void foo65795() { } +void foo65796() { } +void foo65797() { } +void foo65798() { } +void foo65799() { } +void foo65800() { } +void foo65801() { } +void foo65802() { } +void foo65803() { } +void foo65804() { } +void foo65805() { } +void foo65806() { } +void foo65807() { } +void foo65808() { } +void foo65809() { } +void foo65810() { } +void foo65811() { } +void foo65812() { } +void foo65813() { } +void foo65814() { } +void foo65815() { } +void foo65816() { } +void foo65817() { } +void foo65818() { } +void foo65819() { } +void foo65820() { } +void foo65821() { } +void foo65822() { } +void foo65823() { } +void foo65824() { } +void foo65825() { } +void foo65826() { } +void foo65827() { } +void foo65828() { } +void foo65829() { } +void foo65830() { } +void foo65831() { } +void foo65832() { } +void foo65833() { } +void foo65834() { } +void foo65835() { } +void foo65836() { } +void foo65837() { } +void foo65838() { } +void foo65839() { } +void foo65840() { } +void foo65841() { } +void foo65842() { } +void foo65843() { } +void foo65844() { } +void foo65845() { } +void foo65846() { } +void foo65847() { } +void foo65848() { } +void foo65849() { } +void foo65850() { } +void foo65851() { } +void foo65852() { } +void foo65853() { } +void foo65854() { } +void foo65855() { } +void foo65856() { } +void foo65857() { } +void foo65858() { } +void foo65859() { } +void foo65860() { } +void foo65861() { } +void foo65862() { } +void foo65863() { } +void foo65864() { } +void foo65865() { } +void foo65866() { } +void foo65867() { } +void foo65868() { } +void foo65869() { } +void foo65870() { } +void foo65871() { } +void foo65872() { } +void foo65873() { } +void foo65874() { } +void foo65875() { } +void foo65876() { } +void foo65877() { } +void foo65878() { } +void foo65879() { } +void foo65880() { } +void foo65881() { } +void foo65882() { } +void foo65883() { } +void foo65884() { } +void foo65885() { } +void foo65886() { } +void foo65887() { } +void foo65888() { } +void foo65889() { } +void foo65890() { } +void foo65891() { } +void foo65892() { } +void foo65893() { } +void foo65894() { } +void foo65895() { } +void foo65896() { } +void foo65897() { } +void foo65898() { } +void foo65899() { } +void foo65900() { } +void foo65901() { } +void foo65902() { } +void foo65903() { } +void foo65904() { } +void foo65905() { } +void foo65906() { } +void foo65907() { } +void foo65908() { } +void foo65909() { } +void foo65910() { } +void foo65911() { } +void foo65912() { } +void foo65913() { } +void foo65914() { } +void foo65915() { } +void foo65916() { } +void foo65917() { } +void foo65918() { } +void foo65919() { } +void foo65920() { } +void foo65921() { } +void foo65922() { } +void foo65923() { } +void foo65924() { } +void foo65925() { } +void foo65926() { } +void foo65927() { } +void foo65928() { } +void foo65929() { } +void foo65930() { } +void foo65931() { } +void foo65932() { } +void foo65933() { } +void foo65934() { } +void foo65935() { } +void foo65936() { } +void foo65937() { } +void foo65938() { } +void foo65939() { } +void foo65940() { } +void foo65941() { } +void foo65942() { } +void foo65943() { } +void foo65944() { } +void foo65945() { } +void foo65946() { } +void foo65947() { } +void foo65948() { } +void foo65949() { } +void foo65950() { } +void foo65951() { } +void foo65952() { } +void foo65953() { } +void foo65954() { } +void foo65955() { } +void foo65956() { } +void foo65957() { } +void foo65958() { } +void foo65959() { } +void foo65960() { } +void foo65961() { } +void foo65962() { } +void foo65963() { } +void foo65964() { } +void foo65965() { } +void foo65966() { } +void foo65967() { } +void foo65968() { } +void foo65969() { } +void foo65970() { } +void foo65971() { } +void foo65972() { } +void foo65973() { } +void foo65974() { } +void foo65975() { } +void foo65976() { } +void foo65977() { } +void foo65978() { } +void foo65979() { } +void foo65980() { } +void foo65981() { } +void foo65982() { } +void foo65983() { } +void foo65984() { } +void foo65985() { } +void foo65986() { } +void foo65987() { } +void foo65988() { } +void foo65989() { } +void foo65990() { } +void foo65991() { } +void foo65992() { } +void foo65993() { } +void foo65994() { } +void foo65995() { } +void foo65996() { } +void foo65997() { } +void foo65998() { } +void foo65999() { } +void foo66000() { } +void foo66001() { } +void foo66002() { } +void foo66003() { } +void foo66004() { } +void foo66005() { } +void foo66006() { } +void foo66007() { } +void foo66008() { } +void foo66009() { } +void foo66010() { } +void foo66011() { } +void foo66012() { } +void foo66013() { } +void foo66014() { } +void foo66015() { } +void foo66016() { } +void foo66017() { } +void foo66018() { } +void foo66019() { } +void foo66020() { } +void foo66021() { } +void foo66022() { } +void foo66023() { } +void foo66024() { } +void foo66025() { } +void foo66026() { } +void foo66027() { } +void foo66028() { } +void foo66029() { } +void foo66030() { } +void foo66031() { } +void foo66032() { } +void foo66033() { } +void foo66034() { } +void foo66035() { } +void foo66036() { } +void foo66037() { } +void foo66038() { } +void foo66039() { } +void foo66040() { } +void foo66041() { } +void foo66042() { } +void foo66043() { } +void foo66044() { } +void foo66045() { } +void foo66046() { } +void foo66047() { } +void foo66048() { } +void foo66049() { } +void foo66050() { } +void foo66051() { } +void foo66052() { } +void foo66053() { } +void foo66054() { } +void foo66055() { } +void foo66056() { } +void foo66057() { } +void foo66058() { } +void foo66059() { } +void foo66060() { } +void foo66061() { } +void foo66062() { } +void foo66063() { } +void foo66064() { } +void foo66065() { } +void foo66066() { } +void foo66067() { } +void foo66068() { } +void foo66069() { } +void foo66070() { } +void foo66071() { } +void foo66072() { } +void foo66073() { } +void foo66074() { } +void foo66075() { } +void foo66076() { } +void foo66077() { } +void foo66078() { } +void foo66079() { } +void foo66080() { } +void foo66081() { } +void foo66082() { } +void foo66083() { } +void foo66084() { } +void foo66085() { } +void foo66086() { } +void foo66087() { } +void foo66088() { } +void foo66089() { } +void foo66090() { } +void foo66091() { } +void foo66092() { } +void foo66093() { } +void foo66094() { } +void foo66095() { } +void foo66096() { } +void foo66097() { } +void foo66098() { } +void foo66099() { } +void foo66100() { } +void foo66101() { } +void foo66102() { } +void foo66103() { } +void foo66104() { } +void foo66105() { } +void foo66106() { } +void foo66107() { } +void foo66108() { } +void foo66109() { } +void foo66110() { } +void foo66111() { } +void foo66112() { } +void foo66113() { } +void foo66114() { } +void foo66115() { } +void foo66116() { } +void foo66117() { } +void foo66118() { } +void foo66119() { } +void foo66120() { } +void foo66121() { } +void foo66122() { } +void foo66123() { } +void foo66124() { } +void foo66125() { } +void foo66126() { } +void foo66127() { } +void foo66128() { } +void foo66129() { } +void foo66130() { } +void foo66131() { } +void foo66132() { } +void foo66133() { } +void foo66134() { } +void foo66135() { } +void foo66136() { } +void foo66137() { } +void foo66138() { } +void foo66139() { } +void foo66140() { } +void foo66141() { } +void foo66142() { } +void foo66143() { } +void foo66144() { } +void foo66145() { } +void foo66146() { } +void foo66147() { } +void foo66148() { } +void foo66149() { } +void foo66150() { } +void foo66151() { } +void foo66152() { } +void foo66153() { } +void foo66154() { } +void foo66155() { } +void foo66156() { } +void foo66157() { } +void foo66158() { } +void foo66159() { } +void foo66160() { } +void foo66161() { } +void foo66162() { } +void foo66163() { } +void foo66164() { } +void foo66165() { } +void foo66166() { } +void foo66167() { } +void foo66168() { } +void foo66169() { } +void foo66170() { } +void foo66171() { } +void foo66172() { } +void foo66173() { } +void foo66174() { } +void foo66175() { } +void foo66176() { } +void foo66177() { } +void foo66178() { } +void foo66179() { } +void foo66180() { } +void foo66181() { } +void foo66182() { } +void foo66183() { } +void foo66184() { } +void foo66185() { } +void foo66186() { } +void foo66187() { } +void foo66188() { } +void foo66189() { } +void foo66190() { } +void foo66191() { } +void foo66192() { } +void foo66193() { } +void foo66194() { } +void foo66195() { } +void foo66196() { } +void foo66197() { } +void foo66198() { } +void foo66199() { } +void foo66200() { } +void foo66201() { } +void foo66202() { } +void foo66203() { } +void foo66204() { } +void foo66205() { } +void foo66206() { } +void foo66207() { } +void foo66208() { } +void foo66209() { } +void foo66210() { } +void foo66211() { } +void foo66212() { } +void foo66213() { } +void foo66214() { } +void foo66215() { } +void foo66216() { } +void foo66217() { } +void foo66218() { } +void foo66219() { } +void foo66220() { } +void foo66221() { } +void foo66222() { } +void foo66223() { } +void foo66224() { } +void foo66225() { } +void foo66226() { } +void foo66227() { } +void foo66228() { } +void foo66229() { } +void foo66230() { } +void foo66231() { } +void foo66232() { } +void foo66233() { } +void foo66234() { } +void foo66235() { } +void foo66236() { } +void foo66237() { } +void foo66238() { } +void foo66239() { } +void foo66240() { } +void foo66241() { } +void foo66242() { } +void foo66243() { } +void foo66244() { } +void foo66245() { } +void foo66246() { } +void foo66247() { } +void foo66248() { } +void foo66249() { } +void foo66250() { } +void foo66251() { } +void foo66252() { } +void foo66253() { } +void foo66254() { } +void foo66255() { } +void foo66256() { } +void foo66257() { } +void foo66258() { } +void foo66259() { } +void foo66260() { } +void foo66261() { } +void foo66262() { } +void foo66263() { } +void foo66264() { } +void foo66265() { } +void foo66266() { } +void foo66267() { } +void foo66268() { } +void foo66269() { } +void foo66270() { } +void foo66271() { } +void foo66272() { } +void foo66273() { } +void foo66274() { } +void foo66275() { } +void foo66276() { } +void foo66277() { } +void foo66278() { } +void foo66279() { } +void foo66280() { } +void foo66281() { } +void foo66282() { } +void foo66283() { } +void foo66284() { } +void foo66285() { } +void foo66286() { } +void foo66287() { } +void foo66288() { } +void foo66289() { } +void foo66290() { } +void foo66291() { } +void foo66292() { } +void foo66293() { } +void foo66294() { } +void foo66295() { } +void foo66296() { } +void foo66297() { } +void foo66298() { } +void foo66299() { } +void foo66300() { } +void foo66301() { } +void foo66302() { } +void foo66303() { } +void foo66304() { } +void foo66305() { } +void foo66306() { } +void foo66307() { } +void foo66308() { } +void foo66309() { } +void foo66310() { } +void foo66311() { } +void foo66312() { } +void foo66313() { } +void foo66314() { } +void foo66315() { } +void foo66316() { } +void foo66317() { } +void foo66318() { } +void foo66319() { } +void foo66320() { } +void foo66321() { } +void foo66322() { } +void foo66323() { } +void foo66324() { } +void foo66325() { } +void foo66326() { } +void foo66327() { } +void foo66328() { } +void foo66329() { } +void foo66330() { } +void foo66331() { } +void foo66332() { } +void foo66333() { } +void foo66334() { } +void foo66335() { } +void foo66336() { } +void foo66337() { } +void foo66338() { } +void foo66339() { } +void foo66340() { } +void foo66341() { } +void foo66342() { } +void foo66343() { } +void foo66344() { } +void foo66345() { } +void foo66346() { } +void foo66347() { } +void foo66348() { } +void foo66349() { } +void foo66350() { } +void foo66351() { } +void foo66352() { } +void foo66353() { } +void foo66354() { } +void foo66355() { } +void foo66356() { } +void foo66357() { } +void foo66358() { } +void foo66359() { } +void foo66360() { } +void foo66361() { } +void foo66362() { } +void foo66363() { } +void foo66364() { } +void foo66365() { } +void foo66366() { } +void foo66367() { } +void foo66368() { } +void foo66369() { } +void foo66370() { } +void foo66371() { } +void foo66372() { } +void foo66373() { } +void foo66374() { } +void foo66375() { } +void foo66376() { } +void foo66377() { } +void foo66378() { } +void foo66379() { } +void foo66380() { } +void foo66381() { } +void foo66382() { } +void foo66383() { } +void foo66384() { } +void foo66385() { } +void foo66386() { } +void foo66387() { } +void foo66388() { } +void foo66389() { } +void foo66390() { } +void foo66391() { } +void foo66392() { } +void foo66393() { } +void foo66394() { } +void foo66395() { } +void foo66396() { } +void foo66397() { } +void foo66398() { } +void foo66399() { } +void foo66400() { } +void foo66401() { } +void foo66402() { } +void foo66403() { } +void foo66404() { } +void foo66405() { } +void foo66406() { } +void foo66407() { } +void foo66408() { } +void foo66409() { } +void foo66410() { } +void foo66411() { } +void foo66412() { } +void foo66413() { } +void foo66414() { } +void foo66415() { } +void foo66416() { } +void foo66417() { } +void foo66418() { } +void foo66419() { } +void foo66420() { } +void foo66421() { } +void foo66422() { } +void foo66423() { } +void foo66424() { } +void foo66425() { } +void foo66426() { } +void foo66427() { } +void foo66428() { } +void foo66429() { } +void foo66430() { } +void foo66431() { } +void foo66432() { } +void foo66433() { } +void foo66434() { } +void foo66435() { } +void foo66436() { } +void foo66437() { } +void foo66438() { } +void foo66439() { } +void foo66440() { } +void foo66441() { } +void foo66442() { } +void foo66443() { } +void foo66444() { } +void foo66445() { } +void foo66446() { } +void foo66447() { } +void foo66448() { } +void foo66449() { } +void foo66450() { } +void foo66451() { } +void foo66452() { } +void foo66453() { } +void foo66454() { } +void foo66455() { } +void foo66456() { } +void foo66457() { } +void foo66458() { } +void foo66459() { } +void foo66460() { } +void foo66461() { } +void foo66462() { } +void foo66463() { } +void foo66464() { } +void foo66465() { } +void foo66466() { } +void foo66467() { } +void foo66468() { } +void foo66469() { } +void foo66470() { } +void foo66471() { } +void foo66472() { } +void foo66473() { } +void foo66474() { } +void foo66475() { } +void foo66476() { } +void foo66477() { } +void foo66478() { } +void foo66479() { } +void foo66480() { } +void foo66481() { } +void foo66482() { } +void foo66483() { } +void foo66484() { } +void foo66485() { } +void foo66486() { } +void foo66487() { } +void foo66488() { } +void foo66489() { } +void foo66490() { } +void foo66491() { } +void foo66492() { } +void foo66493() { } +void foo66494() { } +void foo66495() { } +void foo66496() { } +void foo66497() { } +void foo66498() { } +void foo66499() { } +void foo66500() { } +void foo66501() { } +void foo66502() { } +void foo66503() { } +void foo66504() { } +void foo66505() { } +void foo66506() { } +void foo66507() { } +void foo66508() { } +void foo66509() { } +void foo66510() { } +void foo66511() { } +void foo66512() { } +void foo66513() { } +void foo66514() { } +void foo66515() { } +void foo66516() { } +void foo66517() { } +void foo66518() { } +void foo66519() { } +void foo66520() { } +void foo66521() { } +void foo66522() { } +void foo66523() { } +void foo66524() { } +void foo66525() { } +void foo66526() { } +void foo66527() { } +void foo66528() { } +void foo66529() { } +void foo66530() { } +void foo66531() { } +void foo66532() { } +void foo66533() { } +void foo66534() { } +void foo66535() { } +void foo66536() { } +void foo66537() { } +void foo66538() { } +void foo66539() { } +void foo66540() { } +void foo66541() { } +void foo66542() { } +void foo66543() { } +void foo66544() { } +void foo66545() { } +void foo66546() { } +void foo66547() { } +void foo66548() { } +void foo66549() { } +void foo66550() { } +void foo66551() { } +void foo66552() { } +void foo66553() { } +void foo66554() { } +void foo66555() { } +void foo66556() { } +void foo66557() { } +void foo66558() { } +void foo66559() { } +void foo66560() { } +void foo66561() { } +void foo66562() { } +void foo66563() { } +void foo66564() { } +void foo66565() { } +void foo66566() { } +void foo66567() { } +void foo66568() { } +void foo66569() { } +void foo66570() { } +void foo66571() { } +void foo66572() { } +void foo66573() { } +void foo66574() { } +void foo66575() { } +void foo66576() { } +void foo66577() { } +void foo66578() { } +void foo66579() { } +void foo66580() { } +void foo66581() { } +void foo66582() { } +void foo66583() { } +void foo66584() { } +void foo66585() { } +void foo66586() { } +void foo66587() { } +void foo66588() { } +void foo66589() { } +void foo66590() { } +void foo66591() { } +void foo66592() { } +void foo66593() { } +void foo66594() { } +void foo66595() { } +void foo66596() { } +void foo66597() { } +void foo66598() { } +void foo66599() { } +void foo66600() { } +void foo66601() { } +void foo66602() { } +void foo66603() { } +void foo66604() { } +void foo66605() { } +void foo66606() { } +void foo66607() { } +void foo66608() { } +void foo66609() { } +void foo66610() { } +void foo66611() { } +void foo66612() { } +void foo66613() { } +void foo66614() { } +void foo66615() { } +void foo66616() { } +void foo66617() { } +void foo66618() { } +void foo66619() { } +void foo66620() { } +void foo66621() { } +void foo66622() { } +void foo66623() { } +void foo66624() { } +void foo66625() { } +void foo66626() { } +void foo66627() { } +void foo66628() { } +void foo66629() { } +void foo66630() { } +void foo66631() { } +void foo66632() { } +void foo66633() { } +void foo66634() { } +void foo66635() { } +void foo66636() { } +void foo66637() { } +void foo66638() { } +void foo66639() { } +void foo66640() { } +void foo66641() { } +void foo66642() { } +void foo66643() { } +void foo66644() { } +void foo66645() { } +void foo66646() { } +void foo66647() { } +void foo66648() { } +void foo66649() { } +void foo66650() { } +void foo66651() { } +void foo66652() { } +void foo66653() { } +void foo66654() { } +void foo66655() { } +void foo66656() { } +void foo66657() { } +void foo66658() { } +void foo66659() { } +void foo66660() { } +void foo66661() { } +void foo66662() { } +void foo66663() { } +void foo66664() { } +void foo66665() { } +void foo66666() { } +void foo66667() { } +void foo66668() { } +void foo66669() { } +void foo66670() { } +void foo66671() { } +void foo66672() { } +void foo66673() { } +void foo66674() { } +void foo66675() { } +void foo66676() { } +void foo66677() { } +void foo66678() { } +void foo66679() { } +void foo66680() { } +void foo66681() { } +void foo66682() { } +void foo66683() { } +void foo66684() { } +void foo66685() { } +void foo66686() { } +void foo66687() { } +void foo66688() { } +void foo66689() { } +void foo66690() { } +void foo66691() { } +void foo66692() { } +void foo66693() { } +void foo66694() { } +void foo66695() { } +void foo66696() { } +void foo66697() { } +void foo66698() { } +void foo66699() { } +void foo66700() { } +void foo66701() { } +void foo66702() { } +void foo66703() { } +void foo66704() { } +void foo66705() { } +void foo66706() { } +void foo66707() { } +void foo66708() { } +void foo66709() { } +void foo66710() { } +void foo66711() { } +void foo66712() { } +void foo66713() { } +void foo66714() { } +void foo66715() { } +void foo66716() { } +void foo66717() { } +void foo66718() { } +void foo66719() { } +void foo66720() { } +void foo66721() { } +void foo66722() { } +void foo66723() { } +void foo66724() { } +void foo66725() { } +void foo66726() { } +void foo66727() { } +void foo66728() { } +void foo66729() { } +void foo66730() { } +void foo66731() { } +void foo66732() { } +void foo66733() { } +void foo66734() { } +void foo66735() { } +void foo66736() { } +void foo66737() { } +void foo66738() { } +void foo66739() { } +void foo66740() { } +void foo66741() { } +void foo66742() { } +void foo66743() { } +void foo66744() { } +void foo66745() { } +void foo66746() { } +void foo66747() { } +void foo66748() { } +void foo66749() { } +void foo66750() { } +void foo66751() { } +void foo66752() { } +void foo66753() { } +void foo66754() { } +void foo66755() { } +void foo66756() { } +void foo66757() { } +void foo66758() { } +void foo66759() { } +void foo66760() { } +void foo66761() { } +void foo66762() { } +void foo66763() { } +void foo66764() { } +void foo66765() { } +void foo66766() { } +void foo66767() { } +void foo66768() { } +void foo66769() { } +void foo66770() { } +void foo66771() { } +void foo66772() { } +void foo66773() { } +void foo66774() { } +void foo66775() { } +void foo66776() { } +void foo66777() { } +void foo66778() { } +void foo66779() { } +void foo66780() { } +void foo66781() { } +void foo66782() { } +void foo66783() { } +void foo66784() { } +void foo66785() { } +void foo66786() { } +void foo66787() { } +void foo66788() { } +void foo66789() { } +void foo66790() { } +void foo66791() { } +void foo66792() { } +void foo66793() { } +void foo66794() { } +void foo66795() { } +void foo66796() { } +void foo66797() { } +void foo66798() { } +void foo66799() { } +void foo66800() { } +void foo66801() { } +void foo66802() { } +void foo66803() { } +void foo66804() { } +void foo66805() { } +void foo66806() { } +void foo66807() { } +void foo66808() { } +void foo66809() { } +void foo66810() { } +void foo66811() { } +void foo66812() { } +void foo66813() { } +void foo66814() { } +void foo66815() { } +void foo66816() { } +void foo66817() { } +void foo66818() { } +void foo66819() { } +void foo66820() { } +void foo66821() { } +void foo66822() { } +void foo66823() { } +void foo66824() { } +void foo66825() { } +void foo66826() { } +void foo66827() { } +void foo66828() { } +void foo66829() { } +void foo66830() { } +void foo66831() { } +void foo66832() { } +void foo66833() { } +void foo66834() { } +void foo66835() { } +void foo66836() { } +void foo66837() { } +void foo66838() { } +void foo66839() { } +void foo66840() { } +void foo66841() { } +void foo66842() { } +void foo66843() { } +void foo66844() { } +void foo66845() { } +void foo66846() { } +void foo66847() { } +void foo66848() { } +void foo66849() { } +void foo66850() { } +void foo66851() { } +void foo66852() { } +void foo66853() { } +void foo66854() { } +void foo66855() { } +void foo66856() { } +void foo66857() { } +void foo66858() { } +void foo66859() { } +void foo66860() { } +void foo66861() { } +void foo66862() { } +void foo66863() { } +void foo66864() { } +void foo66865() { } +void foo66866() { } +void foo66867() { } +void foo66868() { } +void foo66869() { } +void foo66870() { } +void foo66871() { } +void foo66872() { } +void foo66873() { } +void foo66874() { } +void foo66875() { } +void foo66876() { } +void foo66877() { } +void foo66878() { } +void foo66879() { } +void foo66880() { } +void foo66881() { } +void foo66882() { } +void foo66883() { } +void foo66884() { } +void foo66885() { } +void foo66886() { } +void foo66887() { } +void foo66888() { } +void foo66889() { } +void foo66890() { } +void foo66891() { } +void foo66892() { } +void foo66893() { } +void foo66894() { } +void foo66895() { } +void foo66896() { } +void foo66897() { } +void foo66898() { } +void foo66899() { } +void foo66900() { } +void foo66901() { } +void foo66902() { } +void foo66903() { } +void foo66904() { } +void foo66905() { } +void foo66906() { } +void foo66907() { } +void foo66908() { } +void foo66909() { } +void foo66910() { } +void foo66911() { } +void foo66912() { } +void foo66913() { } +void foo66914() { } +void foo66915() { } +void foo66916() { } +void foo66917() { } +void foo66918() { } +void foo66919() { } +void foo66920() { } +void foo66921() { } +void foo66922() { } +void foo66923() { } +void foo66924() { } +void foo66925() { } +void foo66926() { } +void foo66927() { } +void foo66928() { } +void foo66929() { } +void foo66930() { } +void foo66931() { } +void foo66932() { } +void foo66933() { } +void foo66934() { } +void foo66935() { } +void foo66936() { } +void foo66937() { } +void foo66938() { } +void foo66939() { } +void foo66940() { } +void foo66941() { } +void foo66942() { } +void foo66943() { } +void foo66944() { } +void foo66945() { } +void foo66946() { } +void foo66947() { } +void foo66948() { } +void foo66949() { } +void foo66950() { } +void foo66951() { } +void foo66952() { } +void foo66953() { } +void foo66954() { } +void foo66955() { } +void foo66956() { } +void foo66957() { } +void foo66958() { } +void foo66959() { } +void foo66960() { } +void foo66961() { } +void foo66962() { } +void foo66963() { } +void foo66964() { } +void foo66965() { } +void foo66966() { } +void foo66967() { } +void foo66968() { } +void foo66969() { } +void foo66970() { } +void foo66971() { } +void foo66972() { } +void foo66973() { } +void foo66974() { } +void foo66975() { } +void foo66976() { } +void foo66977() { } +void foo66978() { } +void foo66979() { } +void foo66980() { } +void foo66981() { } +void foo66982() { } +void foo66983() { } +void foo66984() { } +void foo66985() { } +void foo66986() { } +void foo66987() { } +void foo66988() { } +void foo66989() { } +void foo66990() { } +void foo66991() { } +void foo66992() { } +void foo66993() { } +void foo66994() { } +void foo66995() { } +void foo66996() { } +void foo66997() { } +void foo66998() { } +void foo66999() { } +void foo67000() { } +void foo67001() { } +void foo67002() { } +void foo67003() { } +void foo67004() { } +void foo67005() { } +void foo67006() { } +void foo67007() { } +void foo67008() { } +void foo67009() { } +void foo67010() { } +void foo67011() { } +void foo67012() { } +void foo67013() { } +void foo67014() { } +void foo67015() { } +void foo67016() { } +void foo67017() { } +void foo67018() { } +void foo67019() { } +void foo67020() { } +void foo67021() { } +void foo67022() { } +void foo67023() { } +void foo67024() { } +void foo67025() { } +void foo67026() { } +void foo67027() { } +void foo67028() { } +void foo67029() { } +void foo67030() { } +void foo67031() { } +void foo67032() { } +void foo67033() { } +void foo67034() { } +void foo67035() { } +void foo67036() { } +void foo67037() { } +void foo67038() { } +void foo67039() { } +void foo67040() { } +void foo67041() { } +void foo67042() { } +void foo67043() { } +void foo67044() { } +void foo67045() { } +void foo67046() { } +void foo67047() { } +void foo67048() { } +void foo67049() { } +void foo67050() { } +void foo67051() { } +void foo67052() { } +void foo67053() { } +void foo67054() { } +void foo67055() { } +void foo67056() { } +void foo67057() { } +void foo67058() { } +void foo67059() { } +void foo67060() { } +void foo67061() { } +void foo67062() { } +void foo67063() { } +void foo67064() { } +void foo67065() { } +void foo67066() { } +void foo67067() { } +void foo67068() { } +void foo67069() { } +void foo67070() { } +void foo67071() { } +void foo67072() { } +void foo67073() { } +void foo67074() { } +void foo67075() { } +void foo67076() { } +void foo67077() { } +void foo67078() { } +void foo67079() { } +void foo67080() { } +void foo67081() { } +void foo67082() { } +void foo67083() { } +void foo67084() { } +void foo67085() { } +void foo67086() { } +void foo67087() { } +void foo67088() { } +void foo67089() { } +void foo67090() { } +void foo67091() { } +void foo67092() { } +void foo67093() { } +void foo67094() { } +void foo67095() { } +void foo67096() { } +void foo67097() { } +void foo67098() { } +void foo67099() { } +void foo67100() { } +void foo67101() { } +void foo67102() { } +void foo67103() { } +void foo67104() { } +void foo67105() { } +void foo67106() { } +void foo67107() { } +void foo67108() { } +void foo67109() { } +void foo67110() { } +void foo67111() { } +void foo67112() { } +void foo67113() { } +void foo67114() { } +void foo67115() { } +void foo67116() { } +void foo67117() { } +void foo67118() { } +void foo67119() { } +void foo67120() { } +void foo67121() { } +void foo67122() { } +void foo67123() { } +void foo67124() { } +void foo67125() { } +void foo67126() { } +void foo67127() { } +void foo67128() { } +void foo67129() { } +void foo67130() { } +void foo67131() { } +void foo67132() { } +void foo67133() { } +void foo67134() { } +void foo67135() { } +void foo67136() { } +void foo67137() { } +void foo67138() { } +void foo67139() { } +void foo67140() { } +void foo67141() { } +void foo67142() { } +void foo67143() { } +void foo67144() { } +void foo67145() { } +void foo67146() { } +void foo67147() { } +void foo67148() { } +void foo67149() { } +void foo67150() { } +void foo67151() { } +void foo67152() { } +void foo67153() { } +void foo67154() { } +void foo67155() { } +void foo67156() { } +void foo67157() { } +void foo67158() { } +void foo67159() { } +void foo67160() { } +void foo67161() { } +void foo67162() { } +void foo67163() { } +void foo67164() { } +void foo67165() { } +void foo67166() { } +void foo67167() { } +void foo67168() { } +void foo67169() { } +void foo67170() { } +void foo67171() { } +void foo67172() { } +void foo67173() { } +void foo67174() { } +void foo67175() { } +void foo67176() { } +void foo67177() { } +void foo67178() { } +void foo67179() { } +void foo67180() { } +void foo67181() { } +void foo67182() { } +void foo67183() { } +void foo67184() { } +void foo67185() { } +void foo67186() { } +void foo67187() { } +void foo67188() { } +void foo67189() { } +void foo67190() { } +void foo67191() { } +void foo67192() { } +void foo67193() { } +void foo67194() { } +void foo67195() { } +void foo67196() { } +void foo67197() { } +void foo67198() { } +void foo67199() { } +void foo67200() { } +void foo67201() { } +void foo67202() { } +void foo67203() { } +void foo67204() { } +void foo67205() { } +void foo67206() { } +void foo67207() { } +void foo67208() { } +void foo67209() { } +void foo67210() { } +void foo67211() { } +void foo67212() { } +void foo67213() { } +void foo67214() { } +void foo67215() { } +void foo67216() { } +void foo67217() { } +void foo67218() { } +void foo67219() { } +void foo67220() { } +void foo67221() { } +void foo67222() { } +void foo67223() { } +void foo67224() { } +void foo67225() { } +void foo67226() { } +void foo67227() { } +void foo67228() { } +void foo67229() { } +void foo67230() { } +void foo67231() { } +void foo67232() { } +void foo67233() { } +void foo67234() { } +void foo67235() { } +void foo67236() { } +void foo67237() { } +void foo67238() { } +void foo67239() { } +void foo67240() { } +void foo67241() { } +void foo67242() { } +void foo67243() { } +void foo67244() { } +void foo67245() { } +void foo67246() { } +void foo67247() { } +void foo67248() { } +void foo67249() { } +void foo67250() { } +void foo67251() { } +void foo67252() { } +void foo67253() { } +void foo67254() { } +void foo67255() { } +void foo67256() { } +void foo67257() { } +void foo67258() { } +void foo67259() { } +void foo67260() { } +void foo67261() { } +void foo67262() { } +void foo67263() { } +void foo67264() { } +void foo67265() { } +void foo67266() { } +void foo67267() { } +void foo67268() { } +void foo67269() { } +void foo67270() { } +void foo67271() { } +void foo67272() { } +void foo67273() { } +void foo67274() { } +void foo67275() { } +void foo67276() { } +void foo67277() { } +void foo67278() { } +void foo67279() { } +void foo67280() { } +void foo67281() { } +void foo67282() { } +void foo67283() { } +void foo67284() { } +void foo67285() { } +void foo67286() { } +void foo67287() { } +void foo67288() { } +void foo67289() { } +void foo67290() { } +void foo67291() { } +void foo67292() { } +void foo67293() { } +void foo67294() { } +void foo67295() { } +void foo67296() { } +void foo67297() { } +void foo67298() { } +void foo67299() { } +void foo67300() { } +void foo67301() { } +void foo67302() { } +void foo67303() { } +void foo67304() { } +void foo67305() { } +void foo67306() { } +void foo67307() { } +void foo67308() { } +void foo67309() { } +void foo67310() { } +void foo67311() { } +void foo67312() { } +void foo67313() { } +void foo67314() { } +void foo67315() { } +void foo67316() { } +void foo67317() { } +void foo67318() { } +void foo67319() { } +void foo67320() { } +void foo67321() { } +void foo67322() { } +void foo67323() { } +void foo67324() { } +void foo67325() { } +void foo67326() { } +void foo67327() { } +void foo67328() { } +void foo67329() { } +void foo67330() { } +void foo67331() { } +void foo67332() { } +void foo67333() { } +void foo67334() { } +void foo67335() { } +void foo67336() { } +void foo67337() { } +void foo67338() { } +void foo67339() { } +void foo67340() { } +void foo67341() { } +void foo67342() { } +void foo67343() { } +void foo67344() { } +void foo67345() { } +void foo67346() { } +void foo67347() { } +void foo67348() { } +void foo67349() { } +void foo67350() { } +void foo67351() { } +void foo67352() { } +void foo67353() { } +void foo67354() { } +void foo67355() { } +void foo67356() { } +void foo67357() { } +void foo67358() { } +void foo67359() { } +void foo67360() { } +void foo67361() { } +void foo67362() { } +void foo67363() { } +void foo67364() { } +void foo67365() { } +void foo67366() { } +void foo67367() { } +void foo67368() { } +void foo67369() { } +void foo67370() { } +void foo67371() { } +void foo67372() { } +void foo67373() { } +void foo67374() { } +void foo67375() { } +void foo67376() { } +void foo67377() { } +void foo67378() { } +void foo67379() { } +void foo67380() { } +void foo67381() { } +void foo67382() { } +void foo67383() { } +void foo67384() { } +void foo67385() { } +void foo67386() { } +void foo67387() { } +void foo67388() { } +void foo67389() { } +void foo67390() { } +void foo67391() { } +void foo67392() { } +void foo67393() { } +void foo67394() { } +void foo67395() { } +void foo67396() { } +void foo67397() { } +void foo67398() { } +void foo67399() { } +void foo67400() { } +void foo67401() { } +void foo67402() { } +void foo67403() { } +void foo67404() { } +void foo67405() { } +void foo67406() { } +void foo67407() { } +void foo67408() { } +void foo67409() { } +void foo67410() { } +void foo67411() { } +void foo67412() { } +void foo67413() { } +void foo67414() { } +void foo67415() { } +void foo67416() { } +void foo67417() { } +void foo67418() { } +void foo67419() { } +void foo67420() { } +void foo67421() { } +void foo67422() { } +void foo67423() { } +void foo67424() { } +void foo67425() { } +void foo67426() { } +void foo67427() { } +void foo67428() { } +void foo67429() { } +void foo67430() { } +void foo67431() { } +void foo67432() { } +void foo67433() { } +void foo67434() { } +void foo67435() { } +void foo67436() { } +void foo67437() { } +void foo67438() { } +void foo67439() { } +void foo67440() { } +void foo67441() { } +void foo67442() { } +void foo67443() { } +void foo67444() { } +void foo67445() { } +void foo67446() { } +void foo67447() { } +void foo67448() { } +void foo67449() { } +void foo67450() { } +void foo67451() { } +void foo67452() { } +void foo67453() { } +void foo67454() { } +void foo67455() { } +void foo67456() { } +void foo67457() { } +void foo67458() { } +void foo67459() { } +void foo67460() { } +void foo67461() { } +void foo67462() { } +void foo67463() { } +void foo67464() { } +void foo67465() { } +void foo67466() { } +void foo67467() { } +void foo67468() { } +void foo67469() { } +void foo67470() { } +void foo67471() { } +void foo67472() { } +void foo67473() { } +void foo67474() { } +void foo67475() { } +void foo67476() { } +void foo67477() { } +void foo67478() { } +void foo67479() { } +void foo67480() { } +void foo67481() { } +void foo67482() { } +void foo67483() { } +void foo67484() { } +void foo67485() { } +void foo67486() { } +void foo67487() { } +void foo67488() { } +void foo67489() { } +void foo67490() { } +void foo67491() { } +void foo67492() { } +void foo67493() { } +void foo67494() { } +void foo67495() { } +void foo67496() { } +void foo67497() { } +void foo67498() { } +void foo67499() { } +void foo67500() { } +void foo67501() { } +void foo67502() { } +void foo67503() { } +void foo67504() { } +void foo67505() { } +void foo67506() { } +void foo67507() { } +void foo67508() { } +void foo67509() { } +void foo67510() { } +void foo67511() { } +void foo67512() { } +void foo67513() { } +void foo67514() { } +void foo67515() { } +void foo67516() { } +void foo67517() { } +void foo67518() { } +void foo67519() { } +void foo67520() { } +void foo67521() { } +void foo67522() { } +void foo67523() { } +void foo67524() { } +void foo67525() { } +void foo67526() { } +void foo67527() { } +void foo67528() { } +void foo67529() { } +void foo67530() { } +void foo67531() { } +void foo67532() { } +void foo67533() { } +void foo67534() { } +void foo67535() { } +void foo67536() { } +void foo67537() { } +void foo67538() { } +void foo67539() { } +void foo67540() { } +void foo67541() { } +void foo67542() { } +void foo67543() { } +void foo67544() { } +void foo67545() { } +void foo67546() { } +void foo67547() { } +void foo67548() { } +void foo67549() { } +void foo67550() { } +void foo67551() { } +void foo67552() { } +void foo67553() { } +void foo67554() { } +void foo67555() { } +void foo67556() { } +void foo67557() { } +void foo67558() { } +void foo67559() { } +void foo67560() { } +void foo67561() { } +void foo67562() { } +void foo67563() { } +void foo67564() { } +void foo67565() { } +void foo67566() { } +void foo67567() { } +void foo67568() { } +void foo67569() { } +void foo67570() { } +void foo67571() { } +void foo67572() { } +void foo67573() { } +void foo67574() { } +void foo67575() { } +void foo67576() { } +void foo67577() { } +void foo67578() { } +void foo67579() { } +void foo67580() { } +void foo67581() { } +void foo67582() { } +void foo67583() { } +void foo67584() { } +void foo67585() { } +void foo67586() { } +void foo67587() { } +void foo67588() { } +void foo67589() { } +void foo67590() { } +void foo67591() { } +void foo67592() { } +void foo67593() { } +void foo67594() { } +void foo67595() { } +void foo67596() { } +void foo67597() { } +void foo67598() { } +void foo67599() { } +void foo67600() { } +void foo67601() { } +void foo67602() { } +void foo67603() { } +void foo67604() { } +void foo67605() { } +void foo67606() { } +void foo67607() { } +void foo67608() { } +void foo67609() { } +void foo67610() { } +void foo67611() { } +void foo67612() { } +void foo67613() { } +void foo67614() { } +void foo67615() { } +void foo67616() { } +void foo67617() { } +void foo67618() { } +void foo67619() { } +void foo67620() { } +void foo67621() { } +void foo67622() { } +void foo67623() { } +void foo67624() { } +void foo67625() { } +void foo67626() { } +void foo67627() { } +void foo67628() { } +void foo67629() { } +void foo67630() { } +void foo67631() { } +void foo67632() { } +void foo67633() { } +void foo67634() { } +void foo67635() { } +void foo67636() { } +void foo67637() { } +void foo67638() { } +void foo67639() { } +void foo67640() { } +void foo67641() { } +void foo67642() { } +void foo67643() { } +void foo67644() { } +void foo67645() { } +void foo67646() { } +void foo67647() { } +void foo67648() { } +void foo67649() { } +void foo67650() { } +void foo67651() { } +void foo67652() { } +void foo67653() { } +void foo67654() { } +void foo67655() { } +void foo67656() { } +void foo67657() { } +void foo67658() { } +void foo67659() { } +void foo67660() { } +void foo67661() { } +void foo67662() { } +void foo67663() { } +void foo67664() { } +void foo67665() { } +void foo67666() { } +void foo67667() { } +void foo67668() { } +void foo67669() { } +void foo67670() { } +void foo67671() { } +void foo67672() { } +void foo67673() { } +void foo67674() { } +void foo67675() { } +void foo67676() { } +void foo67677() { } +void foo67678() { } +void foo67679() { } +void foo67680() { } +void foo67681() { } +void foo67682() { } +void foo67683() { } +void foo67684() { } +void foo67685() { } +void foo67686() { } +void foo67687() { } +void foo67688() { } +void foo67689() { } +void foo67690() { } +void foo67691() { } +void foo67692() { } +void foo67693() { } +void foo67694() { } +void foo67695() { } +void foo67696() { } +void foo67697() { } +void foo67698() { } +void foo67699() { } +void foo67700() { } +void foo67701() { } +void foo67702() { } +void foo67703() { } +void foo67704() { } +void foo67705() { } +void foo67706() { } +void foo67707() { } +void foo67708() { } +void foo67709() { } +void foo67710() { } +void foo67711() { } +void foo67712() { } +void foo67713() { } +void foo67714() { } +void foo67715() { } +void foo67716() { } +void foo67717() { } +void foo67718() { } +void foo67719() { } +void foo67720() { } +void foo67721() { } +void foo67722() { } +void foo67723() { } +void foo67724() { } +void foo67725() { } +void foo67726() { } +void foo67727() { } +void foo67728() { } +void foo67729() { } +void foo67730() { } +void foo67731() { } +void foo67732() { } +void foo67733() { } +void foo67734() { } +void foo67735() { } +void foo67736() { } +void foo67737() { } +void foo67738() { } +void foo67739() { } +void foo67740() { } +void foo67741() { } +void foo67742() { } +void foo67743() { } +void foo67744() { } +void foo67745() { } +void foo67746() { } +void foo67747() { } +void foo67748() { } +void foo67749() { } +void foo67750() { } +void foo67751() { } +void foo67752() { } +void foo67753() { } +void foo67754() { } +void foo67755() { } +void foo67756() { } +void foo67757() { } +void foo67758() { } +void foo67759() { } +void foo67760() { } +void foo67761() { } +void foo67762() { } +void foo67763() { } +void foo67764() { } +void foo67765() { } +void foo67766() { } +void foo67767() { } +void foo67768() { } +void foo67769() { } +void foo67770() { } +void foo67771() { } +void foo67772() { } +void foo67773() { } +void foo67774() { } +void foo67775() { } +void foo67776() { } +void foo67777() { } +void foo67778() { } +void foo67779() { } +void foo67780() { } +void foo67781() { } +void foo67782() { } +void foo67783() { } +void foo67784() { } +void foo67785() { } +void foo67786() { } +void foo67787() { } +void foo67788() { } +void foo67789() { } +void foo67790() { } +void foo67791() { } +void foo67792() { } +void foo67793() { } +void foo67794() { } +void foo67795() { } +void foo67796() { } +void foo67797() { } +void foo67798() { } +void foo67799() { } +void foo67800() { } +void foo67801() { } +void foo67802() { } +void foo67803() { } +void foo67804() { } +void foo67805() { } +void foo67806() { } +void foo67807() { } +void foo67808() { } +void foo67809() { } +void foo67810() { } +void foo67811() { } +void foo67812() { } +void foo67813() { } +void foo67814() { } +void foo67815() { } +void foo67816() { } +void foo67817() { } +void foo67818() { } +void foo67819() { } +void foo67820() { } +void foo67821() { } +void foo67822() { } +void foo67823() { } +void foo67824() { } +void foo67825() { } +void foo67826() { } +void foo67827() { } +void foo67828() { } +void foo67829() { } +void foo67830() { } +void foo67831() { } +void foo67832() { } +void foo67833() { } +void foo67834() { } +void foo67835() { } +void foo67836() { } +void foo67837() { } +void foo67838() { } +void foo67839() { } +void foo67840() { } +void foo67841() { } +void foo67842() { } +void foo67843() { } +void foo67844() { } +void foo67845() { } +void foo67846() { } +void foo67847() { } +void foo67848() { } +void foo67849() { } +void foo67850() { } +void foo67851() { } +void foo67852() { } +void foo67853() { } +void foo67854() { } +void foo67855() { } +void foo67856() { } +void foo67857() { } +void foo67858() { } +void foo67859() { } +void foo67860() { } +void foo67861() { } +void foo67862() { } +void foo67863() { } +void foo67864() { } +void foo67865() { } +void foo67866() { } +void foo67867() { } +void foo67868() { } +void foo67869() { } +void foo67870() { } +void foo67871() { } +void foo67872() { } +void foo67873() { } +void foo67874() { } +void foo67875() { } +void foo67876() { } +void foo67877() { } +void foo67878() { } +void foo67879() { } +void foo67880() { } +void foo67881() { } +void foo67882() { } +void foo67883() { } +void foo67884() { } +void foo67885() { } +void foo67886() { } +void foo67887() { } +void foo67888() { } +void foo67889() { } +void foo67890() { } +void foo67891() { } +void foo67892() { } +void foo67893() { } +void foo67894() { } +void foo67895() { } +void foo67896() { } +void foo67897() { } +void foo67898() { } +void foo67899() { } +void foo67900() { } +void foo67901() { } +void foo67902() { } +void foo67903() { } +void foo67904() { } +void foo67905() { } +void foo67906() { } +void foo67907() { } +void foo67908() { } +void foo67909() { } +void foo67910() { } +void foo67911() { } +void foo67912() { } +void foo67913() { } +void foo67914() { } +void foo67915() { } +void foo67916() { } +void foo67917() { } +void foo67918() { } +void foo67919() { } +void foo67920() { } +void foo67921() { } +void foo67922() { } +void foo67923() { } +void foo67924() { } +void foo67925() { } +void foo67926() { } +void foo67927() { } +void foo67928() { } +void foo67929() { } +void foo67930() { } +void foo67931() { } +void foo67932() { } +void foo67933() { } +void foo67934() { } +void foo67935() { } +void foo67936() { } +void foo67937() { } +void foo67938() { } +void foo67939() { } +void foo67940() { } +void foo67941() { } +void foo67942() { } +void foo67943() { } +void foo67944() { } +void foo67945() { } +void foo67946() { } +void foo67947() { } +void foo67948() { } +void foo67949() { } +void foo67950() { } +void foo67951() { } +void foo67952() { } +void foo67953() { } +void foo67954() { } +void foo67955() { } +void foo67956() { } +void foo67957() { } +void foo67958() { } +void foo67959() { } +void foo67960() { } +void foo67961() { } +void foo67962() { } +void foo67963() { } +void foo67964() { } +void foo67965() { } +void foo67966() { } +void foo67967() { } +void foo67968() { } +void foo67969() { } +void foo67970() { } +void foo67971() { } +void foo67972() { } +void foo67973() { } +void foo67974() { } +void foo67975() { } +void foo67976() { } +void foo67977() { } +void foo67978() { } +void foo67979() { } +void foo67980() { } +void foo67981() { } +void foo67982() { } +void foo67983() { } +void foo67984() { } +void foo67985() { } +void foo67986() { } +void foo67987() { } +void foo67988() { } +void foo67989() { } +void foo67990() { } +void foo67991() { } +void foo67992() { } +void foo67993() { } +void foo67994() { } +void foo67995() { } +void foo67996() { } +void foo67997() { } +void foo67998() { } +void foo67999() { } +void foo68000() { } +void foo68001() { } +void foo68002() { } +void foo68003() { } +void foo68004() { } +void foo68005() { } +void foo68006() { } +void foo68007() { } +void foo68008() { } +void foo68009() { } +void foo68010() { } +void foo68011() { } +void foo68012() { } +void foo68013() { } +void foo68014() { } +void foo68015() { } +void foo68016() { } +void foo68017() { } +void foo68018() { } +void foo68019() { } +void foo68020() { } +void foo68021() { } +void foo68022() { } +void foo68023() { } +void foo68024() { } +void foo68025() { } +void foo68026() { } +void foo68027() { } +void foo68028() { } +void foo68029() { } +void foo68030() { } +void foo68031() { } +void foo68032() { } +void foo68033() { } +void foo68034() { } +void foo68035() { } +void foo68036() { } +void foo68037() { } +void foo68038() { } +void foo68039() { } +void foo68040() { } +void foo68041() { } +void foo68042() { } +void foo68043() { } +void foo68044() { } +void foo68045() { } +void foo68046() { } +void foo68047() { } +void foo68048() { } +void foo68049() { } +void foo68050() { } +void foo68051() { } +void foo68052() { } +void foo68053() { } +void foo68054() { } +void foo68055() { } +void foo68056() { } +void foo68057() { } +void foo68058() { } +void foo68059() { } +void foo68060() { } +void foo68061() { } +void foo68062() { } +void foo68063() { } +void foo68064() { } +void foo68065() { } +void foo68066() { } +void foo68067() { } +void foo68068() { } +void foo68069() { } +void foo68070() { } +void foo68071() { } +void foo68072() { } +void foo68073() { } +void foo68074() { } +void foo68075() { } +void foo68076() { } +void foo68077() { } +void foo68078() { } +void foo68079() { } +void foo68080() { } +void foo68081() { } +void foo68082() { } +void foo68083() { } +void foo68084() { } +void foo68085() { } +void foo68086() { } +void foo68087() { } +void foo68088() { } +void foo68089() { } +void foo68090() { } +void foo68091() { } +void foo68092() { } +void foo68093() { } +void foo68094() { } +void foo68095() { } +void foo68096() { } +void foo68097() { } +void foo68098() { } +void foo68099() { } +void foo68100() { } +void foo68101() { } +void foo68102() { } +void foo68103() { } +void foo68104() { } +void foo68105() { } +void foo68106() { } +void foo68107() { } +void foo68108() { } +void foo68109() { } +void foo68110() { } +void foo68111() { } +void foo68112() { } +void foo68113() { } +void foo68114() { } +void foo68115() { } +void foo68116() { } +void foo68117() { } +void foo68118() { } +void foo68119() { } +void foo68120() { } +void foo68121() { } +void foo68122() { } +void foo68123() { } +void foo68124() { } +void foo68125() { } +void foo68126() { } +void foo68127() { } +void foo68128() { } +void foo68129() { } +void foo68130() { } +void foo68131() { } +void foo68132() { } +void foo68133() { } +void foo68134() { } +void foo68135() { } +void foo68136() { } +void foo68137() { } +void foo68138() { } +void foo68139() { } +void foo68140() { } +void foo68141() { } +void foo68142() { } +void foo68143() { } +void foo68144() { } +void foo68145() { } +void foo68146() { } +void foo68147() { } +void foo68148() { } +void foo68149() { } +void foo68150() { } +void foo68151() { } +void foo68152() { } +void foo68153() { } +void foo68154() { } +void foo68155() { } +void foo68156() { } +void foo68157() { } +void foo68158() { } +void foo68159() { } +void foo68160() { } +void foo68161() { } +void foo68162() { } +void foo68163() { } +void foo68164() { } +void foo68165() { } +void foo68166() { } +void foo68167() { } +void foo68168() { } +void foo68169() { } +void foo68170() { } +void foo68171() { } +void foo68172() { } +void foo68173() { } +void foo68174() { } +void foo68175() { } +void foo68176() { } +void foo68177() { } +void foo68178() { } +void foo68179() { } +void foo68180() { } +void foo68181() { } +void foo68182() { } +void foo68183() { } +void foo68184() { } +void foo68185() { } +void foo68186() { } +void foo68187() { } +void foo68188() { } +void foo68189() { } +void foo68190() { } +void foo68191() { } +void foo68192() { } +void foo68193() { } +void foo68194() { } +void foo68195() { } +void foo68196() { } +void foo68197() { } +void foo68198() { } +void foo68199() { } +void foo68200() { } +void foo68201() { } +void foo68202() { } +void foo68203() { } +void foo68204() { } +void foo68205() { } +void foo68206() { } +void foo68207() { } +void foo68208() { } +void foo68209() { } +void foo68210() { } +void foo68211() { } +void foo68212() { } +void foo68213() { } +void foo68214() { } +void foo68215() { } +void foo68216() { } +void foo68217() { } +void foo68218() { } +void foo68219() { } +void foo68220() { } +void foo68221() { } +void foo68222() { } +void foo68223() { } +void foo68224() { } +void foo68225() { } +void foo68226() { } +void foo68227() { } +void foo68228() { } +void foo68229() { } +void foo68230() { } +void foo68231() { } +void foo68232() { } +void foo68233() { } +void foo68234() { } +void foo68235() { } +void foo68236() { } +void foo68237() { } +void foo68238() { } +void foo68239() { } +void foo68240() { } +void foo68241() { } +void foo68242() { } +void foo68243() { } +void foo68244() { } +void foo68245() { } +void foo68246() { } +void foo68247() { } +void foo68248() { } +void foo68249() { } +void foo68250() { } +void foo68251() { } +void foo68252() { } +void foo68253() { } +void foo68254() { } +void foo68255() { } +void foo68256() { } +void foo68257() { } +void foo68258() { } +void foo68259() { } +void foo68260() { } +void foo68261() { } +void foo68262() { } +void foo68263() { } +void foo68264() { } +void foo68265() { } +void foo68266() { } +void foo68267() { } +void foo68268() { } +void foo68269() { } +void foo68270() { } +void foo68271() { } +void foo68272() { } +void foo68273() { } +void foo68274() { } +void foo68275() { } +void foo68276() { } +void foo68277() { } +void foo68278() { } +void foo68279() { } +void foo68280() { } +void foo68281() { } +void foo68282() { } +void foo68283() { } +void foo68284() { } +void foo68285() { } +void foo68286() { } +void foo68287() { } +void foo68288() { } +void foo68289() { } +void foo68290() { } +void foo68291() { } +void foo68292() { } +void foo68293() { } +void foo68294() { } +void foo68295() { } +void foo68296() { } +void foo68297() { } +void foo68298() { } +void foo68299() { } +void foo68300() { } +void foo68301() { } +void foo68302() { } +void foo68303() { } +void foo68304() { } +void foo68305() { } +void foo68306() { } +void foo68307() { } +void foo68308() { } +void foo68309() { } +void foo68310() { } +void foo68311() { } +void foo68312() { } +void foo68313() { } +void foo68314() { } +void foo68315() { } +void foo68316() { } +void foo68317() { } +void foo68318() { } +void foo68319() { } +void foo68320() { } +void foo68321() { } +void foo68322() { } +void foo68323() { } +void foo68324() { } +void foo68325() { } +void foo68326() { } +void foo68327() { } +void foo68328() { } +void foo68329() { } +void foo68330() { } +void foo68331() { } +void foo68332() { } +void foo68333() { } +void foo68334() { } +void foo68335() { } +void foo68336() { } +void foo68337() { } +void foo68338() { } +void foo68339() { } +void foo68340() { } +void foo68341() { } +void foo68342() { } +void foo68343() { } +void foo68344() { } +void foo68345() { } +void foo68346() { } +void foo68347() { } +void foo68348() { } +void foo68349() { } +void foo68350() { } +void foo68351() { } +void foo68352() { } +void foo68353() { } +void foo68354() { } +void foo68355() { } +void foo68356() { } +void foo68357() { } +void foo68358() { } +void foo68359() { } +void foo68360() { } +void foo68361() { } +void foo68362() { } +void foo68363() { } +void foo68364() { } +void foo68365() { } +void foo68366() { } +void foo68367() { } +void foo68368() { } +void foo68369() { } +void foo68370() { } +void foo68371() { } +void foo68372() { } +void foo68373() { } +void foo68374() { } +void foo68375() { } +void foo68376() { } +void foo68377() { } +void foo68378() { } +void foo68379() { } +void foo68380() { } +void foo68381() { } +void foo68382() { } +void foo68383() { } +void foo68384() { } +void foo68385() { } +void foo68386() { } +void foo68387() { } +void foo68388() { } +void foo68389() { } +void foo68390() { } +void foo68391() { } +void foo68392() { } +void foo68393() { } +void foo68394() { } +void foo68395() { } +void foo68396() { } +void foo68397() { } +void foo68398() { } +void foo68399() { } +void foo68400() { } +void foo68401() { } +void foo68402() { } +void foo68403() { } +void foo68404() { } +void foo68405() { } +void foo68406() { } +void foo68407() { } +void foo68408() { } +void foo68409() { } +void foo68410() { } +void foo68411() { } +void foo68412() { } +void foo68413() { } +void foo68414() { } +void foo68415() { } +void foo68416() { } +void foo68417() { } +void foo68418() { } +void foo68419() { } +void foo68420() { } +void foo68421() { } +void foo68422() { } +void foo68423() { } +void foo68424() { } +void foo68425() { } +void foo68426() { } +void foo68427() { } +void foo68428() { } +void foo68429() { } +void foo68430() { } +void foo68431() { } +void foo68432() { } +void foo68433() { } +void foo68434() { } +void foo68435() { } +void foo68436() { } +void foo68437() { } +void foo68438() { } +void foo68439() { } +void foo68440() { } +void foo68441() { } +void foo68442() { } +void foo68443() { } +void foo68444() { } +void foo68445() { } +void foo68446() { } +void foo68447() { } +void foo68448() { } +void foo68449() { } +void foo68450() { } +void foo68451() { } +void foo68452() { } +void foo68453() { } +void foo68454() { } +void foo68455() { } +void foo68456() { } +void foo68457() { } +void foo68458() { } +void foo68459() { } +void foo68460() { } +void foo68461() { } +void foo68462() { } +void foo68463() { } +void foo68464() { } +void foo68465() { } +void foo68466() { } +void foo68467() { } +void foo68468() { } +void foo68469() { } +void foo68470() { } +void foo68471() { } +void foo68472() { } +void foo68473() { } +void foo68474() { } +void foo68475() { } +void foo68476() { } +void foo68477() { } +void foo68478() { } +void foo68479() { } +void foo68480() { } +void foo68481() { } +void foo68482() { } +void foo68483() { } +void foo68484() { } +void foo68485() { } +void foo68486() { } +void foo68487() { } +void foo68488() { } +void foo68489() { } +void foo68490() { } +void foo68491() { } +void foo68492() { } +void foo68493() { } +void foo68494() { } +void foo68495() { } +void foo68496() { } +void foo68497() { } +void foo68498() { } +void foo68499() { } +void foo68500() { } +void foo68501() { } +void foo68502() { } +void foo68503() { } +void foo68504() { } +void foo68505() { } +void foo68506() { } +void foo68507() { } +void foo68508() { } +void foo68509() { } +void foo68510() { } +void foo68511() { } +void foo68512() { } +void foo68513() { } +void foo68514() { } +void foo68515() { } +void foo68516() { } +void foo68517() { } +void foo68518() { } +void foo68519() { } +void foo68520() { } +void foo68521() { } +void foo68522() { } +void foo68523() { } +void foo68524() { } +void foo68525() { } +void foo68526() { } +void foo68527() { } +void foo68528() { } +void foo68529() { } +void foo68530() { } +void foo68531() { } +void foo68532() { } +void foo68533() { } +void foo68534() { } +void foo68535() { } +void foo68536() { } +void foo68537() { } +void foo68538() { } +void foo68539() { } +void foo68540() { } +void foo68541() { } +void foo68542() { } +void foo68543() { } +void foo68544() { } +void foo68545() { } +void foo68546() { } +void foo68547() { } +void foo68548() { } +void foo68549() { } +void foo68550() { } +void foo68551() { } +void foo68552() { } +void foo68553() { } +void foo68554() { } +void foo68555() { } +void foo68556() { } +void foo68557() { } +void foo68558() { } +void foo68559() { } +void foo68560() { } +void foo68561() { } +void foo68562() { } +void foo68563() { } +void foo68564() { } +void foo68565() { } +void foo68566() { } +void foo68567() { } +void foo68568() { } +void foo68569() { } +void foo68570() { } +void foo68571() { } +void foo68572() { } +void foo68573() { } +void foo68574() { } +void foo68575() { } +void foo68576() { } +void foo68577() { } +void foo68578() { } +void foo68579() { } +void foo68580() { } +void foo68581() { } +void foo68582() { } +void foo68583() { } +void foo68584() { } +void foo68585() { } +void foo68586() { } +void foo68587() { } +void foo68588() { } +void foo68589() { } +void foo68590() { } +void foo68591() { } +void foo68592() { } +void foo68593() { } +void foo68594() { } +void foo68595() { } +void foo68596() { } +void foo68597() { } +void foo68598() { } +void foo68599() { } +void foo68600() { } +void foo68601() { } +void foo68602() { } +void foo68603() { } +void foo68604() { } +void foo68605() { } +void foo68606() { } +void foo68607() { } +void foo68608() { } +void foo68609() { } +void foo68610() { } +void foo68611() { } +void foo68612() { } +void foo68613() { } +void foo68614() { } +void foo68615() { } +void foo68616() { } +void foo68617() { } +void foo68618() { } +void foo68619() { } +void foo68620() { } +void foo68621() { } +void foo68622() { } +void foo68623() { } +void foo68624() { } +void foo68625() { } +void foo68626() { } +void foo68627() { } +void foo68628() { } +void foo68629() { } +void foo68630() { } +void foo68631() { } +void foo68632() { } +void foo68633() { } +void foo68634() { } +void foo68635() { } +void foo68636() { } +void foo68637() { } +void foo68638() { } +void foo68639() { } +void foo68640() { } +void foo68641() { } +void foo68642() { } +void foo68643() { } +void foo68644() { } +void foo68645() { } +void foo68646() { } +void foo68647() { } +void foo68648() { } +void foo68649() { } +void foo68650() { } +void foo68651() { } +void foo68652() { } +void foo68653() { } +void foo68654() { } +void foo68655() { } +void foo68656() { } +void foo68657() { } +void foo68658() { } +void foo68659() { } +void foo68660() { } +void foo68661() { } +void foo68662() { } +void foo68663() { } +void foo68664() { } +void foo68665() { } +void foo68666() { } +void foo68667() { } +void foo68668() { } +void foo68669() { } +void foo68670() { } +void foo68671() { } +void foo68672() { } +void foo68673() { } +void foo68674() { } +void foo68675() { } +void foo68676() { } +void foo68677() { } +void foo68678() { } +void foo68679() { } +void foo68680() { } +void foo68681() { } +void foo68682() { } +void foo68683() { } +void foo68684() { } +void foo68685() { } +void foo68686() { } +void foo68687() { } +void foo68688() { } +void foo68689() { } +void foo68690() { } +void foo68691() { } +void foo68692() { } +void foo68693() { } +void foo68694() { } +void foo68695() { } +void foo68696() { } +void foo68697() { } +void foo68698() { } +void foo68699() { } +void foo68700() { } +void foo68701() { } +void foo68702() { } +void foo68703() { } +void foo68704() { } +void foo68705() { } +void foo68706() { } +void foo68707() { } +void foo68708() { } +void foo68709() { } +void foo68710() { } +void foo68711() { } +void foo68712() { } +void foo68713() { } +void foo68714() { } +void foo68715() { } +void foo68716() { } +void foo68717() { } +void foo68718() { } +void foo68719() { } +void foo68720() { } +void foo68721() { } +void foo68722() { } +void foo68723() { } +void foo68724() { } +void foo68725() { } +void foo68726() { } +void foo68727() { } +void foo68728() { } +void foo68729() { } +void foo68730() { } +void foo68731() { } +void foo68732() { } +void foo68733() { } +void foo68734() { } +void foo68735() { } +void foo68736() { } +void foo68737() { } +void foo68738() { } +void foo68739() { } +void foo68740() { } +void foo68741() { } +void foo68742() { } +void foo68743() { } +void foo68744() { } +void foo68745() { } +void foo68746() { } +void foo68747() { } +void foo68748() { } +void foo68749() { } +void foo68750() { } +void foo68751() { } +void foo68752() { } +void foo68753() { } +void foo68754() { } +void foo68755() { } +void foo68756() { } +void foo68757() { } +void foo68758() { } +void foo68759() { } +void foo68760() { } +void foo68761() { } +void foo68762() { } +void foo68763() { } +void foo68764() { } +void foo68765() { } +void foo68766() { } +void foo68767() { } +void foo68768() { } +void foo68769() { } +void foo68770() { } +void foo68771() { } +void foo68772() { } +void foo68773() { } +void foo68774() { } +void foo68775() { } +void foo68776() { } +void foo68777() { } +void foo68778() { } +void foo68779() { } +void foo68780() { } +void foo68781() { } +void foo68782() { } +void foo68783() { } +void foo68784() { } +void foo68785() { } +void foo68786() { } +void foo68787() { } +void foo68788() { } +void foo68789() { } +void foo68790() { } +void foo68791() { } +void foo68792() { } +void foo68793() { } +void foo68794() { } +void foo68795() { } +void foo68796() { } +void foo68797() { } +void foo68798() { } +void foo68799() { } +void foo68800() { } +void foo68801() { } +void foo68802() { } +void foo68803() { } +void foo68804() { } +void foo68805() { } +void foo68806() { } +void foo68807() { } +void foo68808() { } +void foo68809() { } +void foo68810() { } +void foo68811() { } +void foo68812() { } +void foo68813() { } +void foo68814() { } +void foo68815() { } +void foo68816() { } +void foo68817() { } +void foo68818() { } +void foo68819() { } +void foo68820() { } +void foo68821() { } +void foo68822() { } +void foo68823() { } +void foo68824() { } +void foo68825() { } +void foo68826() { } +void foo68827() { } +void foo68828() { } +void foo68829() { } +void foo68830() { } +void foo68831() { } +void foo68832() { } +void foo68833() { } +void foo68834() { } +void foo68835() { } +void foo68836() { } +void foo68837() { } +void foo68838() { } +void foo68839() { } +void foo68840() { } +void foo68841() { } +void foo68842() { } +void foo68843() { } +void foo68844() { } +void foo68845() { } +void foo68846() { } +void foo68847() { } +void foo68848() { } +void foo68849() { } +void foo68850() { } +void foo68851() { } +void foo68852() { } +void foo68853() { } +void foo68854() { } +void foo68855() { } +void foo68856() { } +void foo68857() { } +void foo68858() { } +void foo68859() { } +void foo68860() { } +void foo68861() { } +void foo68862() { } +void foo68863() { } +void foo68864() { } +void foo68865() { } +void foo68866() { } +void foo68867() { } +void foo68868() { } +void foo68869() { } +void foo68870() { } +void foo68871() { } +void foo68872() { } +void foo68873() { } +void foo68874() { } +void foo68875() { } +void foo68876() { } +void foo68877() { } +void foo68878() { } +void foo68879() { } +void foo68880() { } +void foo68881() { } +void foo68882() { } +void foo68883() { } +void foo68884() { } +void foo68885() { } +void foo68886() { } +void foo68887() { } +void foo68888() { } +void foo68889() { } +void foo68890() { } +void foo68891() { } +void foo68892() { } +void foo68893() { } +void foo68894() { } +void foo68895() { } +void foo68896() { } +void foo68897() { } +void foo68898() { } +void foo68899() { } +void foo68900() { } +void foo68901() { } +void foo68902() { } +void foo68903() { } +void foo68904() { } +void foo68905() { } +void foo68906() { } +void foo68907() { } +void foo68908() { } +void foo68909() { } +void foo68910() { } +void foo68911() { } +void foo68912() { } +void foo68913() { } +void foo68914() { } +void foo68915() { } +void foo68916() { } +void foo68917() { } +void foo68918() { } +void foo68919() { } +void foo68920() { } +void foo68921() { } +void foo68922() { } +void foo68923() { } +void foo68924() { } +void foo68925() { } +void foo68926() { } +void foo68927() { } +void foo68928() { } +void foo68929() { } +void foo68930() { } +void foo68931() { } +void foo68932() { } +void foo68933() { } +void foo68934() { } +void foo68935() { } +void foo68936() { } +void foo68937() { } +void foo68938() { } +void foo68939() { } +void foo68940() { } +void foo68941() { } +void foo68942() { } +void foo68943() { } +void foo68944() { } +void foo68945() { } +void foo68946() { } +void foo68947() { } +void foo68948() { } +void foo68949() { } +void foo68950() { } +void foo68951() { } +void foo68952() { } +void foo68953() { } +void foo68954() { } +void foo68955() { } +void foo68956() { } +void foo68957() { } +void foo68958() { } +void foo68959() { } +void foo68960() { } +void foo68961() { } +void foo68962() { } +void foo68963() { } +void foo68964() { } +void foo68965() { } +void foo68966() { } +void foo68967() { } +void foo68968() { } +void foo68969() { } +void foo68970() { } +void foo68971() { } +void foo68972() { } +void foo68973() { } +void foo68974() { } +void foo68975() { } +void foo68976() { } +void foo68977() { } +void foo68978() { } +void foo68979() { } +void foo68980() { } +void foo68981() { } +void foo68982() { } +void foo68983() { } +void foo68984() { } +void foo68985() { } +void foo68986() { } +void foo68987() { } +void foo68988() { } +void foo68989() { } +void foo68990() { } +void foo68991() { } +void foo68992() { } +void foo68993() { } +void foo68994() { } +void foo68995() { } +void foo68996() { } +void foo68997() { } +void foo68998() { } +void foo68999() { } +void foo69000() { } +void foo69001() { } +void foo69002() { } +void foo69003() { } +void foo69004() { } +void foo69005() { } +void foo69006() { } +void foo69007() { } +void foo69008() { } +void foo69009() { } +void foo69010() { } +void foo69011() { } +void foo69012() { } +void foo69013() { } +void foo69014() { } +void foo69015() { } +void foo69016() { } +void foo69017() { } +void foo69018() { } +void foo69019() { } +void foo69020() { } +void foo69021() { } +void foo69022() { } +void foo69023() { } +void foo69024() { } +void foo69025() { } +void foo69026() { } +void foo69027() { } +void foo69028() { } +void foo69029() { } +void foo69030() { } +void foo69031() { } +void foo69032() { } +void foo69033() { } +void foo69034() { } +void foo69035() { } +void foo69036() { } +void foo69037() { } +void foo69038() { } +void foo69039() { } +void foo69040() { } +void foo69041() { } +void foo69042() { } +void foo69043() { } +void foo69044() { } +void foo69045() { } +void foo69046() { } +void foo69047() { } +void foo69048() { } +void foo69049() { } +void foo69050() { } +void foo69051() { } +void foo69052() { } +void foo69053() { } +void foo69054() { } +void foo69055() { } +void foo69056() { } +void foo69057() { } +void foo69058() { } +void foo69059() { } +void foo69060() { } +void foo69061() { } +void foo69062() { } +void foo69063() { } +void foo69064() { } +void foo69065() { } +void foo69066() { } +void foo69067() { } +void foo69068() { } +void foo69069() { } +void foo69070() { } +void foo69071() { } +void foo69072() { } +void foo69073() { } +void foo69074() { } +void foo69075() { } +void foo69076() { } +void foo69077() { } +void foo69078() { } +void foo69079() { } +void foo69080() { } +void foo69081() { } +void foo69082() { } +void foo69083() { } +void foo69084() { } +void foo69085() { } +void foo69086() { } +void foo69087() { } +void foo69088() { } +void foo69089() { } +void foo69090() { } +void foo69091() { } +void foo69092() { } +void foo69093() { } +void foo69094() { } +void foo69095() { } +void foo69096() { } +void foo69097() { } +void foo69098() { } +void foo69099() { } +void foo69100() { } +void foo69101() { } +void foo69102() { } +void foo69103() { } +void foo69104() { } +void foo69105() { } +void foo69106() { } +void foo69107() { } +void foo69108() { } +void foo69109() { } +void foo69110() { } +void foo69111() { } +void foo69112() { } +void foo69113() { } +void foo69114() { } +void foo69115() { } +void foo69116() { } +void foo69117() { } +void foo69118() { } +void foo69119() { } +void foo69120() { } +void foo69121() { } +void foo69122() { } +void foo69123() { } +void foo69124() { } +void foo69125() { } +void foo69126() { } +void foo69127() { } +void foo69128() { } +void foo69129() { } +void foo69130() { } +void foo69131() { } +void foo69132() { } +void foo69133() { } +void foo69134() { } +void foo69135() { } +void foo69136() { } +void foo69137() { } +void foo69138() { } +void foo69139() { } +void foo69140() { } +void foo69141() { } +void foo69142() { } +void foo69143() { } +void foo69144() { } +void foo69145() { } +void foo69146() { } +void foo69147() { } +void foo69148() { } +void foo69149() { } +void foo69150() { } +void foo69151() { } +void foo69152() { } +void foo69153() { } +void foo69154() { } +void foo69155() { } +void foo69156() { } +void foo69157() { } +void foo69158() { } +void foo69159() { } +void foo69160() { } +void foo69161() { } +void foo69162() { } +void foo69163() { } +void foo69164() { } +void foo69165() { } +void foo69166() { } +void foo69167() { } +void foo69168() { } +void foo69169() { } +void foo69170() { } +void foo69171() { } +void foo69172() { } +void foo69173() { } +void foo69174() { } +void foo69175() { } +void foo69176() { } +void foo69177() { } +void foo69178() { } +void foo69179() { } +void foo69180() { } +void foo69181() { } +void foo69182() { } +void foo69183() { } +void foo69184() { } +void foo69185() { } +void foo69186() { } +void foo69187() { } +void foo69188() { } +void foo69189() { } +void foo69190() { } +void foo69191() { } +void foo69192() { } +void foo69193() { } +void foo69194() { } +void foo69195() { } +void foo69196() { } +void foo69197() { } +void foo69198() { } +void foo69199() { } +void foo69200() { } +void foo69201() { } +void foo69202() { } +void foo69203() { } +void foo69204() { } +void foo69205() { } +void foo69206() { } +void foo69207() { } +void foo69208() { } +void foo69209() { } +void foo69210() { } +void foo69211() { } +void foo69212() { } +void foo69213() { } +void foo69214() { } +void foo69215() { } +void foo69216() { } +void foo69217() { } +void foo69218() { } +void foo69219() { } +void foo69220() { } +void foo69221() { } +void foo69222() { } +void foo69223() { } +void foo69224() { } +void foo69225() { } +void foo69226() { } +void foo69227() { } +void foo69228() { } +void foo69229() { } +void foo69230() { } +void foo69231() { } +void foo69232() { } +void foo69233() { } +void foo69234() { } +void foo69235() { } +void foo69236() { } +void foo69237() { } +void foo69238() { } +void foo69239() { } +void foo69240() { } +void foo69241() { } +void foo69242() { } +void foo69243() { } +void foo69244() { } +void foo69245() { } +void foo69246() { } +void foo69247() { } +void foo69248() { } +void foo69249() { } +void foo69250() { } +void foo69251() { } +void foo69252() { } +void foo69253() { } +void foo69254() { } +void foo69255() { } +void foo69256() { } +void foo69257() { } +void foo69258() { } +void foo69259() { } +void foo69260() { } +void foo69261() { } +void foo69262() { } +void foo69263() { } +void foo69264() { } +void foo69265() { } +void foo69266() { } +void foo69267() { } +void foo69268() { } +void foo69269() { } +void foo69270() { } +void foo69271() { } +void foo69272() { } +void foo69273() { } +void foo69274() { } +void foo69275() { } +void foo69276() { } +void foo69277() { } +void foo69278() { } +void foo69279() { } +void foo69280() { } +void foo69281() { } +void foo69282() { } +void foo69283() { } +void foo69284() { } +void foo69285() { } +void foo69286() { } +void foo69287() { } +void foo69288() { } +void foo69289() { } +void foo69290() { } +void foo69291() { } +void foo69292() { } +void foo69293() { } +void foo69294() { } +void foo69295() { } +void foo69296() { } +void foo69297() { } +void foo69298() { } +void foo69299() { } +void foo69300() { } +void foo69301() { } +void foo69302() { } +void foo69303() { } +void foo69304() { } +void foo69305() { } +void foo69306() { } +void foo69307() { } +void foo69308() { } +void foo69309() { } +void foo69310() { } +void foo69311() { } +void foo69312() { } +void foo69313() { } +void foo69314() { } +void foo69315() { } +void foo69316() { } +void foo69317() { } +void foo69318() { } +void foo69319() { } +void foo69320() { } +void foo69321() { } +void foo69322() { } +void foo69323() { } +void foo69324() { } +void foo69325() { } +void foo69326() { } +void foo69327() { } +void foo69328() { } +void foo69329() { } +void foo69330() { } +void foo69331() { } +void foo69332() { } +void foo69333() { } +void foo69334() { } +void foo69335() { } +void foo69336() { } +void foo69337() { } +void foo69338() { } +void foo69339() { } +void foo69340() { } +void foo69341() { } +void foo69342() { } +void foo69343() { } +void foo69344() { } +void foo69345() { } +void foo69346() { } +void foo69347() { } +void foo69348() { } +void foo69349() { } +void foo69350() { } +void foo69351() { } +void foo69352() { } +void foo69353() { } +void foo69354() { } +void foo69355() { } +void foo69356() { } +void foo69357() { } +void foo69358() { } +void foo69359() { } +void foo69360() { } +void foo69361() { } +void foo69362() { } +void foo69363() { } +void foo69364() { } +void foo69365() { } +void foo69366() { } +void foo69367() { } +void foo69368() { } +void foo69369() { } +void foo69370() { } +void foo69371() { } +void foo69372() { } +void foo69373() { } +void foo69374() { } +void foo69375() { } +void foo69376() { } +void foo69377() { } +void foo69378() { } +void foo69379() { } +void foo69380() { } +void foo69381() { } +void foo69382() { } +void foo69383() { } +void foo69384() { } +void foo69385() { } +void foo69386() { } +void foo69387() { } +void foo69388() { } +void foo69389() { } +void foo69390() { } +void foo69391() { } +void foo69392() { } +void foo69393() { } +void foo69394() { } +void foo69395() { } +void foo69396() { } +void foo69397() { } +void foo69398() { } +void foo69399() { } +void foo69400() { } +void foo69401() { } +void foo69402() { } +void foo69403() { } +void foo69404() { } +void foo69405() { } +void foo69406() { } +void foo69407() { } +void foo69408() { } +void foo69409() { } +void foo69410() { } +void foo69411() { } +void foo69412() { } +void foo69413() { } +void foo69414() { } +void foo69415() { } +void foo69416() { } +void foo69417() { } +void foo69418() { } +void foo69419() { } +void foo69420() { } +void foo69421() { } +void foo69422() { } +void foo69423() { } +void foo69424() { } +void foo69425() { } +void foo69426() { } +void foo69427() { } +void foo69428() { } +void foo69429() { } +void foo69430() { } +void foo69431() { } +void foo69432() { } +void foo69433() { } +void foo69434() { } +void foo69435() { } +void foo69436() { } +void foo69437() { } +void foo69438() { } +void foo69439() { } +void foo69440() { } +void foo69441() { } +void foo69442() { } +void foo69443() { } +void foo69444() { } +void foo69445() { } +void foo69446() { } +void foo69447() { } +void foo69448() { } +void foo69449() { } +void foo69450() { } +void foo69451() { } +void foo69452() { } +void foo69453() { } +void foo69454() { } +void foo69455() { } +void foo69456() { } +void foo69457() { } +void foo69458() { } +void foo69459() { } +void foo69460() { } +void foo69461() { } +void foo69462() { } +void foo69463() { } +void foo69464() { } +void foo69465() { } +void foo69466() { } +void foo69467() { } +void foo69468() { } +void foo69469() { } +void foo69470() { } +void foo69471() { } +void foo69472() { } +void foo69473() { } +void foo69474() { } +void foo69475() { } +void foo69476() { } +void foo69477() { } +void foo69478() { } +void foo69479() { } +void foo69480() { } +void foo69481() { } +void foo69482() { } +void foo69483() { } +void foo69484() { } +void foo69485() { } +void foo69486() { } +void foo69487() { } +void foo69488() { } +void foo69489() { } +void foo69490() { } +void foo69491() { } +void foo69492() { } +void foo69493() { } +void foo69494() { } +void foo69495() { } +void foo69496() { } +void foo69497() { } +void foo69498() { } +void foo69499() { } +void foo69500() { } +void foo69501() { } +void foo69502() { } +void foo69503() { } +void foo69504() { } +void foo69505() { } +void foo69506() { } +void foo69507() { } +void foo69508() { } +void foo69509() { } +void foo69510() { } +void foo69511() { } +void foo69512() { } +void foo69513() { } +void foo69514() { } +void foo69515() { } +void foo69516() { } +void foo69517() { } +void foo69518() { } +void foo69519() { } +void foo69520() { } +void foo69521() { } +void foo69522() { } +void foo69523() { } +void foo69524() { } +void foo69525() { } +void foo69526() { } +void foo69527() { } +void foo69528() { } +void foo69529() { } +void foo69530() { } +void foo69531() { } +void foo69532() { } +void foo69533() { } +void foo69534() { } +void foo69535() { } +void foo69536() { } +void foo69537() { } +void foo69538() { } +void foo69539() { } +void foo69540() { } +void foo69541() { } +void foo69542() { } +void foo69543() { } +void foo69544() { } +void foo69545() { } +void foo69546() { } +void foo69547() { } +void foo69548() { } +void foo69549() { } +void foo69550() { } +void foo69551() { } +void foo69552() { } +void foo69553() { } +void foo69554() { } +void foo69555() { } +void foo69556() { } +void foo69557() { } +void foo69558() { } +void foo69559() { } +void foo69560() { } +void foo69561() { } +void foo69562() { } +void foo69563() { } +void foo69564() { } +void foo69565() { } +void foo69566() { } +void foo69567() { } +void foo69568() { } +void foo69569() { } +void foo69570() { } +void foo69571() { } +void foo69572() { } +void foo69573() { } +void foo69574() { } +void foo69575() { } +void foo69576() { } +void foo69577() { } +void foo69578() { } +void foo69579() { } +void foo69580() { } +void foo69581() { } +void foo69582() { } +void foo69583() { } +void foo69584() { } +void foo69585() { } +void foo69586() { } +void foo69587() { } +void foo69588() { } +void foo69589() { } +void foo69590() { } +void foo69591() { } +void foo69592() { } +void foo69593() { } +void foo69594() { } +void foo69595() { } +void foo69596() { } +void foo69597() { } +void foo69598() { } +void foo69599() { } +void foo69600() { } +void foo69601() { } +void foo69602() { } +void foo69603() { } +void foo69604() { } +void foo69605() { } +void foo69606() { } +void foo69607() { } +void foo69608() { } +void foo69609() { } +void foo69610() { } +void foo69611() { } +void foo69612() { } +void foo69613() { } +void foo69614() { } +void foo69615() { } +void foo69616() { } +void foo69617() { } +void foo69618() { } +void foo69619() { } +void foo69620() { } +void foo69621() { } +void foo69622() { } +void foo69623() { } +void foo69624() { } +void foo69625() { } +void foo69626() { } +void foo69627() { } +void foo69628() { } +void foo69629() { } +void foo69630() { } +void foo69631() { } +void foo69632() { } +void foo69633() { } +void foo69634() { } +void foo69635() { } +void foo69636() { } +void foo69637() { } +void foo69638() { } +void foo69639() { } +void foo69640() { } +void foo69641() { } +void foo69642() { } +void foo69643() { } +void foo69644() { } +void foo69645() { } +void foo69646() { } +void foo69647() { } +void foo69648() { } +void foo69649() { } +void foo69650() { } +void foo69651() { } +void foo69652() { } +void foo69653() { } +void foo69654() { } +void foo69655() { } +void foo69656() { } +void foo69657() { } +void foo69658() { } +void foo69659() { } +void foo69660() { } +void foo69661() { } +void foo69662() { } +void foo69663() { } +void foo69664() { } +void foo69665() { } +void foo69666() { } +void foo69667() { } +void foo69668() { } +void foo69669() { } +void foo69670() { } +void foo69671() { } +void foo69672() { } +void foo69673() { } +void foo69674() { } +void foo69675() { } +void foo69676() { } +void foo69677() { } +void foo69678() { } +void foo69679() { } +void foo69680() { } +void foo69681() { } +void foo69682() { } +void foo69683() { } +void foo69684() { } +void foo69685() { } +void foo69686() { } +void foo69687() { } +void foo69688() { } +void foo69689() { } +void foo69690() { } +void foo69691() { } +void foo69692() { } +void foo69693() { } +void foo69694() { } +void foo69695() { } +void foo69696() { } +void foo69697() { } +void foo69698() { } +void foo69699() { } +void foo69700() { } +void foo69701() { } +void foo69702() { } +void foo69703() { } +void foo69704() { } +void foo69705() { } +void foo69706() { } +void foo69707() { } +void foo69708() { } +void foo69709() { } +void foo69710() { } +void foo69711() { } +void foo69712() { } +void foo69713() { } +void foo69714() { } +void foo69715() { } +void foo69716() { } +void foo69717() { } +void foo69718() { } +void foo69719() { } +void foo69720() { } +void foo69721() { } +void foo69722() { } +void foo69723() { } +void foo69724() { } +void foo69725() { } +void foo69726() { } +void foo69727() { } +void foo69728() { } +void foo69729() { } +void foo69730() { } +void foo69731() { } +void foo69732() { } +void foo69733() { } +void foo69734() { } +void foo69735() { } +void foo69736() { } +void foo69737() { } +void foo69738() { } +void foo69739() { } +void foo69740() { } +void foo69741() { } +void foo69742() { } +void foo69743() { } +void foo69744() { } +void foo69745() { } +void foo69746() { } +void foo69747() { } +void foo69748() { } +void foo69749() { } +void foo69750() { } +void foo69751() { } +void foo69752() { } +void foo69753() { } +void foo69754() { } +void foo69755() { } +void foo69756() { } +void foo69757() { } +void foo69758() { } +void foo69759() { } +void foo69760() { } +void foo69761() { } +void foo69762() { } +void foo69763() { } +void foo69764() { } +void foo69765() { } +void foo69766() { } +void foo69767() { } +void foo69768() { } +void foo69769() { } +void foo69770() { } +void foo69771() { } +void foo69772() { } +void foo69773() { } +void foo69774() { } +void foo69775() { } +void foo69776() { } +void foo69777() { } +void foo69778() { } +void foo69779() { } +void foo69780() { } +void foo69781() { } +void foo69782() { } +void foo69783() { } +void foo69784() { } +void foo69785() { } +void foo69786() { } +void foo69787() { } +void foo69788() { } +void foo69789() { } +void foo69790() { } +void foo69791() { } +void foo69792() { } +void foo69793() { } +void foo69794() { } +void foo69795() { } +void foo69796() { } +void foo69797() { } +void foo69798() { } +void foo69799() { } +void foo69800() { } +void foo69801() { } +void foo69802() { } +void foo69803() { } +void foo69804() { } +void foo69805() { } +void foo69806() { } +void foo69807() { } +void foo69808() { } +void foo69809() { } +void foo69810() { } +void foo69811() { } +void foo69812() { } +void foo69813() { } +void foo69814() { } +void foo69815() { } +void foo69816() { } +void foo69817() { } +void foo69818() { } +void foo69819() { } +void foo69820() { } +void foo69821() { } +void foo69822() { } +void foo69823() { } +void foo69824() { } +void foo69825() { } +void foo69826() { } +void foo69827() { } +void foo69828() { } +void foo69829() { } +void foo69830() { } +void foo69831() { } +void foo69832() { } +void foo69833() { } +void foo69834() { } +void foo69835() { } +void foo69836() { } +void foo69837() { } +void foo69838() { } +void foo69839() { } +void foo69840() { } +void foo69841() { } +void foo69842() { } +void foo69843() { } +void foo69844() { } +void foo69845() { } +void foo69846() { } +void foo69847() { } +void foo69848() { } +void foo69849() { } +void foo69850() { } +void foo69851() { } +void foo69852() { } +void foo69853() { } +void foo69854() { } +void foo69855() { } +void foo69856() { } +void foo69857() { } +void foo69858() { } +void foo69859() { } +void foo69860() { } +void foo69861() { } +void foo69862() { } +void foo69863() { } +void foo69864() { } +void foo69865() { } +void foo69866() { } +void foo69867() { } +void foo69868() { } +void foo69869() { } +void foo69870() { } +void foo69871() { } +void foo69872() { } +void foo69873() { } +void foo69874() { } +void foo69875() { } +void foo69876() { } +void foo69877() { } +void foo69878() { } +void foo69879() { } +void foo69880() { } +void foo69881() { } +void foo69882() { } +void foo69883() { } +void foo69884() { } +void foo69885() { } +void foo69886() { } +void foo69887() { } +void foo69888() { } +void foo69889() { } +void foo69890() { } +void foo69891() { } +void foo69892() { } +void foo69893() { } +void foo69894() { } +void foo69895() { } +void foo69896() { } +void foo69897() { } +void foo69898() { } +void foo69899() { } +void foo69900() { } +void foo69901() { } +void foo69902() { } +void foo69903() { } +void foo69904() { } +void foo69905() { } +void foo69906() { } +void foo69907() { } +void foo69908() { } +void foo69909() { } +void foo69910() { } +void foo69911() { } +void foo69912() { } +void foo69913() { } +void foo69914() { } +void foo69915() { } +void foo69916() { } +void foo69917() { } +void foo69918() { } +void foo69919() { } +void foo69920() { } +void foo69921() { } +void foo69922() { } +void foo69923() { } +void foo69924() { } +void foo69925() { } +void foo69926() { } +void foo69927() { } +void foo69928() { } +void foo69929() { } +void foo69930() { } +void foo69931() { } +void foo69932() { } +void foo69933() { } +void foo69934() { } +void foo69935() { } +void foo69936() { } +void foo69937() { } +void foo69938() { } +void foo69939() { } +void foo69940() { } +void foo69941() { } +void foo69942() { } +void foo69943() { } +void foo69944() { } +void foo69945() { } +void foo69946() { } +void foo69947() { } +void foo69948() { } +void foo69949() { } +void foo69950() { } +void foo69951() { } +void foo69952() { } +void foo69953() { } +void foo69954() { } +void foo69955() { } +void foo69956() { } +void foo69957() { } +void foo69958() { } +void foo69959() { } +void foo69960() { } +void foo69961() { } +void foo69962() { } +void foo69963() { } +void foo69964() { } +void foo69965() { } +void foo69966() { } +void foo69967() { } +void foo69968() { } +void foo69969() { } +void foo69970() { } +void foo69971() { } +void foo69972() { } +void foo69973() { } +void foo69974() { } +void foo69975() { } +void foo69976() { } +void foo69977() { } +void foo69978() { } +void foo69979() { } +void foo69980() { } +void foo69981() { } +void foo69982() { } +void foo69983() { } +void foo69984() { } +void foo69985() { } +void foo69986() { } +void foo69987() { } +void foo69988() { } +void foo69989() { } +void foo69990() { } +void foo69991() { } +void foo69992() { } +void foo69993() { } +void foo69994() { } +void foo69995() { } +void foo69996() { } +void foo69997() { } +void foo69998() { } +void foo69999() { } +void foo70000() { } diff --git a/testing/test-cases/chained-fixups-many-binds.dtest/foo.h b/testing/test-cases/chained-fixups-many-binds.dtest/foo.h index 77fe3ab..d0e5162 100644 --- a/testing/test-cases/chained-fixups-many-binds.dtest/foo.h +++ b/testing/test-cases/chained-fixups-many-binds.dtest/foo.h @@ -64998,3 +64998,5003 @@ extern void foo64997(); extern void foo64998(); extern void foo64999(); extern void foo65000(); +extern void foo65001(); +extern void foo65002(); +extern void foo65003(); +extern void foo65004(); +extern void foo65005(); +extern void foo65006(); +extern void foo65007(); +extern void foo65008(); +extern void foo65009(); +extern void foo65010(); +extern void foo65011(); +extern void foo65012(); +extern void foo65013(); +extern void foo65014(); +extern void foo65015(); +extern void foo65016(); +extern void foo65017(); +extern void foo65018(); +extern void foo65019(); +extern void foo65020(); +extern void foo65021(); +extern void foo65022(); +extern void foo65023(); +extern void foo65024(); +extern void foo65025(); +extern void foo65026(); +extern void foo65027(); +extern void foo65028(); +extern void foo65029(); +extern void foo65030(); +extern void foo65031(); +extern void foo65032(); +extern void foo65033(); +extern void foo65034(); +extern void foo65035(); +extern void foo65036(); +extern void foo65037(); +extern void foo65038(); +extern void foo65039(); +extern void foo65040(); +extern void foo65041(); +extern void foo65042(); +extern void foo65043(); +extern void foo65044(); +extern void foo65045(); +extern void foo65046(); +extern void foo65047(); +extern void foo65048(); +extern void foo65049(); +extern void foo65050(); +extern void foo65051(); +extern void foo65052(); +extern void foo65053(); +extern void foo65054(); +extern void foo65055(); +extern void foo65056(); +extern void foo65057(); +extern void foo65058(); +extern void foo65059(); +extern void foo65060(); +extern void foo65061(); +extern void foo65062(); +extern void foo65063(); +extern void foo65064(); +extern void foo65065(); +extern void foo65066(); +extern void foo65067(); +extern void foo65068(); +extern void foo65069(); +extern void foo65070(); +extern void foo65071(); +extern void foo65072(); +extern void foo65073(); +extern void foo65074(); +extern void foo65075(); +extern void foo65076(); +extern void foo65077(); +extern void foo65078(); +extern void foo65079(); +extern void foo65080(); +extern void foo65081(); +extern void foo65082(); +extern void foo65083(); +extern void foo65084(); +extern void foo65085(); +extern void foo65086(); +extern void foo65087(); +extern void foo65088(); +extern void foo65089(); +extern void foo65090(); +extern void foo65091(); +extern void foo65092(); +extern void foo65093(); +extern void foo65094(); +extern void foo65095(); +extern void foo65096(); +extern void foo65097(); +extern void foo65098(); +extern void foo65099(); +extern void foo65100(); +extern void foo65101(); +extern void foo65102(); +extern void foo65103(); +extern void foo65104(); +extern void foo65105(); +extern void foo65106(); +extern void foo65107(); +extern void foo65108(); +extern void foo65109(); +extern void foo65110(); +extern void foo65111(); +extern void foo65112(); +extern void foo65113(); +extern void foo65114(); +extern void foo65115(); +extern void foo65116(); +extern void foo65117(); +extern void foo65118(); +extern void foo65119(); +extern void foo65120(); +extern void foo65121(); +extern void foo65122(); +extern void foo65123(); +extern void foo65124(); +extern void foo65125(); +extern void foo65126(); +extern void foo65127(); +extern void foo65128(); +extern void foo65129(); +extern void foo65130(); +extern void foo65131(); +extern void foo65132(); +extern void foo65133(); +extern void foo65134(); +extern void foo65135(); +extern void foo65136(); +extern void foo65137(); +extern void foo65138(); +extern void foo65139(); +extern void foo65140(); +extern void foo65141(); +extern void foo65142(); +extern void foo65143(); +extern void foo65144(); +extern void foo65145(); +extern void foo65146(); +extern void foo65147(); +extern void foo65148(); +extern void foo65149(); +extern void foo65150(); +extern void foo65151(); +extern void foo65152(); +extern void foo65153(); +extern void foo65154(); +extern void foo65155(); +extern void foo65156(); +extern void foo65157(); +extern void foo65158(); +extern void foo65159(); +extern void foo65160(); +extern void foo65161(); +extern void foo65162(); +extern void foo65163(); +extern void foo65164(); +extern void foo65165(); +extern void foo65166(); +extern void foo65167(); +extern void foo65168(); +extern void foo65169(); +extern void foo65170(); +extern void foo65171(); +extern void foo65172(); +extern void foo65173(); +extern void foo65174(); +extern void foo65175(); +extern void foo65176(); +extern void foo65177(); +extern void foo65178(); +extern void foo65179(); +extern void foo65180(); +extern void foo65181(); +extern void foo65182(); +extern void foo65183(); +extern void foo65184(); +extern void foo65185(); +extern void foo65186(); +extern void foo65187(); +extern void foo65188(); +extern void foo65189(); +extern void foo65190(); +extern void foo65191(); +extern void foo65192(); +extern void foo65193(); +extern void foo65194(); +extern void foo65195(); +extern void foo65196(); +extern void foo65197(); +extern void foo65198(); +extern void foo65199(); +extern void foo65200(); +extern void foo65201(); +extern void foo65202(); +extern void foo65203(); +extern void foo65204(); +extern void foo65205(); +extern void foo65206(); +extern void foo65207(); +extern void foo65208(); +extern void foo65209(); +extern void foo65210(); +extern void foo65211(); +extern void foo65212(); +extern void foo65213(); +extern void foo65214(); +extern void foo65215(); +extern void foo65216(); +extern void foo65217(); +extern void foo65218(); +extern void foo65219(); +extern void foo65220(); +extern void foo65221(); +extern void foo65222(); +extern void foo65223(); +extern void foo65224(); +extern void foo65225(); +extern void foo65226(); +extern void foo65227(); +extern void foo65228(); +extern void foo65229(); +extern void foo65230(); +extern void foo65231(); +extern void foo65232(); +extern void foo65233(); +extern void foo65234(); +extern void foo65235(); +extern void foo65236(); +extern void foo65237(); +extern void foo65238(); +extern void foo65239(); +extern void foo65240(); +extern void foo65241(); +extern void foo65242(); +extern void foo65243(); +extern void foo65244(); +extern void foo65245(); +extern void foo65246(); +extern void foo65247(); +extern void foo65248(); +extern void foo65249(); +extern void foo65250(); +extern void foo65251(); +extern void foo65252(); +extern void foo65253(); +extern void foo65254(); +extern void foo65255(); +extern void foo65256(); +extern void foo65257(); +extern void foo65258(); +extern void foo65259(); +extern void foo65260(); +extern void foo65261(); +extern void foo65262(); +extern void foo65263(); +extern void foo65264(); +extern void foo65265(); +extern void foo65266(); +extern void foo65267(); +extern void foo65268(); +extern void foo65269(); +extern void foo65270(); +extern void foo65271(); +extern void foo65272(); +extern void foo65273(); +extern void foo65274(); +extern void foo65275(); +extern void foo65276(); +extern void foo65277(); +extern void foo65278(); +extern void foo65279(); +extern void foo65280(); +extern void foo65281(); +extern void foo65282(); +extern void foo65283(); +extern void foo65284(); +extern void foo65285(); +extern void foo65286(); +extern void foo65287(); +extern void foo65288(); +extern void foo65289(); +extern void foo65290(); +extern void foo65291(); +extern void foo65292(); +extern void foo65293(); +extern void foo65294(); +extern void foo65295(); +extern void foo65296(); +extern void foo65297(); +extern void foo65298(); +extern void foo65299(); +extern void foo65300(); +extern void foo65301(); +extern void foo65302(); +extern void foo65303(); +extern void foo65304(); +extern void foo65305(); +extern void foo65306(); +extern void foo65307(); +extern void foo65308(); +extern void foo65309(); +extern void foo65310(); +extern void foo65311(); +extern void foo65312(); +extern void foo65313(); +extern void foo65314(); +extern void foo65315(); +extern void foo65316(); +extern void foo65317(); +extern void foo65318(); +extern void foo65319(); +extern void foo65320(); +extern void foo65321(); +extern void foo65322(); +extern void foo65323(); +extern void foo65324(); +extern void foo65325(); +extern void foo65326(); +extern void foo65327(); +extern void foo65328(); +extern void foo65329(); +extern void foo65330(); +extern void foo65331(); +extern void foo65332(); +extern void foo65333(); +extern void foo65334(); +extern void foo65335(); +extern void foo65336(); +extern void foo65337(); +extern void foo65338(); +extern void foo65339(); +extern void foo65340(); +extern void foo65341(); +extern void foo65342(); +extern void foo65343(); +extern void foo65344(); +extern void foo65345(); +extern void foo65346(); +extern void foo65347(); +extern void foo65348(); +extern void foo65349(); +extern void foo65350(); +extern void foo65351(); +extern void foo65352(); +extern void foo65353(); +extern void foo65354(); +extern void foo65355(); +extern void foo65356(); +extern void foo65357(); +extern void foo65358(); +extern void foo65359(); +extern void foo65360(); +extern void foo65361(); +extern void foo65362(); +extern void foo65363(); +extern void foo65364(); +extern void foo65365(); +extern void foo65366(); +extern void foo65367(); +extern void foo65368(); +extern void foo65369(); +extern void foo65370(); +extern void foo65371(); +extern void foo65372(); +extern void foo65373(); +extern void foo65374(); +extern void foo65375(); +extern void foo65376(); +extern void foo65377(); +extern void foo65378(); +extern void foo65379(); +extern void foo65380(); +extern void foo65381(); +extern void foo65382(); +extern void foo65383(); +extern void foo65384(); +extern void foo65385(); +extern void foo65386(); +extern void foo65387(); +extern void foo65388(); +extern void foo65389(); +extern void foo65390(); +extern void foo65391(); +extern void foo65392(); +extern void foo65393(); +extern void foo65394(); +extern void foo65395(); +extern void foo65396(); +extern void foo65397(); +extern void foo65398(); +extern void foo65399(); +extern void foo65400(); +extern void foo65401(); +extern void foo65402(); +extern void foo65403(); +extern void foo65404(); +extern void foo65405(); +extern void foo65406(); +extern void foo65407(); +extern void foo65408(); +extern void foo65409(); +extern void foo65410(); +extern void foo65411(); +extern void foo65412(); +extern void foo65413(); +extern void foo65414(); +extern void foo65415(); +extern void foo65416(); +extern void foo65417(); +extern void foo65418(); +extern void foo65419(); +extern void foo65420(); +extern void foo65421(); +extern void foo65422(); +extern void foo65423(); +extern void foo65424(); +extern void foo65425(); +extern void foo65426(); +extern void foo65427(); +extern void foo65428(); +extern void foo65429(); +extern void foo65430(); +extern void foo65431(); +extern void foo65432(); +extern void foo65433(); +extern void foo65434(); +extern void foo65435(); +extern void foo65436(); +extern void foo65437(); +extern void foo65438(); +extern void foo65439(); +extern void foo65440(); +extern void foo65441(); +extern void foo65442(); +extern void foo65443(); +extern void foo65444(); +extern void foo65445(); +extern void foo65446(); +extern void foo65447(); +extern void foo65448(); +extern void foo65449(); +extern void foo65450(); +extern void foo65451(); +extern void foo65452(); +extern void foo65453(); +extern void foo65454(); +extern void foo65455(); +extern void foo65456(); +extern void foo65457(); +extern void foo65458(); +extern void foo65459(); +extern void foo65460(); +extern void foo65461(); +extern void foo65462(); +extern void foo65463(); +extern void foo65464(); +extern void foo65465(); +extern void foo65466(); +extern void foo65467(); +extern void foo65468(); +extern void foo65469(); +extern void foo65470(); +extern void foo65471(); +extern void foo65472(); +extern void foo65473(); +extern void foo65474(); +extern void foo65475(); +extern void foo65476(); +extern void foo65477(); +extern void foo65478(); +extern void foo65479(); +extern void foo65480(); +extern void foo65481(); +extern void foo65482(); +extern void foo65483(); +extern void foo65484(); +extern void foo65485(); +extern void foo65486(); +extern void foo65487(); +extern void foo65488(); +extern void foo65489(); +extern void foo65490(); +extern void foo65491(); +extern void foo65492(); +extern void foo65493(); +extern void foo65494(); +extern void foo65495(); +extern void foo65496(); +extern void foo65497(); +extern void foo65498(); +extern void foo65499(); +extern void foo65500(); +extern void foo65501(); +extern void foo65502(); +extern void foo65503(); +extern void foo65504(); +extern void foo65505(); +extern void foo65506(); +extern void foo65507(); +extern void foo65508(); +extern void foo65509(); +extern void foo65510(); +extern void foo65511(); +extern void foo65512(); +extern void foo65513(); +extern void foo65514(); +extern void foo65515(); +extern void foo65516(); +extern void foo65517(); +extern void foo65518(); +extern void foo65519(); +extern void foo65520(); +extern void foo65521(); +extern void foo65522(); +extern void foo65523(); +extern void foo65524(); +extern void foo65525(); +extern void foo65526(); +extern void foo65527(); +extern void foo65528(); +extern void foo65529(); +extern void foo65530(); +extern void foo65531(); +extern void foo65532(); +extern void foo65533(); +extern void foo65534(); +extern void foo65535(); +extern void foo65536(); +extern void foo65537(); +extern void foo65538(); +extern void foo65539(); +extern void foo65540(); +extern void foo65541(); +extern void foo65542(); +extern void foo65543(); +extern void foo65544(); +extern void foo65545(); +extern void foo65546(); +extern void foo65547(); +extern void foo65548(); +extern void foo65549(); +extern void foo65550(); +extern void foo65551(); +extern void foo65552(); +extern void foo65553(); +extern void foo65554(); +extern void foo65555(); +extern void foo65556(); +extern void foo65557(); +extern void foo65558(); +extern void foo65559(); +extern void foo65560(); +extern void foo65561(); +extern void foo65562(); +extern void foo65563(); +extern void foo65564(); +extern void foo65565(); +extern void foo65566(); +extern void foo65567(); +extern void foo65568(); +extern void foo65569(); +extern void foo65570(); +extern void foo65571(); +extern void foo65572(); +extern void foo65573(); +extern void foo65574(); +extern void foo65575(); +extern void foo65576(); +extern void foo65577(); +extern void foo65578(); +extern void foo65579(); +extern void foo65580(); +extern void foo65581(); +extern void foo65582(); +extern void foo65583(); +extern void foo65584(); +extern void foo65585(); +extern void foo65586(); +extern void foo65587(); +extern void foo65588(); +extern void foo65589(); +extern void foo65590(); +extern void foo65591(); +extern void foo65592(); +extern void foo65593(); +extern void foo65594(); +extern void foo65595(); +extern void foo65596(); +extern void foo65597(); +extern void foo65598(); +extern void foo65599(); +extern void foo65600(); +extern void foo65601(); +extern void foo65602(); +extern void foo65603(); +extern void foo65604(); +extern void foo65605(); +extern void foo65606(); +extern void foo65607(); +extern void foo65608(); +extern void foo65609(); +extern void foo65610(); +extern void foo65611(); +extern void foo65612(); +extern void foo65613(); +extern void foo65614(); +extern void foo65615(); +extern void foo65616(); +extern void foo65617(); +extern void foo65618(); +extern void foo65619(); +extern void foo65620(); +extern void foo65621(); +extern void foo65622(); +extern void foo65623(); +extern void foo65624(); +extern void foo65625(); +extern void foo65626(); +extern void foo65627(); +extern void foo65628(); +extern void foo65629(); +extern void foo65630(); +extern void foo65631(); +extern void foo65632(); +extern void foo65633(); +extern void foo65634(); +extern void foo65635(); +extern void foo65636(); +extern void foo65637(); +extern void foo65638(); +extern void foo65639(); +extern void foo65640(); +extern void foo65641(); +extern void foo65642(); +extern void foo65643(); +extern void foo65644(); +extern void foo65645(); +extern void foo65646(); +extern void foo65647(); +extern void foo65648(); +extern void foo65649(); +extern void foo65650(); +extern void foo65651(); +extern void foo65652(); +extern void foo65653(); +extern void foo65654(); +extern void foo65655(); +extern void foo65656(); +extern void foo65657(); +extern void foo65658(); +extern void foo65659(); +extern void foo65660(); +extern void foo65661(); +extern void foo65662(); +extern void foo65663(); +extern void foo65664(); +extern void foo65665(); +extern void foo65666(); +extern void foo65667(); +extern void foo65668(); +extern void foo65669(); +extern void foo65670(); +extern void foo65671(); +extern void foo65672(); +extern void foo65673(); +extern void foo65674(); +extern void foo65675(); +extern void foo65676(); +extern void foo65677(); +extern void foo65678(); +extern void foo65679(); +extern void foo65680(); +extern void foo65681(); +extern void foo65682(); +extern void foo65683(); +extern void foo65684(); +extern void foo65685(); +extern void foo65686(); +extern void foo65687(); +extern void foo65688(); +extern void foo65689(); +extern void foo65690(); +extern void foo65691(); +extern void foo65692(); +extern void foo65693(); +extern void foo65694(); +extern void foo65695(); +extern void foo65696(); +extern void foo65697(); +extern void foo65698(); +extern void foo65699(); +extern void foo65700(); +extern void foo65701(); +extern void foo65702(); +extern void foo65703(); +extern void foo65704(); +extern void foo65705(); +extern void foo65706(); +extern void foo65707(); +extern void foo65708(); +extern void foo65709(); +extern void foo65710(); +extern void foo65711(); +extern void foo65712(); +extern void foo65713(); +extern void foo65714(); +extern void foo65715(); +extern void foo65716(); +extern void foo65717(); +extern void foo65718(); +extern void foo65719(); +extern void foo65720(); +extern void foo65721(); +extern void foo65722(); +extern void foo65723(); +extern void foo65724(); +extern void foo65725(); +extern void foo65726(); +extern void foo65727(); +extern void foo65728(); +extern void foo65729(); +extern void foo65730(); +extern void foo65731(); +extern void foo65732(); +extern void foo65733(); +extern void foo65734(); +extern void foo65735(); +extern void foo65736(); +extern void foo65737(); +extern void foo65738(); +extern void foo65739(); +extern void foo65740(); +extern void foo65741(); +extern void foo65742(); +extern void foo65743(); +extern void foo65744(); +extern void foo65745(); +extern void foo65746(); +extern void foo65747(); +extern void foo65748(); +extern void foo65749(); +extern void foo65750(); +extern void foo65751(); +extern void foo65752(); +extern void foo65753(); +extern void foo65754(); +extern void foo65755(); +extern void foo65756(); +extern void foo65757(); +extern void foo65758(); +extern void foo65759(); +extern void foo65760(); +extern void foo65761(); +extern void foo65762(); +extern void foo65763(); +extern void foo65764(); +extern void foo65765(); +extern void foo65766(); +extern void foo65767(); +extern void foo65768(); +extern void foo65769(); +extern void foo65770(); +extern void foo65771(); +extern void foo65772(); +extern void foo65773(); +extern void foo65774(); +extern void foo65775(); +extern void foo65776(); +extern void foo65777(); +extern void foo65778(); +extern void foo65779(); +extern void foo65780(); +extern void foo65781(); +extern void foo65782(); +extern void foo65783(); +extern void foo65784(); +extern void foo65785(); +extern void foo65786(); +extern void foo65787(); +extern void foo65788(); +extern void foo65789(); +extern void foo65790(); +extern void foo65791(); +extern void foo65792(); +extern void foo65793(); +extern void foo65794(); +extern void foo65795(); +extern void foo65796(); +extern void foo65797(); +extern void foo65798(); +extern void foo65799(); +extern void foo65800(); +extern void foo65801(); +extern void foo65802(); +extern void foo65803(); +extern void foo65804(); +extern void foo65805(); +extern void foo65806(); +extern void foo65807(); +extern void foo65808(); +extern void foo65809(); +extern void foo65810(); +extern void foo65811(); +extern void foo65812(); +extern void foo65813(); +extern void foo65814(); +extern void foo65815(); +extern void foo65816(); +extern void foo65817(); +extern void foo65818(); +extern void foo65819(); +extern void foo65820(); +extern void foo65821(); +extern void foo65822(); +extern void foo65823(); +extern void foo65824(); +extern void foo65825(); +extern void foo65826(); +extern void foo65827(); +extern void foo65828(); +extern void foo65829(); +extern void foo65830(); +extern void foo65831(); +extern void foo65832(); +extern void foo65833(); +extern void foo65834(); +extern void foo65835(); +extern void foo65836(); +extern void foo65837(); +extern void foo65838(); +extern void foo65839(); +extern void foo65840(); +extern void foo65841(); +extern void foo65842(); +extern void foo65843(); +extern void foo65844(); +extern void foo65845(); +extern void foo65846(); +extern void foo65847(); +extern void foo65848(); +extern void foo65849(); +extern void foo65850(); +extern void foo65851(); +extern void foo65852(); +extern void foo65853(); +extern void foo65854(); +extern void foo65855(); +extern void foo65856(); +extern void foo65857(); +extern void foo65858(); +extern void foo65859(); +extern void foo65860(); +extern void foo65861(); +extern void foo65862(); +extern void foo65863(); +extern void foo65864(); +extern void foo65865(); +extern void foo65866(); +extern void foo65867(); +extern void foo65868(); +extern void foo65869(); +extern void foo65870(); +extern void foo65871(); +extern void foo65872(); +extern void foo65873(); +extern void foo65874(); +extern void foo65875(); +extern void foo65876(); +extern void foo65877(); +extern void foo65878(); +extern void foo65879(); +extern void foo65880(); +extern void foo65881(); +extern void foo65882(); +extern void foo65883(); +extern void foo65884(); +extern void foo65885(); +extern void foo65886(); +extern void foo65887(); +extern void foo65888(); +extern void foo65889(); +extern void foo65890(); +extern void foo65891(); +extern void foo65892(); +extern void foo65893(); +extern void foo65894(); +extern void foo65895(); +extern void foo65896(); +extern void foo65897(); +extern void foo65898(); +extern void foo65899(); +extern void foo65900(); +extern void foo65901(); +extern void foo65902(); +extern void foo65903(); +extern void foo65904(); +extern void foo65905(); +extern void foo65906(); +extern void foo65907(); +extern void foo65908(); +extern void foo65909(); +extern void foo65910(); +extern void foo65911(); +extern void foo65912(); +extern void foo65913(); +extern void foo65914(); +extern void foo65915(); +extern void foo65916(); +extern void foo65917(); +extern void foo65918(); +extern void foo65919(); +extern void foo65920(); +extern void foo65921(); +extern void foo65922(); +extern void foo65923(); +extern void foo65924(); +extern void foo65925(); +extern void foo65926(); +extern void foo65927(); +extern void foo65928(); +extern void foo65929(); +extern void foo65930(); +extern void foo65931(); +extern void foo65932(); +extern void foo65933(); +extern void foo65934(); +extern void foo65935(); +extern void foo65936(); +extern void foo65937(); +extern void foo65938(); +extern void foo65939(); +extern void foo65940(); +extern void foo65941(); +extern void foo65942(); +extern void foo65943(); +extern void foo65944(); +extern void foo65945(); +extern void foo65946(); +extern void foo65947(); +extern void foo65948(); +extern void foo65949(); +extern void foo65950(); +extern void foo65951(); +extern void foo65952(); +extern void foo65953(); +extern void foo65954(); +extern void foo65955(); +extern void foo65956(); +extern void foo65957(); +extern void foo65958(); +extern void foo65959(); +extern void foo65960(); +extern void foo65961(); +extern void foo65962(); +extern void foo65963(); +extern void foo65964(); +extern void foo65965(); +extern void foo65966(); +extern void foo65967(); +extern void foo65968(); +extern void foo65969(); +extern void foo65970(); +extern void foo65971(); +extern void foo65972(); +extern void foo65973(); +extern void foo65974(); +extern void foo65975(); +extern void foo65976(); +extern void foo65977(); +extern void foo65978(); +extern void foo65979(); +extern void foo65980(); +extern void foo65981(); +extern void foo65982(); +extern void foo65983(); +extern void foo65984(); +extern void foo65985(); +extern void foo65986(); +extern void foo65987(); +extern void foo65988(); +extern void foo65989(); +extern void foo65990(); +extern void foo65991(); +extern void foo65992(); +extern void foo65993(); +extern void foo65994(); +extern void foo65995(); +extern void foo65996(); +extern void foo65997(); +extern void foo65998(); +extern void foo65999(); +extern void foo66000(); +extern void foo66001(); +extern void foo66002(); +extern void foo66003(); +extern void foo66004(); +extern void foo66005(); +extern void foo66006(); +extern void foo66007(); +extern void foo66008(); +extern void foo66009(); +extern void foo66010(); +extern void foo66011(); +extern void foo66012(); +extern void foo66013(); +extern void foo66014(); +extern void foo66015(); +extern void foo66016(); +extern void foo66017(); +extern void foo66018(); +extern void foo66019(); +extern void foo66020(); +extern void foo66021(); +extern void foo66022(); +extern void foo66023(); +extern void foo66024(); +extern void foo66025(); +extern void foo66026(); +extern void foo66027(); +extern void foo66028(); +extern void foo66029(); +extern void foo66030(); +extern void foo66031(); +extern void foo66032(); +extern void foo66033(); +extern void foo66034(); +extern void foo66035(); +extern void foo66036(); +extern void foo66037(); +extern void foo66038(); +extern void foo66039(); +extern void foo66040(); +extern void foo66041(); +extern void foo66042(); +extern void foo66043(); +extern void foo66044(); +extern void foo66045(); +extern void foo66046(); +extern void foo66047(); +extern void foo66048(); +extern void foo66049(); +extern void foo66050(); +extern void foo66051(); +extern void foo66052(); +extern void foo66053(); +extern void foo66054(); +extern void foo66055(); +extern void foo66056(); +extern void foo66057(); +extern void foo66058(); +extern void foo66059(); +extern void foo66060(); +extern void foo66061(); +extern void foo66062(); +extern void foo66063(); +extern void foo66064(); +extern void foo66065(); +extern void foo66066(); +extern void foo66067(); +extern void foo66068(); +extern void foo66069(); +extern void foo66070(); +extern void foo66071(); +extern void foo66072(); +extern void foo66073(); +extern void foo66074(); +extern void foo66075(); +extern void foo66076(); +extern void foo66077(); +extern void foo66078(); +extern void foo66079(); +extern void foo66080(); +extern void foo66081(); +extern void foo66082(); +extern void foo66083(); +extern void foo66084(); +extern void foo66085(); +extern void foo66086(); +extern void foo66087(); +extern void foo66088(); +extern void foo66089(); +extern void foo66090(); +extern void foo66091(); +extern void foo66092(); +extern void foo66093(); +extern void foo66094(); +extern void foo66095(); +extern void foo66096(); +extern void foo66097(); +extern void foo66098(); +extern void foo66099(); +extern void foo66100(); +extern void foo66101(); +extern void foo66102(); +extern void foo66103(); +extern void foo66104(); +extern void foo66105(); +extern void foo66106(); +extern void foo66107(); +extern void foo66108(); +extern void foo66109(); +extern void foo66110(); +extern void foo66111(); +extern void foo66112(); +extern void foo66113(); +extern void foo66114(); +extern void foo66115(); +extern void foo66116(); +extern void foo66117(); +extern void foo66118(); +extern void foo66119(); +extern void foo66120(); +extern void foo66121(); +extern void foo66122(); +extern void foo66123(); +extern void foo66124(); +extern void foo66125(); +extern void foo66126(); +extern void foo66127(); +extern void foo66128(); +extern void foo66129(); +extern void foo66130(); +extern void foo66131(); +extern void foo66132(); +extern void foo66133(); +extern void foo66134(); +extern void foo66135(); +extern void foo66136(); +extern void foo66137(); +extern void foo66138(); +extern void foo66139(); +extern void foo66140(); +extern void foo66141(); +extern void foo66142(); +extern void foo66143(); +extern void foo66144(); +extern void foo66145(); +extern void foo66146(); +extern void foo66147(); +extern void foo66148(); +extern void foo66149(); +extern void foo66150(); +extern void foo66151(); +extern void foo66152(); +extern void foo66153(); +extern void foo66154(); +extern void foo66155(); +extern void foo66156(); +extern void foo66157(); +extern void foo66158(); +extern void foo66159(); +extern void foo66160(); +extern void foo66161(); +extern void foo66162(); +extern void foo66163(); +extern void foo66164(); +extern void foo66165(); +extern void foo66166(); +extern void foo66167(); +extern void foo66168(); +extern void foo66169(); +extern void foo66170(); +extern void foo66171(); +extern void foo66172(); +extern void foo66173(); +extern void foo66174(); +extern void foo66175(); +extern void foo66176(); +extern void foo66177(); +extern void foo66178(); +extern void foo66179(); +extern void foo66180(); +extern void foo66181(); +extern void foo66182(); +extern void foo66183(); +extern void foo66184(); +extern void foo66185(); +extern void foo66186(); +extern void foo66187(); +extern void foo66188(); +extern void foo66189(); +extern void foo66190(); +extern void foo66191(); +extern void foo66192(); +extern void foo66193(); +extern void foo66194(); +extern void foo66195(); +extern void foo66196(); +extern void foo66197(); +extern void foo66198(); +extern void foo66199(); +extern void foo66200(); +extern void foo66201(); +extern void foo66202(); +extern void foo66203(); +extern void foo66204(); +extern void foo66205(); +extern void foo66206(); +extern void foo66207(); +extern void foo66208(); +extern void foo66209(); +extern void foo66210(); +extern void foo66211(); +extern void foo66212(); +extern void foo66213(); +extern void foo66214(); +extern void foo66215(); +extern void foo66216(); +extern void foo66217(); +extern void foo66218(); +extern void foo66219(); +extern void foo66220(); +extern void foo66221(); +extern void foo66222(); +extern void foo66223(); +extern void foo66224(); +extern void foo66225(); +extern void foo66226(); +extern void foo66227(); +extern void foo66228(); +extern void foo66229(); +extern void foo66230(); +extern void foo66231(); +extern void foo66232(); +extern void foo66233(); +extern void foo66234(); +extern void foo66235(); +extern void foo66236(); +extern void foo66237(); +extern void foo66238(); +extern void foo66239(); +extern void foo66240(); +extern void foo66241(); +extern void foo66242(); +extern void foo66243(); +extern void foo66244(); +extern void foo66245(); +extern void foo66246(); +extern void foo66247(); +extern void foo66248(); +extern void foo66249(); +extern void foo66250(); +extern void foo66251(); +extern void foo66252(); +extern void foo66253(); +extern void foo66254(); +extern void foo66255(); +extern void foo66256(); +extern void foo66257(); +extern void foo66258(); +extern void foo66259(); +extern void foo66260(); +extern void foo66261(); +extern void foo66262(); +extern void foo66263(); +extern void foo66264(); +extern void foo66265(); +extern void foo66266(); +extern void foo66267(); +extern void foo66268(); +extern void foo66269(); +extern void foo66270(); +extern void foo66271(); +extern void foo66272(); +extern void foo66273(); +extern void foo66274(); +extern void foo66275(); +extern void foo66276(); +extern void foo66277(); +extern void foo66278(); +extern void foo66279(); +extern void foo66280(); +extern void foo66281(); +extern void foo66282(); +extern void foo66283(); +extern void foo66284(); +extern void foo66285(); +extern void foo66286(); +extern void foo66287(); +extern void foo66288(); +extern void foo66289(); +extern void foo66290(); +extern void foo66291(); +extern void foo66292(); +extern void foo66293(); +extern void foo66294(); +extern void foo66295(); +extern void foo66296(); +extern void foo66297(); +extern void foo66298(); +extern void foo66299(); +extern void foo66300(); +extern void foo66301(); +extern void foo66302(); +extern void foo66303(); +extern void foo66304(); +extern void foo66305(); +extern void foo66306(); +extern void foo66307(); +extern void foo66308(); +extern void foo66309(); +extern void foo66310(); +extern void foo66311(); +extern void foo66312(); +extern void foo66313(); +extern void foo66314(); +extern void foo66315(); +extern void foo66316(); +extern void foo66317(); +extern void foo66318(); +extern void foo66319(); +extern void foo66320(); +extern void foo66321(); +extern void foo66322(); +extern void foo66323(); +extern void foo66324(); +extern void foo66325(); +extern void foo66326(); +extern void foo66327(); +extern void foo66328(); +extern void foo66329(); +extern void foo66330(); +extern void foo66331(); +extern void foo66332(); +extern void foo66333(); +extern void foo66334(); +extern void foo66335(); +extern void foo66336(); +extern void foo66337(); +extern void foo66338(); +extern void foo66339(); +extern void foo66340(); +extern void foo66341(); +extern void foo66342(); +extern void foo66343(); +extern void foo66344(); +extern void foo66345(); +extern void foo66346(); +extern void foo66347(); +extern void foo66348(); +extern void foo66349(); +extern void foo66350(); +extern void foo66351(); +extern void foo66352(); +extern void foo66353(); +extern void foo66354(); +extern void foo66355(); +extern void foo66356(); +extern void foo66357(); +extern void foo66358(); +extern void foo66359(); +extern void foo66360(); +extern void foo66361(); +extern void foo66362(); +extern void foo66363(); +extern void foo66364(); +extern void foo66365(); +extern void foo66366(); +extern void foo66367(); +extern void foo66368(); +extern void foo66369(); +extern void foo66370(); +extern void foo66371(); +extern void foo66372(); +extern void foo66373(); +extern void foo66374(); +extern void foo66375(); +extern void foo66376(); +extern void foo66377(); +extern void foo66378(); +extern void foo66379(); +extern void foo66380(); +extern void foo66381(); +extern void foo66382(); +extern void foo66383(); +extern void foo66384(); +extern void foo66385(); +extern void foo66386(); +extern void foo66387(); +extern void foo66388(); +extern void foo66389(); +extern void foo66390(); +extern void foo66391(); +extern void foo66392(); +extern void foo66393(); +extern void foo66394(); +extern void foo66395(); +extern void foo66396(); +extern void foo66397(); +extern void foo66398(); +extern void foo66399(); +extern void foo66400(); +extern void foo66401(); +extern void foo66402(); +extern void foo66403(); +extern void foo66404(); +extern void foo66405(); +extern void foo66406(); +extern void foo66407(); +extern void foo66408(); +extern void foo66409(); +extern void foo66410(); +extern void foo66411(); +extern void foo66412(); +extern void foo66413(); +extern void foo66414(); +extern void foo66415(); +extern void foo66416(); +extern void foo66417(); +extern void foo66418(); +extern void foo66419(); +extern void foo66420(); +extern void foo66421(); +extern void foo66422(); +extern void foo66423(); +extern void foo66424(); +extern void foo66425(); +extern void foo66426(); +extern void foo66427(); +extern void foo66428(); +extern void foo66429(); +extern void foo66430(); +extern void foo66431(); +extern void foo66432(); +extern void foo66433(); +extern void foo66434(); +extern void foo66435(); +extern void foo66436(); +extern void foo66437(); +extern void foo66438(); +extern void foo66439(); +extern void foo66440(); +extern void foo66441(); +extern void foo66442(); +extern void foo66443(); +extern void foo66444(); +extern void foo66445(); +extern void foo66446(); +extern void foo66447(); +extern void foo66448(); +extern void foo66449(); +extern void foo66450(); +extern void foo66451(); +extern void foo66452(); +extern void foo66453(); +extern void foo66454(); +extern void foo66455(); +extern void foo66456(); +extern void foo66457(); +extern void foo66458(); +extern void foo66459(); +extern void foo66460(); +extern void foo66461(); +extern void foo66462(); +extern void foo66463(); +extern void foo66464(); +extern void foo66465(); +extern void foo66466(); +extern void foo66467(); +extern void foo66468(); +extern void foo66469(); +extern void foo66470(); +extern void foo66471(); +extern void foo66472(); +extern void foo66473(); +extern void foo66474(); +extern void foo66475(); +extern void foo66476(); +extern void foo66477(); +extern void foo66478(); +extern void foo66479(); +extern void foo66480(); +extern void foo66481(); +extern void foo66482(); +extern void foo66483(); +extern void foo66484(); +extern void foo66485(); +extern void foo66486(); +extern void foo66487(); +extern void foo66488(); +extern void foo66489(); +extern void foo66490(); +extern void foo66491(); +extern void foo66492(); +extern void foo66493(); +extern void foo66494(); +extern void foo66495(); +extern void foo66496(); +extern void foo66497(); +extern void foo66498(); +extern void foo66499(); +extern void foo66500(); +extern void foo66501(); +extern void foo66502(); +extern void foo66503(); +extern void foo66504(); +extern void foo66505(); +extern void foo66506(); +extern void foo66507(); +extern void foo66508(); +extern void foo66509(); +extern void foo66510(); +extern void foo66511(); +extern void foo66512(); +extern void foo66513(); +extern void foo66514(); +extern void foo66515(); +extern void foo66516(); +extern void foo66517(); +extern void foo66518(); +extern void foo66519(); +extern void foo66520(); +extern void foo66521(); +extern void foo66522(); +extern void foo66523(); +extern void foo66524(); +extern void foo66525(); +extern void foo66526(); +extern void foo66527(); +extern void foo66528(); +extern void foo66529(); +extern void foo66530(); +extern void foo66531(); +extern void foo66532(); +extern void foo66533(); +extern void foo66534(); +extern void foo66535(); +extern void foo66536(); +extern void foo66537(); +extern void foo66538(); +extern void foo66539(); +extern void foo66540(); +extern void foo66541(); +extern void foo66542(); +extern void foo66543(); +extern void foo66544(); +extern void foo66545(); +extern void foo66546(); +extern void foo66547(); +extern void foo66548(); +extern void foo66549(); +extern void foo66550(); +extern void foo66551(); +extern void foo66552(); +extern void foo66553(); +extern void foo66554(); +extern void foo66555(); +extern void foo66556(); +extern void foo66557(); +extern void foo66558(); +extern void foo66559(); +extern void foo66560(); +extern void foo66561(); +extern void foo66562(); +extern void foo66563(); +extern void foo66564(); +extern void foo66565(); +extern void foo66566(); +extern void foo66567(); +extern void foo66568(); +extern void foo66569(); +extern void foo66570(); +extern void foo66571(); +extern void foo66572(); +extern void foo66573(); +extern void foo66574(); +extern void foo66575(); +extern void foo66576(); +extern void foo66577(); +extern void foo66578(); +extern void foo66579(); +extern void foo66580(); +extern void foo66581(); +extern void foo66582(); +extern void foo66583(); +extern void foo66584(); +extern void foo66585(); +extern void foo66586(); +extern void foo66587(); +extern void foo66588(); +extern void foo66589(); +extern void foo66590(); +extern void foo66591(); +extern void foo66592(); +extern void foo66593(); +extern void foo66594(); +extern void foo66595(); +extern void foo66596(); +extern void foo66597(); +extern void foo66598(); +extern void foo66599(); +extern void foo66600(); +extern void foo66601(); +extern void foo66602(); +extern void foo66603(); +extern void foo66604(); +extern void foo66605(); +extern void foo66606(); +extern void foo66607(); +extern void foo66608(); +extern void foo66609(); +extern void foo66610(); +extern void foo66611(); +extern void foo66612(); +extern void foo66613(); +extern void foo66614(); +extern void foo66615(); +extern void foo66616(); +extern void foo66617(); +extern void foo66618(); +extern void foo66619(); +extern void foo66620(); +extern void foo66621(); +extern void foo66622(); +extern void foo66623(); +extern void foo66624(); +extern void foo66625(); +extern void foo66626(); +extern void foo66627(); +extern void foo66628(); +extern void foo66629(); +extern void foo66630(); +extern void foo66631(); +extern void foo66632(); +extern void foo66633(); +extern void foo66634(); +extern void foo66635(); +extern void foo66636(); +extern void foo66637(); +extern void foo66638(); +extern void foo66639(); +extern void foo66640(); +extern void foo66641(); +extern void foo66642(); +extern void foo66643(); +extern void foo66644(); +extern void foo66645(); +extern void foo66646(); +extern void foo66647(); +extern void foo66648(); +extern void foo66649(); +extern void foo66650(); +extern void foo66651(); +extern void foo66652(); +extern void foo66653(); +extern void foo66654(); +extern void foo66655(); +extern void foo66656(); +extern void foo66657(); +extern void foo66658(); +extern void foo66659(); +extern void foo66660(); +extern void foo66661(); +extern void foo66662(); +extern void foo66663(); +extern void foo66664(); +extern void foo66665(); +extern void foo66666(); +extern void foo66667(); +extern void foo66668(); +extern void foo66669(); +extern void foo66670(); +extern void foo66671(); +extern void foo66672(); +extern void foo66673(); +extern void foo66674(); +extern void foo66675(); +extern void foo66676(); +extern void foo66677(); +extern void foo66678(); +extern void foo66679(); +extern void foo66680(); +extern void foo66681(); +extern void foo66682(); +extern void foo66683(); +extern void foo66684(); +extern void foo66685(); +extern void foo66686(); +extern void foo66687(); +extern void foo66688(); +extern void foo66689(); +extern void foo66690(); +extern void foo66691(); +extern void foo66692(); +extern void foo66693(); +extern void foo66694(); +extern void foo66695(); +extern void foo66696(); +extern void foo66697(); +extern void foo66698(); +extern void foo66699(); +extern void foo66700(); +extern void foo66701(); +extern void foo66702(); +extern void foo66703(); +extern void foo66704(); +extern void foo66705(); +extern void foo66706(); +extern void foo66707(); +extern void foo66708(); +extern void foo66709(); +extern void foo66710(); +extern void foo66711(); +extern void foo66712(); +extern void foo66713(); +extern void foo66714(); +extern void foo66715(); +extern void foo66716(); +extern void foo66717(); +extern void foo66718(); +extern void foo66719(); +extern void foo66720(); +extern void foo66721(); +extern void foo66722(); +extern void foo66723(); +extern void foo66724(); +extern void foo66725(); +extern void foo66726(); +extern void foo66727(); +extern void foo66728(); +extern void foo66729(); +extern void foo66730(); +extern void foo66731(); +extern void foo66732(); +extern void foo66733(); +extern void foo66734(); +extern void foo66735(); +extern void foo66736(); +extern void foo66737(); +extern void foo66738(); +extern void foo66739(); +extern void foo66740(); +extern void foo66741(); +extern void foo66742(); +extern void foo66743(); +extern void foo66744(); +extern void foo66745(); +extern void foo66746(); +extern void foo66747(); +extern void foo66748(); +extern void foo66749(); +extern void foo66750(); +extern void foo66751(); +extern void foo66752(); +extern void foo66753(); +extern void foo66754(); +extern void foo66755(); +extern void foo66756(); +extern void foo66757(); +extern void foo66758(); +extern void foo66759(); +extern void foo66760(); +extern void foo66761(); +extern void foo66762(); +extern void foo66763(); +extern void foo66764(); +extern void foo66765(); +extern void foo66766(); +extern void foo66767(); +extern void foo66768(); +extern void foo66769(); +extern void foo66770(); +extern void foo66771(); +extern void foo66772(); +extern void foo66773(); +extern void foo66774(); +extern void foo66775(); +extern void foo66776(); +extern void foo66777(); +extern void foo66778(); +extern void foo66779(); +extern void foo66780(); +extern void foo66781(); +extern void foo66782(); +extern void foo66783(); +extern void foo66784(); +extern void foo66785(); +extern void foo66786(); +extern void foo66787(); +extern void foo66788(); +extern void foo66789(); +extern void foo66790(); +extern void foo66791(); +extern void foo66792(); +extern void foo66793(); +extern void foo66794(); +extern void foo66795(); +extern void foo66796(); +extern void foo66797(); +extern void foo66798(); +extern void foo66799(); +extern void foo66800(); +extern void foo66801(); +extern void foo66802(); +extern void foo66803(); +extern void foo66804(); +extern void foo66805(); +extern void foo66806(); +extern void foo66807(); +extern void foo66808(); +extern void foo66809(); +extern void foo66810(); +extern void foo66811(); +extern void foo66812(); +extern void foo66813(); +extern void foo66814(); +extern void foo66815(); +extern void foo66816(); +extern void foo66817(); +extern void foo66818(); +extern void foo66819(); +extern void foo66820(); +extern void foo66821(); +extern void foo66822(); +extern void foo66823(); +extern void foo66824(); +extern void foo66825(); +extern void foo66826(); +extern void foo66827(); +extern void foo66828(); +extern void foo66829(); +extern void foo66830(); +extern void foo66831(); +extern void foo66832(); +extern void foo66833(); +extern void foo66834(); +extern void foo66835(); +extern void foo66836(); +extern void foo66837(); +extern void foo66838(); +extern void foo66839(); +extern void foo66840(); +extern void foo66841(); +extern void foo66842(); +extern void foo66843(); +extern void foo66844(); +extern void foo66845(); +extern void foo66846(); +extern void foo66847(); +extern void foo66848(); +extern void foo66849(); +extern void foo66850(); +extern void foo66851(); +extern void foo66852(); +extern void foo66853(); +extern void foo66854(); +extern void foo66855(); +extern void foo66856(); +extern void foo66857(); +extern void foo66858(); +extern void foo66859(); +extern void foo66860(); +extern void foo66861(); +extern void foo66862(); +extern void foo66863(); +extern void foo66864(); +extern void foo66865(); +extern void foo66866(); +extern void foo66867(); +extern void foo66868(); +extern void foo66869(); +extern void foo66870(); +extern void foo66871(); +extern void foo66872(); +extern void foo66873(); +extern void foo66874(); +extern void foo66875(); +extern void foo66876(); +extern void foo66877(); +extern void foo66878(); +extern void foo66879(); +extern void foo66880(); +extern void foo66881(); +extern void foo66882(); +extern void foo66883(); +extern void foo66884(); +extern void foo66885(); +extern void foo66886(); +extern void foo66887(); +extern void foo66888(); +extern void foo66889(); +extern void foo66890(); +extern void foo66891(); +extern void foo66892(); +extern void foo66893(); +extern void foo66894(); +extern void foo66895(); +extern void foo66896(); +extern void foo66897(); +extern void foo66898(); +extern void foo66899(); +extern void foo66900(); +extern void foo66901(); +extern void foo66902(); +extern void foo66903(); +extern void foo66904(); +extern void foo66905(); +extern void foo66906(); +extern void foo66907(); +extern void foo66908(); +extern void foo66909(); +extern void foo66910(); +extern void foo66911(); +extern void foo66912(); +extern void foo66913(); +extern void foo66914(); +extern void foo66915(); +extern void foo66916(); +extern void foo66917(); +extern void foo66918(); +extern void foo66919(); +extern void foo66920(); +extern void foo66921(); +extern void foo66922(); +extern void foo66923(); +extern void foo66924(); +extern void foo66925(); +extern void foo66926(); +extern void foo66927(); +extern void foo66928(); +extern void foo66929(); +extern void foo66930(); +extern void foo66931(); +extern void foo66932(); +extern void foo66933(); +extern void foo66934(); +extern void foo66935(); +extern void foo66936(); +extern void foo66937(); +extern void foo66938(); +extern void foo66939(); +extern void foo66940(); +extern void foo66941(); +extern void foo66942(); +extern void foo66943(); +extern void foo66944(); +extern void foo66945(); +extern void foo66946(); +extern void foo66947(); +extern void foo66948(); +extern void foo66949(); +extern void foo66950(); +extern void foo66951(); +extern void foo66952(); +extern void foo66953(); +extern void foo66954(); +extern void foo66955(); +extern void foo66956(); +extern void foo66957(); +extern void foo66958(); +extern void foo66959(); +extern void foo66960(); +extern void foo66961(); +extern void foo66962(); +extern void foo66963(); +extern void foo66964(); +extern void foo66965(); +extern void foo66966(); +extern void foo66967(); +extern void foo66968(); +extern void foo66969(); +extern void foo66970(); +extern void foo66971(); +extern void foo66972(); +extern void foo66973(); +extern void foo66974(); +extern void foo66975(); +extern void foo66976(); +extern void foo66977(); +extern void foo66978(); +extern void foo66979(); +extern void foo66980(); +extern void foo66981(); +extern void foo66982(); +extern void foo66983(); +extern void foo66984(); +extern void foo66985(); +extern void foo66986(); +extern void foo66987(); +extern void foo66988(); +extern void foo66989(); +extern void foo66990(); +extern void foo66991(); +extern void foo66992(); +extern void foo66993(); +extern void foo66994(); +extern void foo66995(); +extern void foo66996(); +extern void foo66997(); +extern void foo66998(); +extern void foo66999(); +extern void foo67000(); +extern void foo67001(); +extern void foo67002(); +extern void foo67003(); +extern void foo67004(); +extern void foo67005(); +extern void foo67006(); +extern void foo67007(); +extern void foo67008(); +extern void foo67009(); +extern void foo67010(); +extern void foo67011(); +extern void foo67012(); +extern void foo67013(); +extern void foo67014(); +extern void foo67015(); +extern void foo67016(); +extern void foo67017(); +extern void foo67018(); +extern void foo67019(); +extern void foo67020(); +extern void foo67021(); +extern void foo67022(); +extern void foo67023(); +extern void foo67024(); +extern void foo67025(); +extern void foo67026(); +extern void foo67027(); +extern void foo67028(); +extern void foo67029(); +extern void foo67030(); +extern void foo67031(); +extern void foo67032(); +extern void foo67033(); +extern void foo67034(); +extern void foo67035(); +extern void foo67036(); +extern void foo67037(); +extern void foo67038(); +extern void foo67039(); +extern void foo67040(); +extern void foo67041(); +extern void foo67042(); +extern void foo67043(); +extern void foo67044(); +extern void foo67045(); +extern void foo67046(); +extern void foo67047(); +extern void foo67048(); +extern void foo67049(); +extern void foo67050(); +extern void foo67051(); +extern void foo67052(); +extern void foo67053(); +extern void foo67054(); +extern void foo67055(); +extern void foo67056(); +extern void foo67057(); +extern void foo67058(); +extern void foo67059(); +extern void foo67060(); +extern void foo67061(); +extern void foo67062(); +extern void foo67063(); +extern void foo67064(); +extern void foo67065(); +extern void foo67066(); +extern void foo67067(); +extern void foo67068(); +extern void foo67069(); +extern void foo67070(); +extern void foo67071(); +extern void foo67072(); +extern void foo67073(); +extern void foo67074(); +extern void foo67075(); +extern void foo67076(); +extern void foo67077(); +extern void foo67078(); +extern void foo67079(); +extern void foo67080(); +extern void foo67081(); +extern void foo67082(); +extern void foo67083(); +extern void foo67084(); +extern void foo67085(); +extern void foo67086(); +extern void foo67087(); +extern void foo67088(); +extern void foo67089(); +extern void foo67090(); +extern void foo67091(); +extern void foo67092(); +extern void foo67093(); +extern void foo67094(); +extern void foo67095(); +extern void foo67096(); +extern void foo67097(); +extern void foo67098(); +extern void foo67099(); +extern void foo67100(); +extern void foo67101(); +extern void foo67102(); +extern void foo67103(); +extern void foo67104(); +extern void foo67105(); +extern void foo67106(); +extern void foo67107(); +extern void foo67108(); +extern void foo67109(); +extern void foo67110(); +extern void foo67111(); +extern void foo67112(); +extern void foo67113(); +extern void foo67114(); +extern void foo67115(); +extern void foo67116(); +extern void foo67117(); +extern void foo67118(); +extern void foo67119(); +extern void foo67120(); +extern void foo67121(); +extern void foo67122(); +extern void foo67123(); +extern void foo67124(); +extern void foo67125(); +extern void foo67126(); +extern void foo67127(); +extern void foo67128(); +extern void foo67129(); +extern void foo67130(); +extern void foo67131(); +extern void foo67132(); +extern void foo67133(); +extern void foo67134(); +extern void foo67135(); +extern void foo67136(); +extern void foo67137(); +extern void foo67138(); +extern void foo67139(); +extern void foo67140(); +extern void foo67141(); +extern void foo67142(); +extern void foo67143(); +extern void foo67144(); +extern void foo67145(); +extern void foo67146(); +extern void foo67147(); +extern void foo67148(); +extern void foo67149(); +extern void foo67150(); +extern void foo67151(); +extern void foo67152(); +extern void foo67153(); +extern void foo67154(); +extern void foo67155(); +extern void foo67156(); +extern void foo67157(); +extern void foo67158(); +extern void foo67159(); +extern void foo67160(); +extern void foo67161(); +extern void foo67162(); +extern void foo67163(); +extern void foo67164(); +extern void foo67165(); +extern void foo67166(); +extern void foo67167(); +extern void foo67168(); +extern void foo67169(); +extern void foo67170(); +extern void foo67171(); +extern void foo67172(); +extern void foo67173(); +extern void foo67174(); +extern void foo67175(); +extern void foo67176(); +extern void foo67177(); +extern void foo67178(); +extern void foo67179(); +extern void foo67180(); +extern void foo67181(); +extern void foo67182(); +extern void foo67183(); +extern void foo67184(); +extern void foo67185(); +extern void foo67186(); +extern void foo67187(); +extern void foo67188(); +extern void foo67189(); +extern void foo67190(); +extern void foo67191(); +extern void foo67192(); +extern void foo67193(); +extern void foo67194(); +extern void foo67195(); +extern void foo67196(); +extern void foo67197(); +extern void foo67198(); +extern void foo67199(); +extern void foo67200(); +extern void foo67201(); +extern void foo67202(); +extern void foo67203(); +extern void foo67204(); +extern void foo67205(); +extern void foo67206(); +extern void foo67207(); +extern void foo67208(); +extern void foo67209(); +extern void foo67210(); +extern void foo67211(); +extern void foo67212(); +extern void foo67213(); +extern void foo67214(); +extern void foo67215(); +extern void foo67216(); +extern void foo67217(); +extern void foo67218(); +extern void foo67219(); +extern void foo67220(); +extern void foo67221(); +extern void foo67222(); +extern void foo67223(); +extern void foo67224(); +extern void foo67225(); +extern void foo67226(); +extern void foo67227(); +extern void foo67228(); +extern void foo67229(); +extern void foo67230(); +extern void foo67231(); +extern void foo67232(); +extern void foo67233(); +extern void foo67234(); +extern void foo67235(); +extern void foo67236(); +extern void foo67237(); +extern void foo67238(); +extern void foo67239(); +extern void foo67240(); +extern void foo67241(); +extern void foo67242(); +extern void foo67243(); +extern void foo67244(); +extern void foo67245(); +extern void foo67246(); +extern void foo67247(); +extern void foo67248(); +extern void foo67249(); +extern void foo67250(); +extern void foo67251(); +extern void foo67252(); +extern void foo67253(); +extern void foo67254(); +extern void foo67255(); +extern void foo67256(); +extern void foo67257(); +extern void foo67258(); +extern void foo67259(); +extern void foo67260(); +extern void foo67261(); +extern void foo67262(); +extern void foo67263(); +extern void foo67264(); +extern void foo67265(); +extern void foo67266(); +extern void foo67267(); +extern void foo67268(); +extern void foo67269(); +extern void foo67270(); +extern void foo67271(); +extern void foo67272(); +extern void foo67273(); +extern void foo67274(); +extern void foo67275(); +extern void foo67276(); +extern void foo67277(); +extern void foo67278(); +extern void foo67279(); +extern void foo67280(); +extern void foo67281(); +extern void foo67282(); +extern void foo67283(); +extern void foo67284(); +extern void foo67285(); +extern void foo67286(); +extern void foo67287(); +extern void foo67288(); +extern void foo67289(); +extern void foo67290(); +extern void foo67291(); +extern void foo67292(); +extern void foo67293(); +extern void foo67294(); +extern void foo67295(); +extern void foo67296(); +extern void foo67297(); +extern void foo67298(); +extern void foo67299(); +extern void foo67300(); +extern void foo67301(); +extern void foo67302(); +extern void foo67303(); +extern void foo67304(); +extern void foo67305(); +extern void foo67306(); +extern void foo67307(); +extern void foo67308(); +extern void foo67309(); +extern void foo67310(); +extern void foo67311(); +extern void foo67312(); +extern void foo67313(); +extern void foo67314(); +extern void foo67315(); +extern void foo67316(); +extern void foo67317(); +extern void foo67318(); +extern void foo67319(); +extern void foo67320(); +extern void foo67321(); +extern void foo67322(); +extern void foo67323(); +extern void foo67324(); +extern void foo67325(); +extern void foo67326(); +extern void foo67327(); +extern void foo67328(); +extern void foo67329(); +extern void foo67330(); +extern void foo67331(); +extern void foo67332(); +extern void foo67333(); +extern void foo67334(); +extern void foo67335(); +extern void foo67336(); +extern void foo67337(); +extern void foo67338(); +extern void foo67339(); +extern void foo67340(); +extern void foo67341(); +extern void foo67342(); +extern void foo67343(); +extern void foo67344(); +extern void foo67345(); +extern void foo67346(); +extern void foo67347(); +extern void foo67348(); +extern void foo67349(); +extern void foo67350(); +extern void foo67351(); +extern void foo67352(); +extern void foo67353(); +extern void foo67354(); +extern void foo67355(); +extern void foo67356(); +extern void foo67357(); +extern void foo67358(); +extern void foo67359(); +extern void foo67360(); +extern void foo67361(); +extern void foo67362(); +extern void foo67363(); +extern void foo67364(); +extern void foo67365(); +extern void foo67366(); +extern void foo67367(); +extern void foo67368(); +extern void foo67369(); +extern void foo67370(); +extern void foo67371(); +extern void foo67372(); +extern void foo67373(); +extern void foo67374(); +extern void foo67375(); +extern void foo67376(); +extern void foo67377(); +extern void foo67378(); +extern void foo67379(); +extern void foo67380(); +extern void foo67381(); +extern void foo67382(); +extern void foo67383(); +extern void foo67384(); +extern void foo67385(); +extern void foo67386(); +extern void foo67387(); +extern void foo67388(); +extern void foo67389(); +extern void foo67390(); +extern void foo67391(); +extern void foo67392(); +extern void foo67393(); +extern void foo67394(); +extern void foo67395(); +extern void foo67396(); +extern void foo67397(); +extern void foo67398(); +extern void foo67399(); +extern void foo67400(); +extern void foo67401(); +extern void foo67402(); +extern void foo67403(); +extern void foo67404(); +extern void foo67405(); +extern void foo67406(); +extern void foo67407(); +extern void foo67408(); +extern void foo67409(); +extern void foo67410(); +extern void foo67411(); +extern void foo67412(); +extern void foo67413(); +extern void foo67414(); +extern void foo67415(); +extern void foo67416(); +extern void foo67417(); +extern void foo67418(); +extern void foo67419(); +extern void foo67420(); +extern void foo67421(); +extern void foo67422(); +extern void foo67423(); +extern void foo67424(); +extern void foo67425(); +extern void foo67426(); +extern void foo67427(); +extern void foo67428(); +extern void foo67429(); +extern void foo67430(); +extern void foo67431(); +extern void foo67432(); +extern void foo67433(); +extern void foo67434(); +extern void foo67435(); +extern void foo67436(); +extern void foo67437(); +extern void foo67438(); +extern void foo67439(); +extern void foo67440(); +extern void foo67441(); +extern void foo67442(); +extern void foo67443(); +extern void foo67444(); +extern void foo67445(); +extern void foo67446(); +extern void foo67447(); +extern void foo67448(); +extern void foo67449(); +extern void foo67450(); +extern void foo67451(); +extern void foo67452(); +extern void foo67453(); +extern void foo67454(); +extern void foo67455(); +extern void foo67456(); +extern void foo67457(); +extern void foo67458(); +extern void foo67459(); +extern void foo67460(); +extern void foo67461(); +extern void foo67462(); +extern void foo67463(); +extern void foo67464(); +extern void foo67465(); +extern void foo67466(); +extern void foo67467(); +extern void foo67468(); +extern void foo67469(); +extern void foo67470(); +extern void foo67471(); +extern void foo67472(); +extern void foo67473(); +extern void foo67474(); +extern void foo67475(); +extern void foo67476(); +extern void foo67477(); +extern void foo67478(); +extern void foo67479(); +extern void foo67480(); +extern void foo67481(); +extern void foo67482(); +extern void foo67483(); +extern void foo67484(); +extern void foo67485(); +extern void foo67486(); +extern void foo67487(); +extern void foo67488(); +extern void foo67489(); +extern void foo67490(); +extern void foo67491(); +extern void foo67492(); +extern void foo67493(); +extern void foo67494(); +extern void foo67495(); +extern void foo67496(); +extern void foo67497(); +extern void foo67498(); +extern void foo67499(); +extern void foo67500(); +extern void foo67501(); +extern void foo67502(); +extern void foo67503(); +extern void foo67504(); +extern void foo67505(); +extern void foo67506(); +extern void foo67507(); +extern void foo67508(); +extern void foo67509(); +extern void foo67510(); +extern void foo67511(); +extern void foo67512(); +extern void foo67513(); +extern void foo67514(); +extern void foo67515(); +extern void foo67516(); +extern void foo67517(); +extern void foo67518(); +extern void foo67519(); +extern void foo67520(); +extern void foo67521(); +extern void foo67522(); +extern void foo67523(); +extern void foo67524(); +extern void foo67525(); +extern void foo67526(); +extern void foo67527(); +extern void foo67528(); +extern void foo67529(); +extern void foo67530(); +extern void foo67531(); +extern void foo67532(); +extern void foo67533(); +extern void foo67534(); +extern void foo67535(); +extern void foo67536(); +extern void foo67537(); +extern void foo67538(); +extern void foo67539(); +extern void foo67540(); +extern void foo67541(); +extern void foo67542(); +extern void foo67543(); +extern void foo67544(); +extern void foo67545(); +extern void foo67546(); +extern void foo67547(); +extern void foo67548(); +extern void foo67549(); +extern void foo67550(); +extern void foo67551(); +extern void foo67552(); +extern void foo67553(); +extern void foo67554(); +extern void foo67555(); +extern void foo67556(); +extern void foo67557(); +extern void foo67558(); +extern void foo67559(); +extern void foo67560(); +extern void foo67561(); +extern void foo67562(); +extern void foo67563(); +extern void foo67564(); +extern void foo67565(); +extern void foo67566(); +extern void foo67567(); +extern void foo67568(); +extern void foo67569(); +extern void foo67570(); +extern void foo67571(); +extern void foo67572(); +extern void foo67573(); +extern void foo67574(); +extern void foo67575(); +extern void foo67576(); +extern void foo67577(); +extern void foo67578(); +extern void foo67579(); +extern void foo67580(); +extern void foo67581(); +extern void foo67582(); +extern void foo67583(); +extern void foo67584(); +extern void foo67585(); +extern void foo67586(); +extern void foo67587(); +extern void foo67588(); +extern void foo67589(); +extern void foo67590(); +extern void foo67591(); +extern void foo67592(); +extern void foo67593(); +extern void foo67594(); +extern void foo67595(); +extern void foo67596(); +extern void foo67597(); +extern void foo67598(); +extern void foo67599(); +extern void foo67600(); +extern void foo67601(); +extern void foo67602(); +extern void foo67603(); +extern void foo67604(); +extern void foo67605(); +extern void foo67606(); +extern void foo67607(); +extern void foo67608(); +extern void foo67609(); +extern void foo67610(); +extern void foo67611(); +extern void foo67612(); +extern void foo67613(); +extern void foo67614(); +extern void foo67615(); +extern void foo67616(); +extern void foo67617(); +extern void foo67618(); +extern void foo67619(); +extern void foo67620(); +extern void foo67621(); +extern void foo67622(); +extern void foo67623(); +extern void foo67624(); +extern void foo67625(); +extern void foo67626(); +extern void foo67627(); +extern void foo67628(); +extern void foo67629(); +extern void foo67630(); +extern void foo67631(); +extern void foo67632(); +extern void foo67633(); +extern void foo67634(); +extern void foo67635(); +extern void foo67636(); +extern void foo67637(); +extern void foo67638(); +extern void foo67639(); +extern void foo67640(); +extern void foo67641(); +extern void foo67642(); +extern void foo67643(); +extern void foo67644(); +extern void foo67645(); +extern void foo67646(); +extern void foo67647(); +extern void foo67648(); +extern void foo67649(); +extern void foo67650(); +extern void foo67651(); +extern void foo67652(); +extern void foo67653(); +extern void foo67654(); +extern void foo67655(); +extern void foo67656(); +extern void foo67657(); +extern void foo67658(); +extern void foo67659(); +extern void foo67660(); +extern void foo67661(); +extern void foo67662(); +extern void foo67663(); +extern void foo67664(); +extern void foo67665(); +extern void foo67666(); +extern void foo67667(); +extern void foo67668(); +extern void foo67669(); +extern void foo67670(); +extern void foo67671(); +extern void foo67672(); +extern void foo67673(); +extern void foo67674(); +extern void foo67675(); +extern void foo67676(); +extern void foo67677(); +extern void foo67678(); +extern void foo67679(); +extern void foo67680(); +extern void foo67681(); +extern void foo67682(); +extern void foo67683(); +extern void foo67684(); +extern void foo67685(); +extern void foo67686(); +extern void foo67687(); +extern void foo67688(); +extern void foo67689(); +extern void foo67690(); +extern void foo67691(); +extern void foo67692(); +extern void foo67693(); +extern void foo67694(); +extern void foo67695(); +extern void foo67696(); +extern void foo67697(); +extern void foo67698(); +extern void foo67699(); +extern void foo67700(); +extern void foo67701(); +extern void foo67702(); +extern void foo67703(); +extern void foo67704(); +extern void foo67705(); +extern void foo67706(); +extern void foo67707(); +extern void foo67708(); +extern void foo67709(); +extern void foo67710(); +extern void foo67711(); +extern void foo67712(); +extern void foo67713(); +extern void foo67714(); +extern void foo67715(); +extern void foo67716(); +extern void foo67717(); +extern void foo67718(); +extern void foo67719(); +extern void foo67720(); +extern void foo67721(); +extern void foo67722(); +extern void foo67723(); +extern void foo67724(); +extern void foo67725(); +extern void foo67726(); +extern void foo67727(); +extern void foo67728(); +extern void foo67729(); +extern void foo67730(); +extern void foo67731(); +extern void foo67732(); +extern void foo67733(); +extern void foo67734(); +extern void foo67735(); +extern void foo67736(); +extern void foo67737(); +extern void foo67738(); +extern void foo67739(); +extern void foo67740(); +extern void foo67741(); +extern void foo67742(); +extern void foo67743(); +extern void foo67744(); +extern void foo67745(); +extern void foo67746(); +extern void foo67747(); +extern void foo67748(); +extern void foo67749(); +extern void foo67750(); +extern void foo67751(); +extern void foo67752(); +extern void foo67753(); +extern void foo67754(); +extern void foo67755(); +extern void foo67756(); +extern void foo67757(); +extern void foo67758(); +extern void foo67759(); +extern void foo67760(); +extern void foo67761(); +extern void foo67762(); +extern void foo67763(); +extern void foo67764(); +extern void foo67765(); +extern void foo67766(); +extern void foo67767(); +extern void foo67768(); +extern void foo67769(); +extern void foo67770(); +extern void foo67771(); +extern void foo67772(); +extern void foo67773(); +extern void foo67774(); +extern void foo67775(); +extern void foo67776(); +extern void foo67777(); +extern void foo67778(); +extern void foo67779(); +extern void foo67780(); +extern void foo67781(); +extern void foo67782(); +extern void foo67783(); +extern void foo67784(); +extern void foo67785(); +extern void foo67786(); +extern void foo67787(); +extern void foo67788(); +extern void foo67789(); +extern void foo67790(); +extern void foo67791(); +extern void foo67792(); +extern void foo67793(); +extern void foo67794(); +extern void foo67795(); +extern void foo67796(); +extern void foo67797(); +extern void foo67798(); +extern void foo67799(); +extern void foo67800(); +extern void foo67801(); +extern void foo67802(); +extern void foo67803(); +extern void foo67804(); +extern void foo67805(); +extern void foo67806(); +extern void foo67807(); +extern void foo67808(); +extern void foo67809(); +extern void foo67810(); +extern void foo67811(); +extern void foo67812(); +extern void foo67813(); +extern void foo67814(); +extern void foo67815(); +extern void foo67816(); +extern void foo67817(); +extern void foo67818(); +extern void foo67819(); +extern void foo67820(); +extern void foo67821(); +extern void foo67822(); +extern void foo67823(); +extern void foo67824(); +extern void foo67825(); +extern void foo67826(); +extern void foo67827(); +extern void foo67828(); +extern void foo67829(); +extern void foo67830(); +extern void foo67831(); +extern void foo67832(); +extern void foo67833(); +extern void foo67834(); +extern void foo67835(); +extern void foo67836(); +extern void foo67837(); +extern void foo67838(); +extern void foo67839(); +extern void foo67840(); +extern void foo67841(); +extern void foo67842(); +extern void foo67843(); +extern void foo67844(); +extern void foo67845(); +extern void foo67846(); +extern void foo67847(); +extern void foo67848(); +extern void foo67849(); +extern void foo67850(); +extern void foo67851(); +extern void foo67852(); +extern void foo67853(); +extern void foo67854(); +extern void foo67855(); +extern void foo67856(); +extern void foo67857(); +extern void foo67858(); +extern void foo67859(); +extern void foo67860(); +extern void foo67861(); +extern void foo67862(); +extern void foo67863(); +extern void foo67864(); +extern void foo67865(); +extern void foo67866(); +extern void foo67867(); +extern void foo67868(); +extern void foo67869(); +extern void foo67870(); +extern void foo67871(); +extern void foo67872(); +extern void foo67873(); +extern void foo67874(); +extern void foo67875(); +extern void foo67876(); +extern void foo67877(); +extern void foo67878(); +extern void foo67879(); +extern void foo67880(); +extern void foo67881(); +extern void foo67882(); +extern void foo67883(); +extern void foo67884(); +extern void foo67885(); +extern void foo67886(); +extern void foo67887(); +extern void foo67888(); +extern void foo67889(); +extern void foo67890(); +extern void foo67891(); +extern void foo67892(); +extern void foo67893(); +extern void foo67894(); +extern void foo67895(); +extern void foo67896(); +extern void foo67897(); +extern void foo67898(); +extern void foo67899(); +extern void foo67900(); +extern void foo67901(); +extern void foo67902(); +extern void foo67903(); +extern void foo67904(); +extern void foo67905(); +extern void foo67906(); +extern void foo67907(); +extern void foo67908(); +extern void foo67909(); +extern void foo67910(); +extern void foo67911(); +extern void foo67912(); +extern void foo67913(); +extern void foo67914(); +extern void foo67915(); +extern void foo67916(); +extern void foo67917(); +extern void foo67918(); +extern void foo67919(); +extern void foo67920(); +extern void foo67921(); +extern void foo67922(); +extern void foo67923(); +extern void foo67924(); +extern void foo67925(); +extern void foo67926(); +extern void foo67927(); +extern void foo67928(); +extern void foo67929(); +extern void foo67930(); +extern void foo67931(); +extern void foo67932(); +extern void foo67933(); +extern void foo67934(); +extern void foo67935(); +extern void foo67936(); +extern void foo67937(); +extern void foo67938(); +extern void foo67939(); +extern void foo67940(); +extern void foo67941(); +extern void foo67942(); +extern void foo67943(); +extern void foo67944(); +extern void foo67945(); +extern void foo67946(); +extern void foo67947(); +extern void foo67948(); +extern void foo67949(); +extern void foo67950(); +extern void foo67951(); +extern void foo67952(); +extern void foo67953(); +extern void foo67954(); +extern void foo67955(); +extern void foo67956(); +extern void foo67957(); +extern void foo67958(); +extern void foo67959(); +extern void foo67960(); +extern void foo67961(); +extern void foo67962(); +extern void foo67963(); +extern void foo67964(); +extern void foo67965(); +extern void foo67966(); +extern void foo67967(); +extern void foo67968(); +extern void foo67969(); +extern void foo67970(); +extern void foo67971(); +extern void foo67972(); +extern void foo67973(); +extern void foo67974(); +extern void foo67975(); +extern void foo67976(); +extern void foo67977(); +extern void foo67978(); +extern void foo67979(); +extern void foo67980(); +extern void foo67981(); +extern void foo67982(); +extern void foo67983(); +extern void foo67984(); +extern void foo67985(); +extern void foo67986(); +extern void foo67987(); +extern void foo67988(); +extern void foo67989(); +extern void foo67990(); +extern void foo67991(); +extern void foo67992(); +extern void foo67993(); +extern void foo67994(); +extern void foo67995(); +extern void foo67996(); +extern void foo67997(); +extern void foo67998(); +extern void foo67999(); +extern void foo68000(); +extern void foo68001(); +extern void foo68002(); +extern void foo68003(); +extern void foo68004(); +extern void foo68005(); +extern void foo68006(); +extern void foo68007(); +extern void foo68008(); +extern void foo68009(); +extern void foo68010(); +extern void foo68011(); +extern void foo68012(); +extern void foo68013(); +extern void foo68014(); +extern void foo68015(); +extern void foo68016(); +extern void foo68017(); +extern void foo68018(); +extern void foo68019(); +extern void foo68020(); +extern void foo68021(); +extern void foo68022(); +extern void foo68023(); +extern void foo68024(); +extern void foo68025(); +extern void foo68026(); +extern void foo68027(); +extern void foo68028(); +extern void foo68029(); +extern void foo68030(); +extern void foo68031(); +extern void foo68032(); +extern void foo68033(); +extern void foo68034(); +extern void foo68035(); +extern void foo68036(); +extern void foo68037(); +extern void foo68038(); +extern void foo68039(); +extern void foo68040(); +extern void foo68041(); +extern void foo68042(); +extern void foo68043(); +extern void foo68044(); +extern void foo68045(); +extern void foo68046(); +extern void foo68047(); +extern void foo68048(); +extern void foo68049(); +extern void foo68050(); +extern void foo68051(); +extern void foo68052(); +extern void foo68053(); +extern void foo68054(); +extern void foo68055(); +extern void foo68056(); +extern void foo68057(); +extern void foo68058(); +extern void foo68059(); +extern void foo68060(); +extern void foo68061(); +extern void foo68062(); +extern void foo68063(); +extern void foo68064(); +extern void foo68065(); +extern void foo68066(); +extern void foo68067(); +extern void foo68068(); +extern void foo68069(); +extern void foo68070(); +extern void foo68071(); +extern void foo68072(); +extern void foo68073(); +extern void foo68074(); +extern void foo68075(); +extern void foo68076(); +extern void foo68077(); +extern void foo68078(); +extern void foo68079(); +extern void foo68080(); +extern void foo68081(); +extern void foo68082(); +extern void foo68083(); +extern void foo68084(); +extern void foo68085(); +extern void foo68086(); +extern void foo68087(); +extern void foo68088(); +extern void foo68089(); +extern void foo68090(); +extern void foo68091(); +extern void foo68092(); +extern void foo68093(); +extern void foo68094(); +extern void foo68095(); +extern void foo68096(); +extern void foo68097(); +extern void foo68098(); +extern void foo68099(); +extern void foo68100(); +extern void foo68101(); +extern void foo68102(); +extern void foo68103(); +extern void foo68104(); +extern void foo68105(); +extern void foo68106(); +extern void foo68107(); +extern void foo68108(); +extern void foo68109(); +extern void foo68110(); +extern void foo68111(); +extern void foo68112(); +extern void foo68113(); +extern void foo68114(); +extern void foo68115(); +extern void foo68116(); +extern void foo68117(); +extern void foo68118(); +extern void foo68119(); +extern void foo68120(); +extern void foo68121(); +extern void foo68122(); +extern void foo68123(); +extern void foo68124(); +extern void foo68125(); +extern void foo68126(); +extern void foo68127(); +extern void foo68128(); +extern void foo68129(); +extern void foo68130(); +extern void foo68131(); +extern void foo68132(); +extern void foo68133(); +extern void foo68134(); +extern void foo68135(); +extern void foo68136(); +extern void foo68137(); +extern void foo68138(); +extern void foo68139(); +extern void foo68140(); +extern void foo68141(); +extern void foo68142(); +extern void foo68143(); +extern void foo68144(); +extern void foo68145(); +extern void foo68146(); +extern void foo68147(); +extern void foo68148(); +extern void foo68149(); +extern void foo68150(); +extern void foo68151(); +extern void foo68152(); +extern void foo68153(); +extern void foo68154(); +extern void foo68155(); +extern void foo68156(); +extern void foo68157(); +extern void foo68158(); +extern void foo68159(); +extern void foo68160(); +extern void foo68161(); +extern void foo68162(); +extern void foo68163(); +extern void foo68164(); +extern void foo68165(); +extern void foo68166(); +extern void foo68167(); +extern void foo68168(); +extern void foo68169(); +extern void foo68170(); +extern void foo68171(); +extern void foo68172(); +extern void foo68173(); +extern void foo68174(); +extern void foo68175(); +extern void foo68176(); +extern void foo68177(); +extern void foo68178(); +extern void foo68179(); +extern void foo68180(); +extern void foo68181(); +extern void foo68182(); +extern void foo68183(); +extern void foo68184(); +extern void foo68185(); +extern void foo68186(); +extern void foo68187(); +extern void foo68188(); +extern void foo68189(); +extern void foo68190(); +extern void foo68191(); +extern void foo68192(); +extern void foo68193(); +extern void foo68194(); +extern void foo68195(); +extern void foo68196(); +extern void foo68197(); +extern void foo68198(); +extern void foo68199(); +extern void foo68200(); +extern void foo68201(); +extern void foo68202(); +extern void foo68203(); +extern void foo68204(); +extern void foo68205(); +extern void foo68206(); +extern void foo68207(); +extern void foo68208(); +extern void foo68209(); +extern void foo68210(); +extern void foo68211(); +extern void foo68212(); +extern void foo68213(); +extern void foo68214(); +extern void foo68215(); +extern void foo68216(); +extern void foo68217(); +extern void foo68218(); +extern void foo68219(); +extern void foo68220(); +extern void foo68221(); +extern void foo68222(); +extern void foo68223(); +extern void foo68224(); +extern void foo68225(); +extern void foo68226(); +extern void foo68227(); +extern void foo68228(); +extern void foo68229(); +extern void foo68230(); +extern void foo68231(); +extern void foo68232(); +extern void foo68233(); +extern void foo68234(); +extern void foo68235(); +extern void foo68236(); +extern void foo68237(); +extern void foo68238(); +extern void foo68239(); +extern void foo68240(); +extern void foo68241(); +extern void foo68242(); +extern void foo68243(); +extern void foo68244(); +extern void foo68245(); +extern void foo68246(); +extern void foo68247(); +extern void foo68248(); +extern void foo68249(); +extern void foo68250(); +extern void foo68251(); +extern void foo68252(); +extern void foo68253(); +extern void foo68254(); +extern void foo68255(); +extern void foo68256(); +extern void foo68257(); +extern void foo68258(); +extern void foo68259(); +extern void foo68260(); +extern void foo68261(); +extern void foo68262(); +extern void foo68263(); +extern void foo68264(); +extern void foo68265(); +extern void foo68266(); +extern void foo68267(); +extern void foo68268(); +extern void foo68269(); +extern void foo68270(); +extern void foo68271(); +extern void foo68272(); +extern void foo68273(); +extern void foo68274(); +extern void foo68275(); +extern void foo68276(); +extern void foo68277(); +extern void foo68278(); +extern void foo68279(); +extern void foo68280(); +extern void foo68281(); +extern void foo68282(); +extern void foo68283(); +extern void foo68284(); +extern void foo68285(); +extern void foo68286(); +extern void foo68287(); +extern void foo68288(); +extern void foo68289(); +extern void foo68290(); +extern void foo68291(); +extern void foo68292(); +extern void foo68293(); +extern void foo68294(); +extern void foo68295(); +extern void foo68296(); +extern void foo68297(); +extern void foo68298(); +extern void foo68299(); +extern void foo68300(); +extern void foo68301(); +extern void foo68302(); +extern void foo68303(); +extern void foo68304(); +extern void foo68305(); +extern void foo68306(); +extern void foo68307(); +extern void foo68308(); +extern void foo68309(); +extern void foo68310(); +extern void foo68311(); +extern void foo68312(); +extern void foo68313(); +extern void foo68314(); +extern void foo68315(); +extern void foo68316(); +extern void foo68317(); +extern void foo68318(); +extern void foo68319(); +extern void foo68320(); +extern void foo68321(); +extern void foo68322(); +extern void foo68323(); +extern void foo68324(); +extern void foo68325(); +extern void foo68326(); +extern void foo68327(); +extern void foo68328(); +extern void foo68329(); +extern void foo68330(); +extern void foo68331(); +extern void foo68332(); +extern void foo68333(); +extern void foo68334(); +extern void foo68335(); +extern void foo68336(); +extern void foo68337(); +extern void foo68338(); +extern void foo68339(); +extern void foo68340(); +extern void foo68341(); +extern void foo68342(); +extern void foo68343(); +extern void foo68344(); +extern void foo68345(); +extern void foo68346(); +extern void foo68347(); +extern void foo68348(); +extern void foo68349(); +extern void foo68350(); +extern void foo68351(); +extern void foo68352(); +extern void foo68353(); +extern void foo68354(); +extern void foo68355(); +extern void foo68356(); +extern void foo68357(); +extern void foo68358(); +extern void foo68359(); +extern void foo68360(); +extern void foo68361(); +extern void foo68362(); +extern void foo68363(); +extern void foo68364(); +extern void foo68365(); +extern void foo68366(); +extern void foo68367(); +extern void foo68368(); +extern void foo68369(); +extern void foo68370(); +extern void foo68371(); +extern void foo68372(); +extern void foo68373(); +extern void foo68374(); +extern void foo68375(); +extern void foo68376(); +extern void foo68377(); +extern void foo68378(); +extern void foo68379(); +extern void foo68380(); +extern void foo68381(); +extern void foo68382(); +extern void foo68383(); +extern void foo68384(); +extern void foo68385(); +extern void foo68386(); +extern void foo68387(); +extern void foo68388(); +extern void foo68389(); +extern void foo68390(); +extern void foo68391(); +extern void foo68392(); +extern void foo68393(); +extern void foo68394(); +extern void foo68395(); +extern void foo68396(); +extern void foo68397(); +extern void foo68398(); +extern void foo68399(); +extern void foo68400(); +extern void foo68401(); +extern void foo68402(); +extern void foo68403(); +extern void foo68404(); +extern void foo68405(); +extern void foo68406(); +extern void foo68407(); +extern void foo68408(); +extern void foo68409(); +extern void foo68410(); +extern void foo68411(); +extern void foo68412(); +extern void foo68413(); +extern void foo68414(); +extern void foo68415(); +extern void foo68416(); +extern void foo68417(); +extern void foo68418(); +extern void foo68419(); +extern void foo68420(); +extern void foo68421(); +extern void foo68422(); +extern void foo68423(); +extern void foo68424(); +extern void foo68425(); +extern void foo68426(); +extern void foo68427(); +extern void foo68428(); +extern void foo68429(); +extern void foo68430(); +extern void foo68431(); +extern void foo68432(); +extern void foo68433(); +extern void foo68434(); +extern void foo68435(); +extern void foo68436(); +extern void foo68437(); +extern void foo68438(); +extern void foo68439(); +extern void foo68440(); +extern void foo68441(); +extern void foo68442(); +extern void foo68443(); +extern void foo68444(); +extern void foo68445(); +extern void foo68446(); +extern void foo68447(); +extern void foo68448(); +extern void foo68449(); +extern void foo68450(); +extern void foo68451(); +extern void foo68452(); +extern void foo68453(); +extern void foo68454(); +extern void foo68455(); +extern void foo68456(); +extern void foo68457(); +extern void foo68458(); +extern void foo68459(); +extern void foo68460(); +extern void foo68461(); +extern void foo68462(); +extern void foo68463(); +extern void foo68464(); +extern void foo68465(); +extern void foo68466(); +extern void foo68467(); +extern void foo68468(); +extern void foo68469(); +extern void foo68470(); +extern void foo68471(); +extern void foo68472(); +extern void foo68473(); +extern void foo68474(); +extern void foo68475(); +extern void foo68476(); +extern void foo68477(); +extern void foo68478(); +extern void foo68479(); +extern void foo68480(); +extern void foo68481(); +extern void foo68482(); +extern void foo68483(); +extern void foo68484(); +extern void foo68485(); +extern void foo68486(); +extern void foo68487(); +extern void foo68488(); +extern void foo68489(); +extern void foo68490(); +extern void foo68491(); +extern void foo68492(); +extern void foo68493(); +extern void foo68494(); +extern void foo68495(); +extern void foo68496(); +extern void foo68497(); +extern void foo68498(); +extern void foo68499(); +extern void foo68500(); +extern void foo68501(); +extern void foo68502(); +extern void foo68503(); +extern void foo68504(); +extern void foo68505(); +extern void foo68506(); +extern void foo68507(); +extern void foo68508(); +extern void foo68509(); +extern void foo68510(); +extern void foo68511(); +extern void foo68512(); +extern void foo68513(); +extern void foo68514(); +extern void foo68515(); +extern void foo68516(); +extern void foo68517(); +extern void foo68518(); +extern void foo68519(); +extern void foo68520(); +extern void foo68521(); +extern void foo68522(); +extern void foo68523(); +extern void foo68524(); +extern void foo68525(); +extern void foo68526(); +extern void foo68527(); +extern void foo68528(); +extern void foo68529(); +extern void foo68530(); +extern void foo68531(); +extern void foo68532(); +extern void foo68533(); +extern void foo68534(); +extern void foo68535(); +extern void foo68536(); +extern void foo68537(); +extern void foo68538(); +extern void foo68539(); +extern void foo68540(); +extern void foo68541(); +extern void foo68542(); +extern void foo68543(); +extern void foo68544(); +extern void foo68545(); +extern void foo68546(); +extern void foo68547(); +extern void foo68548(); +extern void foo68549(); +extern void foo68550(); +extern void foo68551(); +extern void foo68552(); +extern void foo68553(); +extern void foo68554(); +extern void foo68555(); +extern void foo68556(); +extern void foo68557(); +extern void foo68558(); +extern void foo68559(); +extern void foo68560(); +extern void foo68561(); +extern void foo68562(); +extern void foo68563(); +extern void foo68564(); +extern void foo68565(); +extern void foo68566(); +extern void foo68567(); +extern void foo68568(); +extern void foo68569(); +extern void foo68570(); +extern void foo68571(); +extern void foo68572(); +extern void foo68573(); +extern void foo68574(); +extern void foo68575(); +extern void foo68576(); +extern void foo68577(); +extern void foo68578(); +extern void foo68579(); +extern void foo68580(); +extern void foo68581(); +extern void foo68582(); +extern void foo68583(); +extern void foo68584(); +extern void foo68585(); +extern void foo68586(); +extern void foo68587(); +extern void foo68588(); +extern void foo68589(); +extern void foo68590(); +extern void foo68591(); +extern void foo68592(); +extern void foo68593(); +extern void foo68594(); +extern void foo68595(); +extern void foo68596(); +extern void foo68597(); +extern void foo68598(); +extern void foo68599(); +extern void foo68600(); +extern void foo68601(); +extern void foo68602(); +extern void foo68603(); +extern void foo68604(); +extern void foo68605(); +extern void foo68606(); +extern void foo68607(); +extern void foo68608(); +extern void foo68609(); +extern void foo68610(); +extern void foo68611(); +extern void foo68612(); +extern void foo68613(); +extern void foo68614(); +extern void foo68615(); +extern void foo68616(); +extern void foo68617(); +extern void foo68618(); +extern void foo68619(); +extern void foo68620(); +extern void foo68621(); +extern void foo68622(); +extern void foo68623(); +extern void foo68624(); +extern void foo68625(); +extern void foo68626(); +extern void foo68627(); +extern void foo68628(); +extern void foo68629(); +extern void foo68630(); +extern void foo68631(); +extern void foo68632(); +extern void foo68633(); +extern void foo68634(); +extern void foo68635(); +extern void foo68636(); +extern void foo68637(); +extern void foo68638(); +extern void foo68639(); +extern void foo68640(); +extern void foo68641(); +extern void foo68642(); +extern void foo68643(); +extern void foo68644(); +extern void foo68645(); +extern void foo68646(); +extern void foo68647(); +extern void foo68648(); +extern void foo68649(); +extern void foo68650(); +extern void foo68651(); +extern void foo68652(); +extern void foo68653(); +extern void foo68654(); +extern void foo68655(); +extern void foo68656(); +extern void foo68657(); +extern void foo68658(); +extern void foo68659(); +extern void foo68660(); +extern void foo68661(); +extern void foo68662(); +extern void foo68663(); +extern void foo68664(); +extern void foo68665(); +extern void foo68666(); +extern void foo68667(); +extern void foo68668(); +extern void foo68669(); +extern void foo68670(); +extern void foo68671(); +extern void foo68672(); +extern void foo68673(); +extern void foo68674(); +extern void foo68675(); +extern void foo68676(); +extern void foo68677(); +extern void foo68678(); +extern void foo68679(); +extern void foo68680(); +extern void foo68681(); +extern void foo68682(); +extern void foo68683(); +extern void foo68684(); +extern void foo68685(); +extern void foo68686(); +extern void foo68687(); +extern void foo68688(); +extern void foo68689(); +extern void foo68690(); +extern void foo68691(); +extern void foo68692(); +extern void foo68693(); +extern void foo68694(); +extern void foo68695(); +extern void foo68696(); +extern void foo68697(); +extern void foo68698(); +extern void foo68699(); +extern void foo68700(); +extern void foo68701(); +extern void foo68702(); +extern void foo68703(); +extern void foo68704(); +extern void foo68705(); +extern void foo68706(); +extern void foo68707(); +extern void foo68708(); +extern void foo68709(); +extern void foo68710(); +extern void foo68711(); +extern void foo68712(); +extern void foo68713(); +extern void foo68714(); +extern void foo68715(); +extern void foo68716(); +extern void foo68717(); +extern void foo68718(); +extern void foo68719(); +extern void foo68720(); +extern void foo68721(); +extern void foo68722(); +extern void foo68723(); +extern void foo68724(); +extern void foo68725(); +extern void foo68726(); +extern void foo68727(); +extern void foo68728(); +extern void foo68729(); +extern void foo68730(); +extern void foo68731(); +extern void foo68732(); +extern void foo68733(); +extern void foo68734(); +extern void foo68735(); +extern void foo68736(); +extern void foo68737(); +extern void foo68738(); +extern void foo68739(); +extern void foo68740(); +extern void foo68741(); +extern void foo68742(); +extern void foo68743(); +extern void foo68744(); +extern void foo68745(); +extern void foo68746(); +extern void foo68747(); +extern void foo68748(); +extern void foo68749(); +extern void foo68750(); +extern void foo68751(); +extern void foo68752(); +extern void foo68753(); +extern void foo68754(); +extern void foo68755(); +extern void foo68756(); +extern void foo68757(); +extern void foo68758(); +extern void foo68759(); +extern void foo68760(); +extern void foo68761(); +extern void foo68762(); +extern void foo68763(); +extern void foo68764(); +extern void foo68765(); +extern void foo68766(); +extern void foo68767(); +extern void foo68768(); +extern void foo68769(); +extern void foo68770(); +extern void foo68771(); +extern void foo68772(); +extern void foo68773(); +extern void foo68774(); +extern void foo68775(); +extern void foo68776(); +extern void foo68777(); +extern void foo68778(); +extern void foo68779(); +extern void foo68780(); +extern void foo68781(); +extern void foo68782(); +extern void foo68783(); +extern void foo68784(); +extern void foo68785(); +extern void foo68786(); +extern void foo68787(); +extern void foo68788(); +extern void foo68789(); +extern void foo68790(); +extern void foo68791(); +extern void foo68792(); +extern void foo68793(); +extern void foo68794(); +extern void foo68795(); +extern void foo68796(); +extern void foo68797(); +extern void foo68798(); +extern void foo68799(); +extern void foo68800(); +extern void foo68801(); +extern void foo68802(); +extern void foo68803(); +extern void foo68804(); +extern void foo68805(); +extern void foo68806(); +extern void foo68807(); +extern void foo68808(); +extern void foo68809(); +extern void foo68810(); +extern void foo68811(); +extern void foo68812(); +extern void foo68813(); +extern void foo68814(); +extern void foo68815(); +extern void foo68816(); +extern void foo68817(); +extern void foo68818(); +extern void foo68819(); +extern void foo68820(); +extern void foo68821(); +extern void foo68822(); +extern void foo68823(); +extern void foo68824(); +extern void foo68825(); +extern void foo68826(); +extern void foo68827(); +extern void foo68828(); +extern void foo68829(); +extern void foo68830(); +extern void foo68831(); +extern void foo68832(); +extern void foo68833(); +extern void foo68834(); +extern void foo68835(); +extern void foo68836(); +extern void foo68837(); +extern void foo68838(); +extern void foo68839(); +extern void foo68840(); +extern void foo68841(); +extern void foo68842(); +extern void foo68843(); +extern void foo68844(); +extern void foo68845(); +extern void foo68846(); +extern void foo68847(); +extern void foo68848(); +extern void foo68849(); +extern void foo68850(); +extern void foo68851(); +extern void foo68852(); +extern void foo68853(); +extern void foo68854(); +extern void foo68855(); +extern void foo68856(); +extern void foo68857(); +extern void foo68858(); +extern void foo68859(); +extern void foo68860(); +extern void foo68861(); +extern void foo68862(); +extern void foo68863(); +extern void foo68864(); +extern void foo68865(); +extern void foo68866(); +extern void foo68867(); +extern void foo68868(); +extern void foo68869(); +extern void foo68870(); +extern void foo68871(); +extern void foo68872(); +extern void foo68873(); +extern void foo68874(); +extern void foo68875(); +extern void foo68876(); +extern void foo68877(); +extern void foo68878(); +extern void foo68879(); +extern void foo68880(); +extern void foo68881(); +extern void foo68882(); +extern void foo68883(); +extern void foo68884(); +extern void foo68885(); +extern void foo68886(); +extern void foo68887(); +extern void foo68888(); +extern void foo68889(); +extern void foo68890(); +extern void foo68891(); +extern void foo68892(); +extern void foo68893(); +extern void foo68894(); +extern void foo68895(); +extern void foo68896(); +extern void foo68897(); +extern void foo68898(); +extern void foo68899(); +extern void foo68900(); +extern void foo68901(); +extern void foo68902(); +extern void foo68903(); +extern void foo68904(); +extern void foo68905(); +extern void foo68906(); +extern void foo68907(); +extern void foo68908(); +extern void foo68909(); +extern void foo68910(); +extern void foo68911(); +extern void foo68912(); +extern void foo68913(); +extern void foo68914(); +extern void foo68915(); +extern void foo68916(); +extern void foo68917(); +extern void foo68918(); +extern void foo68919(); +extern void foo68920(); +extern void foo68921(); +extern void foo68922(); +extern void foo68923(); +extern void foo68924(); +extern void foo68925(); +extern void foo68926(); +extern void foo68927(); +extern void foo68928(); +extern void foo68929(); +extern void foo68930(); +extern void foo68931(); +extern void foo68932(); +extern void foo68933(); +extern void foo68934(); +extern void foo68935(); +extern void foo68936(); +extern void foo68937(); +extern void foo68938(); +extern void foo68939(); +extern void foo68940(); +extern void foo68941(); +extern void foo68942(); +extern void foo68943(); +extern void foo68944(); +extern void foo68945(); +extern void foo68946(); +extern void foo68947(); +extern void foo68948(); +extern void foo68949(); +extern void foo68950(); +extern void foo68951(); +extern void foo68952(); +extern void foo68953(); +extern void foo68954(); +extern void foo68955(); +extern void foo68956(); +extern void foo68957(); +extern void foo68958(); +extern void foo68959(); +extern void foo68960(); +extern void foo68961(); +extern void foo68962(); +extern void foo68963(); +extern void foo68964(); +extern void foo68965(); +extern void foo68966(); +extern void foo68967(); +extern void foo68968(); +extern void foo68969(); +extern void foo68970(); +extern void foo68971(); +extern void foo68972(); +extern void foo68973(); +extern void foo68974(); +extern void foo68975(); +extern void foo68976(); +extern void foo68977(); +extern void foo68978(); +extern void foo68979(); +extern void foo68980(); +extern void foo68981(); +extern void foo68982(); +extern void foo68983(); +extern void foo68984(); +extern void foo68985(); +extern void foo68986(); +extern void foo68987(); +extern void foo68988(); +extern void foo68989(); +extern void foo68990(); +extern void foo68991(); +extern void foo68992(); +extern void foo68993(); +extern void foo68994(); +extern void foo68995(); +extern void foo68996(); +extern void foo68997(); +extern void foo68998(); +extern void foo68999(); +extern void foo69000(); +extern void foo69001(); +extern void foo69002(); +extern void foo69003(); +extern void foo69004(); +extern void foo69005(); +extern void foo69006(); +extern void foo69007(); +extern void foo69008(); +extern void foo69009(); +extern void foo69010(); +extern void foo69011(); +extern void foo69012(); +extern void foo69013(); +extern void foo69014(); +extern void foo69015(); +extern void foo69016(); +extern void foo69017(); +extern void foo69018(); +extern void foo69019(); +extern void foo69020(); +extern void foo69021(); +extern void foo69022(); +extern void foo69023(); +extern void foo69024(); +extern void foo69025(); +extern void foo69026(); +extern void foo69027(); +extern void foo69028(); +extern void foo69029(); +extern void foo69030(); +extern void foo69031(); +extern void foo69032(); +extern void foo69033(); +extern void foo69034(); +extern void foo69035(); +extern void foo69036(); +extern void foo69037(); +extern void foo69038(); +extern void foo69039(); +extern void foo69040(); +extern void foo69041(); +extern void foo69042(); +extern void foo69043(); +extern void foo69044(); +extern void foo69045(); +extern void foo69046(); +extern void foo69047(); +extern void foo69048(); +extern void foo69049(); +extern void foo69050(); +extern void foo69051(); +extern void foo69052(); +extern void foo69053(); +extern void foo69054(); +extern void foo69055(); +extern void foo69056(); +extern void foo69057(); +extern void foo69058(); +extern void foo69059(); +extern void foo69060(); +extern void foo69061(); +extern void foo69062(); +extern void foo69063(); +extern void foo69064(); +extern void foo69065(); +extern void foo69066(); +extern void foo69067(); +extern void foo69068(); +extern void foo69069(); +extern void foo69070(); +extern void foo69071(); +extern void foo69072(); +extern void foo69073(); +extern void foo69074(); +extern void foo69075(); +extern void foo69076(); +extern void foo69077(); +extern void foo69078(); +extern void foo69079(); +extern void foo69080(); +extern void foo69081(); +extern void foo69082(); +extern void foo69083(); +extern void foo69084(); +extern void foo69085(); +extern void foo69086(); +extern void foo69087(); +extern void foo69088(); +extern void foo69089(); +extern void foo69090(); +extern void foo69091(); +extern void foo69092(); +extern void foo69093(); +extern void foo69094(); +extern void foo69095(); +extern void foo69096(); +extern void foo69097(); +extern void foo69098(); +extern void foo69099(); +extern void foo69100(); +extern void foo69101(); +extern void foo69102(); +extern void foo69103(); +extern void foo69104(); +extern void foo69105(); +extern void foo69106(); +extern void foo69107(); +extern void foo69108(); +extern void foo69109(); +extern void foo69110(); +extern void foo69111(); +extern void foo69112(); +extern void foo69113(); +extern void foo69114(); +extern void foo69115(); +extern void foo69116(); +extern void foo69117(); +extern void foo69118(); +extern void foo69119(); +extern void foo69120(); +extern void foo69121(); +extern void foo69122(); +extern void foo69123(); +extern void foo69124(); +extern void foo69125(); +extern void foo69126(); +extern void foo69127(); +extern void foo69128(); +extern void foo69129(); +extern void foo69130(); +extern void foo69131(); +extern void foo69132(); +extern void foo69133(); +extern void foo69134(); +extern void foo69135(); +extern void foo69136(); +extern void foo69137(); +extern void foo69138(); +extern void foo69139(); +extern void foo69140(); +extern void foo69141(); +extern void foo69142(); +extern void foo69143(); +extern void foo69144(); +extern void foo69145(); +extern void foo69146(); +extern void foo69147(); +extern void foo69148(); +extern void foo69149(); +extern void foo69150(); +extern void foo69151(); +extern void foo69152(); +extern void foo69153(); +extern void foo69154(); +extern void foo69155(); +extern void foo69156(); +extern void foo69157(); +extern void foo69158(); +extern void foo69159(); +extern void foo69160(); +extern void foo69161(); +extern void foo69162(); +extern void foo69163(); +extern void foo69164(); +extern void foo69165(); +extern void foo69166(); +extern void foo69167(); +extern void foo69168(); +extern void foo69169(); +extern void foo69170(); +extern void foo69171(); +extern void foo69172(); +extern void foo69173(); +extern void foo69174(); +extern void foo69175(); +extern void foo69176(); +extern void foo69177(); +extern void foo69178(); +extern void foo69179(); +extern void foo69180(); +extern void foo69181(); +extern void foo69182(); +extern void foo69183(); +extern void foo69184(); +extern void foo69185(); +extern void foo69186(); +extern void foo69187(); +extern void foo69188(); +extern void foo69189(); +extern void foo69190(); +extern void foo69191(); +extern void foo69192(); +extern void foo69193(); +extern void foo69194(); +extern void foo69195(); +extern void foo69196(); +extern void foo69197(); +extern void foo69198(); +extern void foo69199(); +extern void foo69200(); +extern void foo69201(); +extern void foo69202(); +extern void foo69203(); +extern void foo69204(); +extern void foo69205(); +extern void foo69206(); +extern void foo69207(); +extern void foo69208(); +extern void foo69209(); +extern void foo69210(); +extern void foo69211(); +extern void foo69212(); +extern void foo69213(); +extern void foo69214(); +extern void foo69215(); +extern void foo69216(); +extern void foo69217(); +extern void foo69218(); +extern void foo69219(); +extern void foo69220(); +extern void foo69221(); +extern void foo69222(); +extern void foo69223(); +extern void foo69224(); +extern void foo69225(); +extern void foo69226(); +extern void foo69227(); +extern void foo69228(); +extern void foo69229(); +extern void foo69230(); +extern void foo69231(); +extern void foo69232(); +extern void foo69233(); +extern void foo69234(); +extern void foo69235(); +extern void foo69236(); +extern void foo69237(); +extern void foo69238(); +extern void foo69239(); +extern void foo69240(); +extern void foo69241(); +extern void foo69242(); +extern void foo69243(); +extern void foo69244(); +extern void foo69245(); +extern void foo69246(); +extern void foo69247(); +extern void foo69248(); +extern void foo69249(); +extern void foo69250(); +extern void foo69251(); +extern void foo69252(); +extern void foo69253(); +extern void foo69254(); +extern void foo69255(); +extern void foo69256(); +extern void foo69257(); +extern void foo69258(); +extern void foo69259(); +extern void foo69260(); +extern void foo69261(); +extern void foo69262(); +extern void foo69263(); +extern void foo69264(); +extern void foo69265(); +extern void foo69266(); +extern void foo69267(); +extern void foo69268(); +extern void foo69269(); +extern void foo69270(); +extern void foo69271(); +extern void foo69272(); +extern void foo69273(); +extern void foo69274(); +extern void foo69275(); +extern void foo69276(); +extern void foo69277(); +extern void foo69278(); +extern void foo69279(); +extern void foo69280(); +extern void foo69281(); +extern void foo69282(); +extern void foo69283(); +extern void foo69284(); +extern void foo69285(); +extern void foo69286(); +extern void foo69287(); +extern void foo69288(); +extern void foo69289(); +extern void foo69290(); +extern void foo69291(); +extern void foo69292(); +extern void foo69293(); +extern void foo69294(); +extern void foo69295(); +extern void foo69296(); +extern void foo69297(); +extern void foo69298(); +extern void foo69299(); +extern void foo69300(); +extern void foo69301(); +extern void foo69302(); +extern void foo69303(); +extern void foo69304(); +extern void foo69305(); +extern void foo69306(); +extern void foo69307(); +extern void foo69308(); +extern void foo69309(); +extern void foo69310(); +extern void foo69311(); +extern void foo69312(); +extern void foo69313(); +extern void foo69314(); +extern void foo69315(); +extern void foo69316(); +extern void foo69317(); +extern void foo69318(); +extern void foo69319(); +extern void foo69320(); +extern void foo69321(); +extern void foo69322(); +extern void foo69323(); +extern void foo69324(); +extern void foo69325(); +extern void foo69326(); +extern void foo69327(); +extern void foo69328(); +extern void foo69329(); +extern void foo69330(); +extern void foo69331(); +extern void foo69332(); +extern void foo69333(); +extern void foo69334(); +extern void foo69335(); +extern void foo69336(); +extern void foo69337(); +extern void foo69338(); +extern void foo69339(); +extern void foo69340(); +extern void foo69341(); +extern void foo69342(); +extern void foo69343(); +extern void foo69344(); +extern void foo69345(); +extern void foo69346(); +extern void foo69347(); +extern void foo69348(); +extern void foo69349(); +extern void foo69350(); +extern void foo69351(); +extern void foo69352(); +extern void foo69353(); +extern void foo69354(); +extern void foo69355(); +extern void foo69356(); +extern void foo69357(); +extern void foo69358(); +extern void foo69359(); +extern void foo69360(); +extern void foo69361(); +extern void foo69362(); +extern void foo69363(); +extern void foo69364(); +extern void foo69365(); +extern void foo69366(); +extern void foo69367(); +extern void foo69368(); +extern void foo69369(); +extern void foo69370(); +extern void foo69371(); +extern void foo69372(); +extern void foo69373(); +extern void foo69374(); +extern void foo69375(); +extern void foo69376(); +extern void foo69377(); +extern void foo69378(); +extern void foo69379(); +extern void foo69380(); +extern void foo69381(); +extern void foo69382(); +extern void foo69383(); +extern void foo69384(); +extern void foo69385(); +extern void foo69386(); +extern void foo69387(); +extern void foo69388(); +extern void foo69389(); +extern void foo69390(); +extern void foo69391(); +extern void foo69392(); +extern void foo69393(); +extern void foo69394(); +extern void foo69395(); +extern void foo69396(); +extern void foo69397(); +extern void foo69398(); +extern void foo69399(); +extern void foo69400(); +extern void foo69401(); +extern void foo69402(); +extern void foo69403(); +extern void foo69404(); +extern void foo69405(); +extern void foo69406(); +extern void foo69407(); +extern void foo69408(); +extern void foo69409(); +extern void foo69410(); +extern void foo69411(); +extern void foo69412(); +extern void foo69413(); +extern void foo69414(); +extern void foo69415(); +extern void foo69416(); +extern void foo69417(); +extern void foo69418(); +extern void foo69419(); +extern void foo69420(); +extern void foo69421(); +extern void foo69422(); +extern void foo69423(); +extern void foo69424(); +extern void foo69425(); +extern void foo69426(); +extern void foo69427(); +extern void foo69428(); +extern void foo69429(); +extern void foo69430(); +extern void foo69431(); +extern void foo69432(); +extern void foo69433(); +extern void foo69434(); +extern void foo69435(); +extern void foo69436(); +extern void foo69437(); +extern void foo69438(); +extern void foo69439(); +extern void foo69440(); +extern void foo69441(); +extern void foo69442(); +extern void foo69443(); +extern void foo69444(); +extern void foo69445(); +extern void foo69446(); +extern void foo69447(); +extern void foo69448(); +extern void foo69449(); +extern void foo69450(); +extern void foo69451(); +extern void foo69452(); +extern void foo69453(); +extern void foo69454(); +extern void foo69455(); +extern void foo69456(); +extern void foo69457(); +extern void foo69458(); +extern void foo69459(); +extern void foo69460(); +extern void foo69461(); +extern void foo69462(); +extern void foo69463(); +extern void foo69464(); +extern void foo69465(); +extern void foo69466(); +extern void foo69467(); +extern void foo69468(); +extern void foo69469(); +extern void foo69470(); +extern void foo69471(); +extern void foo69472(); +extern void foo69473(); +extern void foo69474(); +extern void foo69475(); +extern void foo69476(); +extern void foo69477(); +extern void foo69478(); +extern void foo69479(); +extern void foo69480(); +extern void foo69481(); +extern void foo69482(); +extern void foo69483(); +extern void foo69484(); +extern void foo69485(); +extern void foo69486(); +extern void foo69487(); +extern void foo69488(); +extern void foo69489(); +extern void foo69490(); +extern void foo69491(); +extern void foo69492(); +extern void foo69493(); +extern void foo69494(); +extern void foo69495(); +extern void foo69496(); +extern void foo69497(); +extern void foo69498(); +extern void foo69499(); +extern void foo69500(); +extern void foo69501(); +extern void foo69502(); +extern void foo69503(); +extern void foo69504(); +extern void foo69505(); +extern void foo69506(); +extern void foo69507(); +extern void foo69508(); +extern void foo69509(); +extern void foo69510(); +extern void foo69511(); +extern void foo69512(); +extern void foo69513(); +extern void foo69514(); +extern void foo69515(); +extern void foo69516(); +extern void foo69517(); +extern void foo69518(); +extern void foo69519(); +extern void foo69520(); +extern void foo69521(); +extern void foo69522(); +extern void foo69523(); +extern void foo69524(); +extern void foo69525(); +extern void foo69526(); +extern void foo69527(); +extern void foo69528(); +extern void foo69529(); +extern void foo69530(); +extern void foo69531(); +extern void foo69532(); +extern void foo69533(); +extern void foo69534(); +extern void foo69535(); +extern void foo69536(); +extern void foo69537(); +extern void foo69538(); +extern void foo69539(); +extern void foo69540(); +extern void foo69541(); +extern void foo69542(); +extern void foo69543(); +extern void foo69544(); +extern void foo69545(); +extern void foo69546(); +extern void foo69547(); +extern void foo69548(); +extern void foo69549(); +extern void foo69550(); +extern void foo69551(); +extern void foo69552(); +extern void foo69553(); +extern void foo69554(); +extern void foo69555(); +extern void foo69556(); +extern void foo69557(); +extern void foo69558(); +extern void foo69559(); +extern void foo69560(); +extern void foo69561(); +extern void foo69562(); +extern void foo69563(); +extern void foo69564(); +extern void foo69565(); +extern void foo69566(); +extern void foo69567(); +extern void foo69568(); +extern void foo69569(); +extern void foo69570(); +extern void foo69571(); +extern void foo69572(); +extern void foo69573(); +extern void foo69574(); +extern void foo69575(); +extern void foo69576(); +extern void foo69577(); +extern void foo69578(); +extern void foo69579(); +extern void foo69580(); +extern void foo69581(); +extern void foo69582(); +extern void foo69583(); +extern void foo69584(); +extern void foo69585(); +extern void foo69586(); +extern void foo69587(); +extern void foo69588(); +extern void foo69589(); +extern void foo69590(); +extern void foo69591(); +extern void foo69592(); +extern void foo69593(); +extern void foo69594(); +extern void foo69595(); +extern void foo69596(); +extern void foo69597(); +extern void foo69598(); +extern void foo69599(); +extern void foo69600(); +extern void foo69601(); +extern void foo69602(); +extern void foo69603(); +extern void foo69604(); +extern void foo69605(); +extern void foo69606(); +extern void foo69607(); +extern void foo69608(); +extern void foo69609(); +extern void foo69610(); +extern void foo69611(); +extern void foo69612(); +extern void foo69613(); +extern void foo69614(); +extern void foo69615(); +extern void foo69616(); +extern void foo69617(); +extern void foo69618(); +extern void foo69619(); +extern void foo69620(); +extern void foo69621(); +extern void foo69622(); +extern void foo69623(); +extern void foo69624(); +extern void foo69625(); +extern void foo69626(); +extern void foo69627(); +extern void foo69628(); +extern void foo69629(); +extern void foo69630(); +extern void foo69631(); +extern void foo69632(); +extern void foo69633(); +extern void foo69634(); +extern void foo69635(); +extern void foo69636(); +extern void foo69637(); +extern void foo69638(); +extern void foo69639(); +extern void foo69640(); +extern void foo69641(); +extern void foo69642(); +extern void foo69643(); +extern void foo69644(); +extern void foo69645(); +extern void foo69646(); +extern void foo69647(); +extern void foo69648(); +extern void foo69649(); +extern void foo69650(); +extern void foo69651(); +extern void foo69652(); +extern void foo69653(); +extern void foo69654(); +extern void foo69655(); +extern void foo69656(); +extern void foo69657(); +extern void foo69658(); +extern void foo69659(); +extern void foo69660(); +extern void foo69661(); +extern void foo69662(); +extern void foo69663(); +extern void foo69664(); +extern void foo69665(); +extern void foo69666(); +extern void foo69667(); +extern void foo69668(); +extern void foo69669(); +extern void foo69670(); +extern void foo69671(); +extern void foo69672(); +extern void foo69673(); +extern void foo69674(); +extern void foo69675(); +extern void foo69676(); +extern void foo69677(); +extern void foo69678(); +extern void foo69679(); +extern void foo69680(); +extern void foo69681(); +extern void foo69682(); +extern void foo69683(); +extern void foo69684(); +extern void foo69685(); +extern void foo69686(); +extern void foo69687(); +extern void foo69688(); +extern void foo69689(); +extern void foo69690(); +extern void foo69691(); +extern void foo69692(); +extern void foo69693(); +extern void foo69694(); +extern void foo69695(); +extern void foo69696(); +extern void foo69697(); +extern void foo69698(); +extern void foo69699(); +extern void foo69700(); +extern void foo69701(); +extern void foo69702(); +extern void foo69703(); +extern void foo69704(); +extern void foo69705(); +extern void foo69706(); +extern void foo69707(); +extern void foo69708(); +extern void foo69709(); +extern void foo69710(); +extern void foo69711(); +extern void foo69712(); +extern void foo69713(); +extern void foo69714(); +extern void foo69715(); +extern void foo69716(); +extern void foo69717(); +extern void foo69718(); +extern void foo69719(); +extern void foo69720(); +extern void foo69721(); +extern void foo69722(); +extern void foo69723(); +extern void foo69724(); +extern void foo69725(); +extern void foo69726(); +extern void foo69727(); +extern void foo69728(); +extern void foo69729(); +extern void foo69730(); +extern void foo69731(); +extern void foo69732(); +extern void foo69733(); +extern void foo69734(); +extern void foo69735(); +extern void foo69736(); +extern void foo69737(); +extern void foo69738(); +extern void foo69739(); +extern void foo69740(); +extern void foo69741(); +extern void foo69742(); +extern void foo69743(); +extern void foo69744(); +extern void foo69745(); +extern void foo69746(); +extern void foo69747(); +extern void foo69748(); +extern void foo69749(); +extern void foo69750(); +extern void foo69751(); +extern void foo69752(); +extern void foo69753(); +extern void foo69754(); +extern void foo69755(); +extern void foo69756(); +extern void foo69757(); +extern void foo69758(); +extern void foo69759(); +extern void foo69760(); +extern void foo69761(); +extern void foo69762(); +extern void foo69763(); +extern void foo69764(); +extern void foo69765(); +extern void foo69766(); +extern void foo69767(); +extern void foo69768(); +extern void foo69769(); +extern void foo69770(); +extern void foo69771(); +extern void foo69772(); +extern void foo69773(); +extern void foo69774(); +extern void foo69775(); +extern void foo69776(); +extern void foo69777(); +extern void foo69778(); +extern void foo69779(); +extern void foo69780(); +extern void foo69781(); +extern void foo69782(); +extern void foo69783(); +extern void foo69784(); +extern void foo69785(); +extern void foo69786(); +extern void foo69787(); +extern void foo69788(); +extern void foo69789(); +extern void foo69790(); +extern void foo69791(); +extern void foo69792(); +extern void foo69793(); +extern void foo69794(); +extern void foo69795(); +extern void foo69796(); +extern void foo69797(); +extern void foo69798(); +extern void foo69799(); +extern void foo69800(); +extern void foo69801(); +extern void foo69802(); +extern void foo69803(); +extern void foo69804(); +extern void foo69805(); +extern void foo69806(); +extern void foo69807(); +extern void foo69808(); +extern void foo69809(); +extern void foo69810(); +extern void foo69811(); +extern void foo69812(); +extern void foo69813(); +extern void foo69814(); +extern void foo69815(); +extern void foo69816(); +extern void foo69817(); +extern void foo69818(); +extern void foo69819(); +extern void foo69820(); +extern void foo69821(); +extern void foo69822(); +extern void foo69823(); +extern void foo69824(); +extern void foo69825(); +extern void foo69826(); +extern void foo69827(); +extern void foo69828(); +extern void foo69829(); +extern void foo69830(); +extern void foo69831(); +extern void foo69832(); +extern void foo69833(); +extern void foo69834(); +extern void foo69835(); +extern void foo69836(); +extern void foo69837(); +extern void foo69838(); +extern void foo69839(); +extern void foo69840(); +extern void foo69841(); +extern void foo69842(); +extern void foo69843(); +extern void foo69844(); +extern void foo69845(); +extern void foo69846(); +extern void foo69847(); +extern void foo69848(); +extern void foo69849(); +extern void foo69850(); +extern void foo69851(); +extern void foo69852(); +extern void foo69853(); +extern void foo69854(); +extern void foo69855(); +extern void foo69856(); +extern void foo69857(); +extern void foo69858(); +extern void foo69859(); +extern void foo69860(); +extern void foo69861(); +extern void foo69862(); +extern void foo69863(); +extern void foo69864(); +extern void foo69865(); +extern void foo69866(); +extern void foo69867(); +extern void foo69868(); +extern void foo69869(); +extern void foo69870(); +extern void foo69871(); +extern void foo69872(); +extern void foo69873(); +extern void foo69874(); +extern void foo69875(); +extern void foo69876(); +extern void foo69877(); +extern void foo69878(); +extern void foo69879(); +extern void foo69880(); +extern void foo69881(); +extern void foo69882(); +extern void foo69883(); +extern void foo69884(); +extern void foo69885(); +extern void foo69886(); +extern void foo69887(); +extern void foo69888(); +extern void foo69889(); +extern void foo69890(); +extern void foo69891(); +extern void foo69892(); +extern void foo69893(); +extern void foo69894(); +extern void foo69895(); +extern void foo69896(); +extern void foo69897(); +extern void foo69898(); +extern void foo69899(); +extern void foo69900(); +extern void foo69901(); +extern void foo69902(); +extern void foo69903(); +extern void foo69904(); +extern void foo69905(); +extern void foo69906(); +extern void foo69907(); +extern void foo69908(); +extern void foo69909(); +extern void foo69910(); +extern void foo69911(); +extern void foo69912(); +extern void foo69913(); +extern void foo69914(); +extern void foo69915(); +extern void foo69916(); +extern void foo69917(); +extern void foo69918(); +extern void foo69919(); +extern void foo69920(); +extern void foo69921(); +extern void foo69922(); +extern void foo69923(); +extern void foo69924(); +extern void foo69925(); +extern void foo69926(); +extern void foo69927(); +extern void foo69928(); +extern void foo69929(); +extern void foo69930(); +extern void foo69931(); +extern void foo69932(); +extern void foo69933(); +extern void foo69934(); +extern void foo69935(); +extern void foo69936(); +extern void foo69937(); +extern void foo69938(); +extern void foo69939(); +extern void foo69940(); +extern void foo69941(); +extern void foo69942(); +extern void foo69943(); +extern void foo69944(); +extern void foo69945(); +extern void foo69946(); +extern void foo69947(); +extern void foo69948(); +extern void foo69949(); +extern void foo69950(); +extern void foo69951(); +extern void foo69952(); +extern void foo69953(); +extern void foo69954(); +extern void foo69955(); +extern void foo69956(); +extern void foo69957(); +extern void foo69958(); +extern void foo69959(); +extern void foo69960(); +extern void foo69961(); +extern void foo69962(); +extern void foo69963(); +extern void foo69964(); +extern void foo69965(); +extern void foo69966(); +extern void foo69967(); +extern void foo69968(); +extern void foo69969(); +extern void foo69970(); +extern void foo69971(); +extern void foo69972(); +extern void foo69973(); +extern void foo69974(); +extern void foo69975(); +extern void foo69976(); +extern void foo69977(); +extern void foo69978(); +extern void foo69979(); +extern void foo69980(); +extern void foo69981(); +extern void foo69982(); +extern void foo69983(); +extern void foo69984(); +extern void foo69985(); +extern void foo69986(); +extern void foo69987(); +extern void foo69988(); +extern void foo69989(); +extern void foo69990(); +extern void foo69991(); +extern void foo69992(); +extern void foo69993(); +extern void foo69994(); +extern void foo69995(); +extern void foo69996(); +extern void foo69997(); +extern void foo69998(); +extern void foo69999(); +extern void foo70000(); diff --git a/testing/test-cases/chained-fixups-many-binds.dtest/main.c b/testing/test-cases/chained-fixups-many-binds.dtest/main.c index 698dd0a..4d7b4e0 100644 --- a/testing/test-cases/chained-fixups-many-binds.dtest/main.c +++ b/testing/test-cases/chained-fixups-many-binds.dtest/main.c @@ -5,17 +5,17 @@ // RUN: ./chained-fixups-many-binds.exe // Here's how to generate this monster -// ( for i in `seq 1 65000`; do echo "void foo$i() { }"; done ) > foo.c -// ( for i in `seq 1 65000`; do echo "extern void foo$i();"; done ) > foo.h -// ( for i in `seq 1 65000`; do echo "__attribute__((used)) void* use$i = (void*)&foo$i;"; done ) > uses.h +// ( for i in `seq 1 70000`; do echo "void foo$i() { }"; done ) > foo.c +// ( for i in `seq 1 70000`; do echo "extern void foo$i();"; done ) > foo.h +// ( for i in `seq 1 70000`; do echo "__attribute__((used)) void* use$i = (void*)&foo$i;"; done ) > uses.h #include #include "foo.h" #include "uses.h" -int main() { - printf("[BEGIN] chained-fixups-many-binds\n"); - printf("[PASS] chained-fixups-many-binds\n"); - return 0; -} \ No newline at end of file +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + PASS("Success"); +} diff --git a/testing/test-cases/chained-fixups-many-binds.dtest/uses.h b/testing/test-cases/chained-fixups-many-binds.dtest/uses.h index d61c61f..6a86ce3 100644 --- a/testing/test-cases/chained-fixups-many-binds.dtest/uses.h +++ b/testing/test-cases/chained-fixups-many-binds.dtest/uses.h @@ -64998,3 +64998,5003 @@ __attribute__((used)) void* use64997 = (void*)&foo64997; __attribute__((used)) void* use64998 = (void*)&foo64998; __attribute__((used)) void* use64999 = (void*)&foo64999; __attribute__((used)) void* use65000 = (void*)&foo65000; +__attribute__((used)) void* use65001 = (void*)&foo65001; +__attribute__((used)) void* use65002 = (void*)&foo65002; +__attribute__((used)) void* use65003 = (void*)&foo65003; +__attribute__((used)) void* use65004 = (void*)&foo65004; +__attribute__((used)) void* use65005 = (void*)&foo65005; +__attribute__((used)) void* use65006 = (void*)&foo65006; +__attribute__((used)) void* use65007 = (void*)&foo65007; +__attribute__((used)) void* use65008 = (void*)&foo65008; +__attribute__((used)) void* use65009 = (void*)&foo65009; +__attribute__((used)) void* use65010 = (void*)&foo65010; +__attribute__((used)) void* use65011 = (void*)&foo65011; +__attribute__((used)) void* use65012 = (void*)&foo65012; +__attribute__((used)) void* use65013 = (void*)&foo65013; +__attribute__((used)) void* use65014 = (void*)&foo65014; +__attribute__((used)) void* use65015 = (void*)&foo65015; +__attribute__((used)) void* use65016 = (void*)&foo65016; +__attribute__((used)) void* use65017 = (void*)&foo65017; +__attribute__((used)) void* use65018 = (void*)&foo65018; +__attribute__((used)) void* use65019 = (void*)&foo65019; +__attribute__((used)) void* use65020 = (void*)&foo65020; +__attribute__((used)) void* use65021 = (void*)&foo65021; +__attribute__((used)) void* use65022 = (void*)&foo65022; +__attribute__((used)) void* use65023 = (void*)&foo65023; +__attribute__((used)) void* use65024 = (void*)&foo65024; +__attribute__((used)) void* use65025 = (void*)&foo65025; +__attribute__((used)) void* use65026 = (void*)&foo65026; +__attribute__((used)) void* use65027 = (void*)&foo65027; +__attribute__((used)) void* use65028 = (void*)&foo65028; +__attribute__((used)) void* use65029 = (void*)&foo65029; +__attribute__((used)) void* use65030 = (void*)&foo65030; +__attribute__((used)) void* use65031 = (void*)&foo65031; +__attribute__((used)) void* use65032 = (void*)&foo65032; +__attribute__((used)) void* use65033 = (void*)&foo65033; +__attribute__((used)) void* use65034 = (void*)&foo65034; +__attribute__((used)) void* use65035 = (void*)&foo65035; +__attribute__((used)) void* use65036 = (void*)&foo65036; +__attribute__((used)) void* use65037 = (void*)&foo65037; +__attribute__((used)) void* use65038 = (void*)&foo65038; +__attribute__((used)) void* use65039 = (void*)&foo65039; +__attribute__((used)) void* use65040 = (void*)&foo65040; +__attribute__((used)) void* use65041 = (void*)&foo65041; +__attribute__((used)) void* use65042 = (void*)&foo65042; +__attribute__((used)) void* use65043 = (void*)&foo65043; +__attribute__((used)) void* use65044 = (void*)&foo65044; +__attribute__((used)) void* use65045 = (void*)&foo65045; +__attribute__((used)) void* use65046 = (void*)&foo65046; +__attribute__((used)) void* use65047 = (void*)&foo65047; +__attribute__((used)) void* use65048 = (void*)&foo65048; +__attribute__((used)) void* use65049 = (void*)&foo65049; +__attribute__((used)) void* use65050 = (void*)&foo65050; +__attribute__((used)) void* use65051 = (void*)&foo65051; +__attribute__((used)) void* use65052 = (void*)&foo65052; +__attribute__((used)) void* use65053 = (void*)&foo65053; +__attribute__((used)) void* use65054 = (void*)&foo65054; +__attribute__((used)) void* use65055 = (void*)&foo65055; +__attribute__((used)) void* use65056 = (void*)&foo65056; +__attribute__((used)) void* use65057 = (void*)&foo65057; +__attribute__((used)) void* use65058 = (void*)&foo65058; +__attribute__((used)) void* use65059 = (void*)&foo65059; +__attribute__((used)) void* use65060 = (void*)&foo65060; +__attribute__((used)) void* use65061 = (void*)&foo65061; +__attribute__((used)) void* use65062 = (void*)&foo65062; +__attribute__((used)) void* use65063 = (void*)&foo65063; +__attribute__((used)) void* use65064 = (void*)&foo65064; +__attribute__((used)) void* use65065 = (void*)&foo65065; +__attribute__((used)) void* use65066 = (void*)&foo65066; +__attribute__((used)) void* use65067 = (void*)&foo65067; +__attribute__((used)) void* use65068 = (void*)&foo65068; +__attribute__((used)) void* use65069 = (void*)&foo65069; +__attribute__((used)) void* use65070 = (void*)&foo65070; +__attribute__((used)) void* use65071 = (void*)&foo65071; +__attribute__((used)) void* use65072 = (void*)&foo65072; +__attribute__((used)) void* use65073 = (void*)&foo65073; +__attribute__((used)) void* use65074 = (void*)&foo65074; +__attribute__((used)) void* use65075 = (void*)&foo65075; +__attribute__((used)) void* use65076 = (void*)&foo65076; +__attribute__((used)) void* use65077 = (void*)&foo65077; +__attribute__((used)) void* use65078 = (void*)&foo65078; +__attribute__((used)) void* use65079 = (void*)&foo65079; +__attribute__((used)) void* use65080 = (void*)&foo65080; +__attribute__((used)) void* use65081 = (void*)&foo65081; +__attribute__((used)) void* use65082 = (void*)&foo65082; +__attribute__((used)) void* use65083 = (void*)&foo65083; +__attribute__((used)) void* use65084 = (void*)&foo65084; +__attribute__((used)) void* use65085 = (void*)&foo65085; +__attribute__((used)) void* use65086 = (void*)&foo65086; +__attribute__((used)) void* use65087 = (void*)&foo65087; +__attribute__((used)) void* use65088 = (void*)&foo65088; +__attribute__((used)) void* use65089 = (void*)&foo65089; +__attribute__((used)) void* use65090 = (void*)&foo65090; +__attribute__((used)) void* use65091 = (void*)&foo65091; +__attribute__((used)) void* use65092 = (void*)&foo65092; +__attribute__((used)) void* use65093 = (void*)&foo65093; +__attribute__((used)) void* use65094 = (void*)&foo65094; +__attribute__((used)) void* use65095 = (void*)&foo65095; +__attribute__((used)) void* use65096 = (void*)&foo65096; +__attribute__((used)) void* use65097 = (void*)&foo65097; +__attribute__((used)) void* use65098 = (void*)&foo65098; +__attribute__((used)) void* use65099 = (void*)&foo65099; +__attribute__((used)) void* use65100 = (void*)&foo65100; +__attribute__((used)) void* use65101 = (void*)&foo65101; +__attribute__((used)) void* use65102 = (void*)&foo65102; +__attribute__((used)) void* use65103 = (void*)&foo65103; +__attribute__((used)) void* use65104 = (void*)&foo65104; +__attribute__((used)) void* use65105 = (void*)&foo65105; +__attribute__((used)) void* use65106 = (void*)&foo65106; +__attribute__((used)) void* use65107 = (void*)&foo65107; +__attribute__((used)) void* use65108 = (void*)&foo65108; +__attribute__((used)) void* use65109 = (void*)&foo65109; +__attribute__((used)) void* use65110 = (void*)&foo65110; +__attribute__((used)) void* use65111 = (void*)&foo65111; +__attribute__((used)) void* use65112 = (void*)&foo65112; +__attribute__((used)) void* use65113 = (void*)&foo65113; +__attribute__((used)) void* use65114 = (void*)&foo65114; +__attribute__((used)) void* use65115 = (void*)&foo65115; +__attribute__((used)) void* use65116 = (void*)&foo65116; +__attribute__((used)) void* use65117 = (void*)&foo65117; +__attribute__((used)) void* use65118 = (void*)&foo65118; +__attribute__((used)) void* use65119 = (void*)&foo65119; +__attribute__((used)) void* use65120 = (void*)&foo65120; +__attribute__((used)) void* use65121 = (void*)&foo65121; +__attribute__((used)) void* use65122 = (void*)&foo65122; +__attribute__((used)) void* use65123 = (void*)&foo65123; +__attribute__((used)) void* use65124 = (void*)&foo65124; +__attribute__((used)) void* use65125 = (void*)&foo65125; +__attribute__((used)) void* use65126 = (void*)&foo65126; +__attribute__((used)) void* use65127 = (void*)&foo65127; +__attribute__((used)) void* use65128 = (void*)&foo65128; +__attribute__((used)) void* use65129 = (void*)&foo65129; +__attribute__((used)) void* use65130 = (void*)&foo65130; +__attribute__((used)) void* use65131 = (void*)&foo65131; +__attribute__((used)) void* use65132 = (void*)&foo65132; +__attribute__((used)) void* use65133 = (void*)&foo65133; +__attribute__((used)) void* use65134 = (void*)&foo65134; +__attribute__((used)) void* use65135 = (void*)&foo65135; +__attribute__((used)) void* use65136 = (void*)&foo65136; +__attribute__((used)) void* use65137 = (void*)&foo65137; +__attribute__((used)) void* use65138 = (void*)&foo65138; +__attribute__((used)) void* use65139 = (void*)&foo65139; +__attribute__((used)) void* use65140 = (void*)&foo65140; +__attribute__((used)) void* use65141 = (void*)&foo65141; +__attribute__((used)) void* use65142 = (void*)&foo65142; +__attribute__((used)) void* use65143 = (void*)&foo65143; +__attribute__((used)) void* use65144 = (void*)&foo65144; +__attribute__((used)) void* use65145 = (void*)&foo65145; +__attribute__((used)) void* use65146 = (void*)&foo65146; +__attribute__((used)) void* use65147 = (void*)&foo65147; +__attribute__((used)) void* use65148 = (void*)&foo65148; +__attribute__((used)) void* use65149 = (void*)&foo65149; +__attribute__((used)) void* use65150 = (void*)&foo65150; +__attribute__((used)) void* use65151 = (void*)&foo65151; +__attribute__((used)) void* use65152 = (void*)&foo65152; +__attribute__((used)) void* use65153 = (void*)&foo65153; +__attribute__((used)) void* use65154 = (void*)&foo65154; +__attribute__((used)) void* use65155 = (void*)&foo65155; +__attribute__((used)) void* use65156 = (void*)&foo65156; +__attribute__((used)) void* use65157 = (void*)&foo65157; +__attribute__((used)) void* use65158 = (void*)&foo65158; +__attribute__((used)) void* use65159 = (void*)&foo65159; +__attribute__((used)) void* use65160 = (void*)&foo65160; +__attribute__((used)) void* use65161 = (void*)&foo65161; +__attribute__((used)) void* use65162 = (void*)&foo65162; +__attribute__((used)) void* use65163 = (void*)&foo65163; +__attribute__((used)) void* use65164 = (void*)&foo65164; +__attribute__((used)) void* use65165 = (void*)&foo65165; +__attribute__((used)) void* use65166 = (void*)&foo65166; +__attribute__((used)) void* use65167 = (void*)&foo65167; +__attribute__((used)) void* use65168 = (void*)&foo65168; +__attribute__((used)) void* use65169 = (void*)&foo65169; +__attribute__((used)) void* use65170 = (void*)&foo65170; +__attribute__((used)) void* use65171 = (void*)&foo65171; +__attribute__((used)) void* use65172 = (void*)&foo65172; +__attribute__((used)) void* use65173 = (void*)&foo65173; +__attribute__((used)) void* use65174 = (void*)&foo65174; +__attribute__((used)) void* use65175 = (void*)&foo65175; +__attribute__((used)) void* use65176 = (void*)&foo65176; +__attribute__((used)) void* use65177 = (void*)&foo65177; +__attribute__((used)) void* use65178 = (void*)&foo65178; +__attribute__((used)) void* use65179 = (void*)&foo65179; +__attribute__((used)) void* use65180 = (void*)&foo65180; +__attribute__((used)) void* use65181 = (void*)&foo65181; +__attribute__((used)) void* use65182 = (void*)&foo65182; +__attribute__((used)) void* use65183 = (void*)&foo65183; +__attribute__((used)) void* use65184 = (void*)&foo65184; +__attribute__((used)) void* use65185 = (void*)&foo65185; +__attribute__((used)) void* use65186 = (void*)&foo65186; +__attribute__((used)) void* use65187 = (void*)&foo65187; +__attribute__((used)) void* use65188 = (void*)&foo65188; +__attribute__((used)) void* use65189 = (void*)&foo65189; +__attribute__((used)) void* use65190 = (void*)&foo65190; +__attribute__((used)) void* use65191 = (void*)&foo65191; +__attribute__((used)) void* use65192 = (void*)&foo65192; +__attribute__((used)) void* use65193 = (void*)&foo65193; +__attribute__((used)) void* use65194 = (void*)&foo65194; +__attribute__((used)) void* use65195 = (void*)&foo65195; +__attribute__((used)) void* use65196 = (void*)&foo65196; +__attribute__((used)) void* use65197 = (void*)&foo65197; +__attribute__((used)) void* use65198 = (void*)&foo65198; +__attribute__((used)) void* use65199 = (void*)&foo65199; +__attribute__((used)) void* use65200 = (void*)&foo65200; +__attribute__((used)) void* use65201 = (void*)&foo65201; +__attribute__((used)) void* use65202 = (void*)&foo65202; +__attribute__((used)) void* use65203 = (void*)&foo65203; +__attribute__((used)) void* use65204 = (void*)&foo65204; +__attribute__((used)) void* use65205 = (void*)&foo65205; +__attribute__((used)) void* use65206 = (void*)&foo65206; +__attribute__((used)) void* use65207 = (void*)&foo65207; +__attribute__((used)) void* use65208 = (void*)&foo65208; +__attribute__((used)) void* use65209 = (void*)&foo65209; +__attribute__((used)) void* use65210 = (void*)&foo65210; +__attribute__((used)) void* use65211 = (void*)&foo65211; +__attribute__((used)) void* use65212 = (void*)&foo65212; +__attribute__((used)) void* use65213 = (void*)&foo65213; +__attribute__((used)) void* use65214 = (void*)&foo65214; +__attribute__((used)) void* use65215 = (void*)&foo65215; +__attribute__((used)) void* use65216 = (void*)&foo65216; +__attribute__((used)) void* use65217 = (void*)&foo65217; +__attribute__((used)) void* use65218 = (void*)&foo65218; +__attribute__((used)) void* use65219 = (void*)&foo65219; +__attribute__((used)) void* use65220 = (void*)&foo65220; +__attribute__((used)) void* use65221 = (void*)&foo65221; +__attribute__((used)) void* use65222 = (void*)&foo65222; +__attribute__((used)) void* use65223 = (void*)&foo65223; +__attribute__((used)) void* use65224 = (void*)&foo65224; +__attribute__((used)) void* use65225 = (void*)&foo65225; +__attribute__((used)) void* use65226 = (void*)&foo65226; +__attribute__((used)) void* use65227 = (void*)&foo65227; +__attribute__((used)) void* use65228 = (void*)&foo65228; +__attribute__((used)) void* use65229 = (void*)&foo65229; +__attribute__((used)) void* use65230 = (void*)&foo65230; +__attribute__((used)) void* use65231 = (void*)&foo65231; +__attribute__((used)) void* use65232 = (void*)&foo65232; +__attribute__((used)) void* use65233 = (void*)&foo65233; +__attribute__((used)) void* use65234 = (void*)&foo65234; +__attribute__((used)) void* use65235 = (void*)&foo65235; +__attribute__((used)) void* use65236 = (void*)&foo65236; +__attribute__((used)) void* use65237 = (void*)&foo65237; +__attribute__((used)) void* use65238 = (void*)&foo65238; +__attribute__((used)) void* use65239 = (void*)&foo65239; +__attribute__((used)) void* use65240 = (void*)&foo65240; +__attribute__((used)) void* use65241 = (void*)&foo65241; +__attribute__((used)) void* use65242 = (void*)&foo65242; +__attribute__((used)) void* use65243 = (void*)&foo65243; +__attribute__((used)) void* use65244 = (void*)&foo65244; +__attribute__((used)) void* use65245 = (void*)&foo65245; +__attribute__((used)) void* use65246 = (void*)&foo65246; +__attribute__((used)) void* use65247 = (void*)&foo65247; +__attribute__((used)) void* use65248 = (void*)&foo65248; +__attribute__((used)) void* use65249 = (void*)&foo65249; +__attribute__((used)) void* use65250 = (void*)&foo65250; +__attribute__((used)) void* use65251 = (void*)&foo65251; +__attribute__((used)) void* use65252 = (void*)&foo65252; +__attribute__((used)) void* use65253 = (void*)&foo65253; +__attribute__((used)) void* use65254 = (void*)&foo65254; +__attribute__((used)) void* use65255 = (void*)&foo65255; +__attribute__((used)) void* use65256 = (void*)&foo65256; +__attribute__((used)) void* use65257 = (void*)&foo65257; +__attribute__((used)) void* use65258 = (void*)&foo65258; +__attribute__((used)) void* use65259 = (void*)&foo65259; +__attribute__((used)) void* use65260 = (void*)&foo65260; +__attribute__((used)) void* use65261 = (void*)&foo65261; +__attribute__((used)) void* use65262 = (void*)&foo65262; +__attribute__((used)) void* use65263 = (void*)&foo65263; +__attribute__((used)) void* use65264 = (void*)&foo65264; +__attribute__((used)) void* use65265 = (void*)&foo65265; +__attribute__((used)) void* use65266 = (void*)&foo65266; +__attribute__((used)) void* use65267 = (void*)&foo65267; +__attribute__((used)) void* use65268 = (void*)&foo65268; +__attribute__((used)) void* use65269 = (void*)&foo65269; +__attribute__((used)) void* use65270 = (void*)&foo65270; +__attribute__((used)) void* use65271 = (void*)&foo65271; +__attribute__((used)) void* use65272 = (void*)&foo65272; +__attribute__((used)) void* use65273 = (void*)&foo65273; +__attribute__((used)) void* use65274 = (void*)&foo65274; +__attribute__((used)) void* use65275 = (void*)&foo65275; +__attribute__((used)) void* use65276 = (void*)&foo65276; +__attribute__((used)) void* use65277 = (void*)&foo65277; +__attribute__((used)) void* use65278 = (void*)&foo65278; +__attribute__((used)) void* use65279 = (void*)&foo65279; +__attribute__((used)) void* use65280 = (void*)&foo65280; +__attribute__((used)) void* use65281 = (void*)&foo65281; +__attribute__((used)) void* use65282 = (void*)&foo65282; +__attribute__((used)) void* use65283 = (void*)&foo65283; +__attribute__((used)) void* use65284 = (void*)&foo65284; +__attribute__((used)) void* use65285 = (void*)&foo65285; +__attribute__((used)) void* use65286 = (void*)&foo65286; +__attribute__((used)) void* use65287 = (void*)&foo65287; +__attribute__((used)) void* use65288 = (void*)&foo65288; +__attribute__((used)) void* use65289 = (void*)&foo65289; +__attribute__((used)) void* use65290 = (void*)&foo65290; +__attribute__((used)) void* use65291 = (void*)&foo65291; +__attribute__((used)) void* use65292 = (void*)&foo65292; +__attribute__((used)) void* use65293 = (void*)&foo65293; +__attribute__((used)) void* use65294 = (void*)&foo65294; +__attribute__((used)) void* use65295 = (void*)&foo65295; +__attribute__((used)) void* use65296 = (void*)&foo65296; +__attribute__((used)) void* use65297 = (void*)&foo65297; +__attribute__((used)) void* use65298 = (void*)&foo65298; +__attribute__((used)) void* use65299 = (void*)&foo65299; +__attribute__((used)) void* use65300 = (void*)&foo65300; +__attribute__((used)) void* use65301 = (void*)&foo65301; +__attribute__((used)) void* use65302 = (void*)&foo65302; +__attribute__((used)) void* use65303 = (void*)&foo65303; +__attribute__((used)) void* use65304 = (void*)&foo65304; +__attribute__((used)) void* use65305 = (void*)&foo65305; +__attribute__((used)) void* use65306 = (void*)&foo65306; +__attribute__((used)) void* use65307 = (void*)&foo65307; +__attribute__((used)) void* use65308 = (void*)&foo65308; +__attribute__((used)) void* use65309 = (void*)&foo65309; +__attribute__((used)) void* use65310 = (void*)&foo65310; +__attribute__((used)) void* use65311 = (void*)&foo65311; +__attribute__((used)) void* use65312 = (void*)&foo65312; +__attribute__((used)) void* use65313 = (void*)&foo65313; +__attribute__((used)) void* use65314 = (void*)&foo65314; +__attribute__((used)) void* use65315 = (void*)&foo65315; +__attribute__((used)) void* use65316 = (void*)&foo65316; +__attribute__((used)) void* use65317 = (void*)&foo65317; +__attribute__((used)) void* use65318 = (void*)&foo65318; +__attribute__((used)) void* use65319 = (void*)&foo65319; +__attribute__((used)) void* use65320 = (void*)&foo65320; +__attribute__((used)) void* use65321 = (void*)&foo65321; +__attribute__((used)) void* use65322 = (void*)&foo65322; +__attribute__((used)) void* use65323 = (void*)&foo65323; +__attribute__((used)) void* use65324 = (void*)&foo65324; +__attribute__((used)) void* use65325 = (void*)&foo65325; +__attribute__((used)) void* use65326 = (void*)&foo65326; +__attribute__((used)) void* use65327 = (void*)&foo65327; +__attribute__((used)) void* use65328 = (void*)&foo65328; +__attribute__((used)) void* use65329 = (void*)&foo65329; +__attribute__((used)) void* use65330 = (void*)&foo65330; +__attribute__((used)) void* use65331 = (void*)&foo65331; +__attribute__((used)) void* use65332 = (void*)&foo65332; +__attribute__((used)) void* use65333 = (void*)&foo65333; +__attribute__((used)) void* use65334 = (void*)&foo65334; +__attribute__((used)) void* use65335 = (void*)&foo65335; +__attribute__((used)) void* use65336 = (void*)&foo65336; +__attribute__((used)) void* use65337 = (void*)&foo65337; +__attribute__((used)) void* use65338 = (void*)&foo65338; +__attribute__((used)) void* use65339 = (void*)&foo65339; +__attribute__((used)) void* use65340 = (void*)&foo65340; +__attribute__((used)) void* use65341 = (void*)&foo65341; +__attribute__((used)) void* use65342 = (void*)&foo65342; +__attribute__((used)) void* use65343 = (void*)&foo65343; +__attribute__((used)) void* use65344 = (void*)&foo65344; +__attribute__((used)) void* use65345 = (void*)&foo65345; +__attribute__((used)) void* use65346 = (void*)&foo65346; +__attribute__((used)) void* use65347 = (void*)&foo65347; +__attribute__((used)) void* use65348 = (void*)&foo65348; +__attribute__((used)) void* use65349 = (void*)&foo65349; +__attribute__((used)) void* use65350 = (void*)&foo65350; +__attribute__((used)) void* use65351 = (void*)&foo65351; +__attribute__((used)) void* use65352 = (void*)&foo65352; +__attribute__((used)) void* use65353 = (void*)&foo65353; +__attribute__((used)) void* use65354 = (void*)&foo65354; +__attribute__((used)) void* use65355 = (void*)&foo65355; +__attribute__((used)) void* use65356 = (void*)&foo65356; +__attribute__((used)) void* use65357 = (void*)&foo65357; +__attribute__((used)) void* use65358 = (void*)&foo65358; +__attribute__((used)) void* use65359 = (void*)&foo65359; +__attribute__((used)) void* use65360 = (void*)&foo65360; +__attribute__((used)) void* use65361 = (void*)&foo65361; +__attribute__((used)) void* use65362 = (void*)&foo65362; +__attribute__((used)) void* use65363 = (void*)&foo65363; +__attribute__((used)) void* use65364 = (void*)&foo65364; +__attribute__((used)) void* use65365 = (void*)&foo65365; +__attribute__((used)) void* use65366 = (void*)&foo65366; +__attribute__((used)) void* use65367 = (void*)&foo65367; +__attribute__((used)) void* use65368 = (void*)&foo65368; +__attribute__((used)) void* use65369 = (void*)&foo65369; +__attribute__((used)) void* use65370 = (void*)&foo65370; +__attribute__((used)) void* use65371 = (void*)&foo65371; +__attribute__((used)) void* use65372 = (void*)&foo65372; +__attribute__((used)) void* use65373 = (void*)&foo65373; +__attribute__((used)) void* use65374 = (void*)&foo65374; +__attribute__((used)) void* use65375 = (void*)&foo65375; +__attribute__((used)) void* use65376 = (void*)&foo65376; +__attribute__((used)) void* use65377 = (void*)&foo65377; +__attribute__((used)) void* use65378 = (void*)&foo65378; +__attribute__((used)) void* use65379 = (void*)&foo65379; +__attribute__((used)) void* use65380 = (void*)&foo65380; +__attribute__((used)) void* use65381 = (void*)&foo65381; +__attribute__((used)) void* use65382 = (void*)&foo65382; +__attribute__((used)) void* use65383 = (void*)&foo65383; +__attribute__((used)) void* use65384 = (void*)&foo65384; +__attribute__((used)) void* use65385 = (void*)&foo65385; +__attribute__((used)) void* use65386 = (void*)&foo65386; +__attribute__((used)) void* use65387 = (void*)&foo65387; +__attribute__((used)) void* use65388 = (void*)&foo65388; +__attribute__((used)) void* use65389 = (void*)&foo65389; +__attribute__((used)) void* use65390 = (void*)&foo65390; +__attribute__((used)) void* use65391 = (void*)&foo65391; +__attribute__((used)) void* use65392 = (void*)&foo65392; +__attribute__((used)) void* use65393 = (void*)&foo65393; +__attribute__((used)) void* use65394 = (void*)&foo65394; +__attribute__((used)) void* use65395 = (void*)&foo65395; +__attribute__((used)) void* use65396 = (void*)&foo65396; +__attribute__((used)) void* use65397 = (void*)&foo65397; +__attribute__((used)) void* use65398 = (void*)&foo65398; +__attribute__((used)) void* use65399 = (void*)&foo65399; +__attribute__((used)) void* use65400 = (void*)&foo65400; +__attribute__((used)) void* use65401 = (void*)&foo65401; +__attribute__((used)) void* use65402 = (void*)&foo65402; +__attribute__((used)) void* use65403 = (void*)&foo65403; +__attribute__((used)) void* use65404 = (void*)&foo65404; +__attribute__((used)) void* use65405 = (void*)&foo65405; +__attribute__((used)) void* use65406 = (void*)&foo65406; +__attribute__((used)) void* use65407 = (void*)&foo65407; +__attribute__((used)) void* use65408 = (void*)&foo65408; +__attribute__((used)) void* use65409 = (void*)&foo65409; +__attribute__((used)) void* use65410 = (void*)&foo65410; +__attribute__((used)) void* use65411 = (void*)&foo65411; +__attribute__((used)) void* use65412 = (void*)&foo65412; +__attribute__((used)) void* use65413 = (void*)&foo65413; +__attribute__((used)) void* use65414 = (void*)&foo65414; +__attribute__((used)) void* use65415 = (void*)&foo65415; +__attribute__((used)) void* use65416 = (void*)&foo65416; +__attribute__((used)) void* use65417 = (void*)&foo65417; +__attribute__((used)) void* use65418 = (void*)&foo65418; +__attribute__((used)) void* use65419 = (void*)&foo65419; +__attribute__((used)) void* use65420 = (void*)&foo65420; +__attribute__((used)) void* use65421 = (void*)&foo65421; +__attribute__((used)) void* use65422 = (void*)&foo65422; +__attribute__((used)) void* use65423 = (void*)&foo65423; +__attribute__((used)) void* use65424 = (void*)&foo65424; +__attribute__((used)) void* use65425 = (void*)&foo65425; +__attribute__((used)) void* use65426 = (void*)&foo65426; +__attribute__((used)) void* use65427 = (void*)&foo65427; +__attribute__((used)) void* use65428 = (void*)&foo65428; +__attribute__((used)) void* use65429 = (void*)&foo65429; +__attribute__((used)) void* use65430 = (void*)&foo65430; +__attribute__((used)) void* use65431 = (void*)&foo65431; +__attribute__((used)) void* use65432 = (void*)&foo65432; +__attribute__((used)) void* use65433 = (void*)&foo65433; +__attribute__((used)) void* use65434 = (void*)&foo65434; +__attribute__((used)) void* use65435 = (void*)&foo65435; +__attribute__((used)) void* use65436 = (void*)&foo65436; +__attribute__((used)) void* use65437 = (void*)&foo65437; +__attribute__((used)) void* use65438 = (void*)&foo65438; +__attribute__((used)) void* use65439 = (void*)&foo65439; +__attribute__((used)) void* use65440 = (void*)&foo65440; +__attribute__((used)) void* use65441 = (void*)&foo65441; +__attribute__((used)) void* use65442 = (void*)&foo65442; +__attribute__((used)) void* use65443 = (void*)&foo65443; +__attribute__((used)) void* use65444 = (void*)&foo65444; +__attribute__((used)) void* use65445 = (void*)&foo65445; +__attribute__((used)) void* use65446 = (void*)&foo65446; +__attribute__((used)) void* use65447 = (void*)&foo65447; +__attribute__((used)) void* use65448 = (void*)&foo65448; +__attribute__((used)) void* use65449 = (void*)&foo65449; +__attribute__((used)) void* use65450 = (void*)&foo65450; +__attribute__((used)) void* use65451 = (void*)&foo65451; +__attribute__((used)) void* use65452 = (void*)&foo65452; +__attribute__((used)) void* use65453 = (void*)&foo65453; +__attribute__((used)) void* use65454 = (void*)&foo65454; +__attribute__((used)) void* use65455 = (void*)&foo65455; +__attribute__((used)) void* use65456 = (void*)&foo65456; +__attribute__((used)) void* use65457 = (void*)&foo65457; +__attribute__((used)) void* use65458 = (void*)&foo65458; +__attribute__((used)) void* use65459 = (void*)&foo65459; +__attribute__((used)) void* use65460 = (void*)&foo65460; +__attribute__((used)) void* use65461 = (void*)&foo65461; +__attribute__((used)) void* use65462 = (void*)&foo65462; +__attribute__((used)) void* use65463 = (void*)&foo65463; +__attribute__((used)) void* use65464 = (void*)&foo65464; +__attribute__((used)) void* use65465 = (void*)&foo65465; +__attribute__((used)) void* use65466 = (void*)&foo65466; +__attribute__((used)) void* use65467 = (void*)&foo65467; +__attribute__((used)) void* use65468 = (void*)&foo65468; +__attribute__((used)) void* use65469 = (void*)&foo65469; +__attribute__((used)) void* use65470 = (void*)&foo65470; +__attribute__((used)) void* use65471 = (void*)&foo65471; +__attribute__((used)) void* use65472 = (void*)&foo65472; +__attribute__((used)) void* use65473 = (void*)&foo65473; +__attribute__((used)) void* use65474 = (void*)&foo65474; +__attribute__((used)) void* use65475 = (void*)&foo65475; +__attribute__((used)) void* use65476 = (void*)&foo65476; +__attribute__((used)) void* use65477 = (void*)&foo65477; +__attribute__((used)) void* use65478 = (void*)&foo65478; +__attribute__((used)) void* use65479 = (void*)&foo65479; +__attribute__((used)) void* use65480 = (void*)&foo65480; +__attribute__((used)) void* use65481 = (void*)&foo65481; +__attribute__((used)) void* use65482 = (void*)&foo65482; +__attribute__((used)) void* use65483 = (void*)&foo65483; +__attribute__((used)) void* use65484 = (void*)&foo65484; +__attribute__((used)) void* use65485 = (void*)&foo65485; +__attribute__((used)) void* use65486 = (void*)&foo65486; +__attribute__((used)) void* use65487 = (void*)&foo65487; +__attribute__((used)) void* use65488 = (void*)&foo65488; +__attribute__((used)) void* use65489 = (void*)&foo65489; +__attribute__((used)) void* use65490 = (void*)&foo65490; +__attribute__((used)) void* use65491 = (void*)&foo65491; +__attribute__((used)) void* use65492 = (void*)&foo65492; +__attribute__((used)) void* use65493 = (void*)&foo65493; +__attribute__((used)) void* use65494 = (void*)&foo65494; +__attribute__((used)) void* use65495 = (void*)&foo65495; +__attribute__((used)) void* use65496 = (void*)&foo65496; +__attribute__((used)) void* use65497 = (void*)&foo65497; +__attribute__((used)) void* use65498 = (void*)&foo65498; +__attribute__((used)) void* use65499 = (void*)&foo65499; +__attribute__((used)) void* use65500 = (void*)&foo65500; +__attribute__((used)) void* use65501 = (void*)&foo65501; +__attribute__((used)) void* use65502 = (void*)&foo65502; +__attribute__((used)) void* use65503 = (void*)&foo65503; +__attribute__((used)) void* use65504 = (void*)&foo65504; +__attribute__((used)) void* use65505 = (void*)&foo65505; +__attribute__((used)) void* use65506 = (void*)&foo65506; +__attribute__((used)) void* use65507 = (void*)&foo65507; +__attribute__((used)) void* use65508 = (void*)&foo65508; +__attribute__((used)) void* use65509 = (void*)&foo65509; +__attribute__((used)) void* use65510 = (void*)&foo65510; +__attribute__((used)) void* use65511 = (void*)&foo65511; +__attribute__((used)) void* use65512 = (void*)&foo65512; +__attribute__((used)) void* use65513 = (void*)&foo65513; +__attribute__((used)) void* use65514 = (void*)&foo65514; +__attribute__((used)) void* use65515 = (void*)&foo65515; +__attribute__((used)) void* use65516 = (void*)&foo65516; +__attribute__((used)) void* use65517 = (void*)&foo65517; +__attribute__((used)) void* use65518 = (void*)&foo65518; +__attribute__((used)) void* use65519 = (void*)&foo65519; +__attribute__((used)) void* use65520 = (void*)&foo65520; +__attribute__((used)) void* use65521 = (void*)&foo65521; +__attribute__((used)) void* use65522 = (void*)&foo65522; +__attribute__((used)) void* use65523 = (void*)&foo65523; +__attribute__((used)) void* use65524 = (void*)&foo65524; +__attribute__((used)) void* use65525 = (void*)&foo65525; +__attribute__((used)) void* use65526 = (void*)&foo65526; +__attribute__((used)) void* use65527 = (void*)&foo65527; +__attribute__((used)) void* use65528 = (void*)&foo65528; +__attribute__((used)) void* use65529 = (void*)&foo65529; +__attribute__((used)) void* use65530 = (void*)&foo65530; +__attribute__((used)) void* use65531 = (void*)&foo65531; +__attribute__((used)) void* use65532 = (void*)&foo65532; +__attribute__((used)) void* use65533 = (void*)&foo65533; +__attribute__((used)) void* use65534 = (void*)&foo65534; +__attribute__((used)) void* use65535 = (void*)&foo65535; +__attribute__((used)) void* use65536 = (void*)&foo65536; +__attribute__((used)) void* use65537 = (void*)&foo65537; +__attribute__((used)) void* use65538 = (void*)&foo65538; +__attribute__((used)) void* use65539 = (void*)&foo65539; +__attribute__((used)) void* use65540 = (void*)&foo65540; +__attribute__((used)) void* use65541 = (void*)&foo65541; +__attribute__((used)) void* use65542 = (void*)&foo65542; +__attribute__((used)) void* use65543 = (void*)&foo65543; +__attribute__((used)) void* use65544 = (void*)&foo65544; +__attribute__((used)) void* use65545 = (void*)&foo65545; +__attribute__((used)) void* use65546 = (void*)&foo65546; +__attribute__((used)) void* use65547 = (void*)&foo65547; +__attribute__((used)) void* use65548 = (void*)&foo65548; +__attribute__((used)) void* use65549 = (void*)&foo65549; +__attribute__((used)) void* use65550 = (void*)&foo65550; +__attribute__((used)) void* use65551 = (void*)&foo65551; +__attribute__((used)) void* use65552 = (void*)&foo65552; +__attribute__((used)) void* use65553 = (void*)&foo65553; +__attribute__((used)) void* use65554 = (void*)&foo65554; +__attribute__((used)) void* use65555 = (void*)&foo65555; +__attribute__((used)) void* use65556 = (void*)&foo65556; +__attribute__((used)) void* use65557 = (void*)&foo65557; +__attribute__((used)) void* use65558 = (void*)&foo65558; +__attribute__((used)) void* use65559 = (void*)&foo65559; +__attribute__((used)) void* use65560 = (void*)&foo65560; +__attribute__((used)) void* use65561 = (void*)&foo65561; +__attribute__((used)) void* use65562 = (void*)&foo65562; +__attribute__((used)) void* use65563 = (void*)&foo65563; +__attribute__((used)) void* use65564 = (void*)&foo65564; +__attribute__((used)) void* use65565 = (void*)&foo65565; +__attribute__((used)) void* use65566 = (void*)&foo65566; +__attribute__((used)) void* use65567 = (void*)&foo65567; +__attribute__((used)) void* use65568 = (void*)&foo65568; +__attribute__((used)) void* use65569 = (void*)&foo65569; +__attribute__((used)) void* use65570 = (void*)&foo65570; +__attribute__((used)) void* use65571 = (void*)&foo65571; +__attribute__((used)) void* use65572 = (void*)&foo65572; +__attribute__((used)) void* use65573 = (void*)&foo65573; +__attribute__((used)) void* use65574 = (void*)&foo65574; +__attribute__((used)) void* use65575 = (void*)&foo65575; +__attribute__((used)) void* use65576 = (void*)&foo65576; +__attribute__((used)) void* use65577 = (void*)&foo65577; +__attribute__((used)) void* use65578 = (void*)&foo65578; +__attribute__((used)) void* use65579 = (void*)&foo65579; +__attribute__((used)) void* use65580 = (void*)&foo65580; +__attribute__((used)) void* use65581 = (void*)&foo65581; +__attribute__((used)) void* use65582 = (void*)&foo65582; +__attribute__((used)) void* use65583 = (void*)&foo65583; +__attribute__((used)) void* use65584 = (void*)&foo65584; +__attribute__((used)) void* use65585 = (void*)&foo65585; +__attribute__((used)) void* use65586 = (void*)&foo65586; +__attribute__((used)) void* use65587 = (void*)&foo65587; +__attribute__((used)) void* use65588 = (void*)&foo65588; +__attribute__((used)) void* use65589 = (void*)&foo65589; +__attribute__((used)) void* use65590 = (void*)&foo65590; +__attribute__((used)) void* use65591 = (void*)&foo65591; +__attribute__((used)) void* use65592 = (void*)&foo65592; +__attribute__((used)) void* use65593 = (void*)&foo65593; +__attribute__((used)) void* use65594 = (void*)&foo65594; +__attribute__((used)) void* use65595 = (void*)&foo65595; +__attribute__((used)) void* use65596 = (void*)&foo65596; +__attribute__((used)) void* use65597 = (void*)&foo65597; +__attribute__((used)) void* use65598 = (void*)&foo65598; +__attribute__((used)) void* use65599 = (void*)&foo65599; +__attribute__((used)) void* use65600 = (void*)&foo65600; +__attribute__((used)) void* use65601 = (void*)&foo65601; +__attribute__((used)) void* use65602 = (void*)&foo65602; +__attribute__((used)) void* use65603 = (void*)&foo65603; +__attribute__((used)) void* use65604 = (void*)&foo65604; +__attribute__((used)) void* use65605 = (void*)&foo65605; +__attribute__((used)) void* use65606 = (void*)&foo65606; +__attribute__((used)) void* use65607 = (void*)&foo65607; +__attribute__((used)) void* use65608 = (void*)&foo65608; +__attribute__((used)) void* use65609 = (void*)&foo65609; +__attribute__((used)) void* use65610 = (void*)&foo65610; +__attribute__((used)) void* use65611 = (void*)&foo65611; +__attribute__((used)) void* use65612 = (void*)&foo65612; +__attribute__((used)) void* use65613 = (void*)&foo65613; +__attribute__((used)) void* use65614 = (void*)&foo65614; +__attribute__((used)) void* use65615 = (void*)&foo65615; +__attribute__((used)) void* use65616 = (void*)&foo65616; +__attribute__((used)) void* use65617 = (void*)&foo65617; +__attribute__((used)) void* use65618 = (void*)&foo65618; +__attribute__((used)) void* use65619 = (void*)&foo65619; +__attribute__((used)) void* use65620 = (void*)&foo65620; +__attribute__((used)) void* use65621 = (void*)&foo65621; +__attribute__((used)) void* use65622 = (void*)&foo65622; +__attribute__((used)) void* use65623 = (void*)&foo65623; +__attribute__((used)) void* use65624 = (void*)&foo65624; +__attribute__((used)) void* use65625 = (void*)&foo65625; +__attribute__((used)) void* use65626 = (void*)&foo65626; +__attribute__((used)) void* use65627 = (void*)&foo65627; +__attribute__((used)) void* use65628 = (void*)&foo65628; +__attribute__((used)) void* use65629 = (void*)&foo65629; +__attribute__((used)) void* use65630 = (void*)&foo65630; +__attribute__((used)) void* use65631 = (void*)&foo65631; +__attribute__((used)) void* use65632 = (void*)&foo65632; +__attribute__((used)) void* use65633 = (void*)&foo65633; +__attribute__((used)) void* use65634 = (void*)&foo65634; +__attribute__((used)) void* use65635 = (void*)&foo65635; +__attribute__((used)) void* use65636 = (void*)&foo65636; +__attribute__((used)) void* use65637 = (void*)&foo65637; +__attribute__((used)) void* use65638 = (void*)&foo65638; +__attribute__((used)) void* use65639 = (void*)&foo65639; +__attribute__((used)) void* use65640 = (void*)&foo65640; +__attribute__((used)) void* use65641 = (void*)&foo65641; +__attribute__((used)) void* use65642 = (void*)&foo65642; +__attribute__((used)) void* use65643 = (void*)&foo65643; +__attribute__((used)) void* use65644 = (void*)&foo65644; +__attribute__((used)) void* use65645 = (void*)&foo65645; +__attribute__((used)) void* use65646 = (void*)&foo65646; +__attribute__((used)) void* use65647 = (void*)&foo65647; +__attribute__((used)) void* use65648 = (void*)&foo65648; +__attribute__((used)) void* use65649 = (void*)&foo65649; +__attribute__((used)) void* use65650 = (void*)&foo65650; +__attribute__((used)) void* use65651 = (void*)&foo65651; +__attribute__((used)) void* use65652 = (void*)&foo65652; +__attribute__((used)) void* use65653 = (void*)&foo65653; +__attribute__((used)) void* use65654 = (void*)&foo65654; +__attribute__((used)) void* use65655 = (void*)&foo65655; +__attribute__((used)) void* use65656 = (void*)&foo65656; +__attribute__((used)) void* use65657 = (void*)&foo65657; +__attribute__((used)) void* use65658 = (void*)&foo65658; +__attribute__((used)) void* use65659 = (void*)&foo65659; +__attribute__((used)) void* use65660 = (void*)&foo65660; +__attribute__((used)) void* use65661 = (void*)&foo65661; +__attribute__((used)) void* use65662 = (void*)&foo65662; +__attribute__((used)) void* use65663 = (void*)&foo65663; +__attribute__((used)) void* use65664 = (void*)&foo65664; +__attribute__((used)) void* use65665 = (void*)&foo65665; +__attribute__((used)) void* use65666 = (void*)&foo65666; +__attribute__((used)) void* use65667 = (void*)&foo65667; +__attribute__((used)) void* use65668 = (void*)&foo65668; +__attribute__((used)) void* use65669 = (void*)&foo65669; +__attribute__((used)) void* use65670 = (void*)&foo65670; +__attribute__((used)) void* use65671 = (void*)&foo65671; +__attribute__((used)) void* use65672 = (void*)&foo65672; +__attribute__((used)) void* use65673 = (void*)&foo65673; +__attribute__((used)) void* use65674 = (void*)&foo65674; +__attribute__((used)) void* use65675 = (void*)&foo65675; +__attribute__((used)) void* use65676 = (void*)&foo65676; +__attribute__((used)) void* use65677 = (void*)&foo65677; +__attribute__((used)) void* use65678 = (void*)&foo65678; +__attribute__((used)) void* use65679 = (void*)&foo65679; +__attribute__((used)) void* use65680 = (void*)&foo65680; +__attribute__((used)) void* use65681 = (void*)&foo65681; +__attribute__((used)) void* use65682 = (void*)&foo65682; +__attribute__((used)) void* use65683 = (void*)&foo65683; +__attribute__((used)) void* use65684 = (void*)&foo65684; +__attribute__((used)) void* use65685 = (void*)&foo65685; +__attribute__((used)) void* use65686 = (void*)&foo65686; +__attribute__((used)) void* use65687 = (void*)&foo65687; +__attribute__((used)) void* use65688 = (void*)&foo65688; +__attribute__((used)) void* use65689 = (void*)&foo65689; +__attribute__((used)) void* use65690 = (void*)&foo65690; +__attribute__((used)) void* use65691 = (void*)&foo65691; +__attribute__((used)) void* use65692 = (void*)&foo65692; +__attribute__((used)) void* use65693 = (void*)&foo65693; +__attribute__((used)) void* use65694 = (void*)&foo65694; +__attribute__((used)) void* use65695 = (void*)&foo65695; +__attribute__((used)) void* use65696 = (void*)&foo65696; +__attribute__((used)) void* use65697 = (void*)&foo65697; +__attribute__((used)) void* use65698 = (void*)&foo65698; +__attribute__((used)) void* use65699 = (void*)&foo65699; +__attribute__((used)) void* use65700 = (void*)&foo65700; +__attribute__((used)) void* use65701 = (void*)&foo65701; +__attribute__((used)) void* use65702 = (void*)&foo65702; +__attribute__((used)) void* use65703 = (void*)&foo65703; +__attribute__((used)) void* use65704 = (void*)&foo65704; +__attribute__((used)) void* use65705 = (void*)&foo65705; +__attribute__((used)) void* use65706 = (void*)&foo65706; +__attribute__((used)) void* use65707 = (void*)&foo65707; +__attribute__((used)) void* use65708 = (void*)&foo65708; +__attribute__((used)) void* use65709 = (void*)&foo65709; +__attribute__((used)) void* use65710 = (void*)&foo65710; +__attribute__((used)) void* use65711 = (void*)&foo65711; +__attribute__((used)) void* use65712 = (void*)&foo65712; +__attribute__((used)) void* use65713 = (void*)&foo65713; +__attribute__((used)) void* use65714 = (void*)&foo65714; +__attribute__((used)) void* use65715 = (void*)&foo65715; +__attribute__((used)) void* use65716 = (void*)&foo65716; +__attribute__((used)) void* use65717 = (void*)&foo65717; +__attribute__((used)) void* use65718 = (void*)&foo65718; +__attribute__((used)) void* use65719 = (void*)&foo65719; +__attribute__((used)) void* use65720 = (void*)&foo65720; +__attribute__((used)) void* use65721 = (void*)&foo65721; +__attribute__((used)) void* use65722 = (void*)&foo65722; +__attribute__((used)) void* use65723 = (void*)&foo65723; +__attribute__((used)) void* use65724 = (void*)&foo65724; +__attribute__((used)) void* use65725 = (void*)&foo65725; +__attribute__((used)) void* use65726 = (void*)&foo65726; +__attribute__((used)) void* use65727 = (void*)&foo65727; +__attribute__((used)) void* use65728 = (void*)&foo65728; +__attribute__((used)) void* use65729 = (void*)&foo65729; +__attribute__((used)) void* use65730 = (void*)&foo65730; +__attribute__((used)) void* use65731 = (void*)&foo65731; +__attribute__((used)) void* use65732 = (void*)&foo65732; +__attribute__((used)) void* use65733 = (void*)&foo65733; +__attribute__((used)) void* use65734 = (void*)&foo65734; +__attribute__((used)) void* use65735 = (void*)&foo65735; +__attribute__((used)) void* use65736 = (void*)&foo65736; +__attribute__((used)) void* use65737 = (void*)&foo65737; +__attribute__((used)) void* use65738 = (void*)&foo65738; +__attribute__((used)) void* use65739 = (void*)&foo65739; +__attribute__((used)) void* use65740 = (void*)&foo65740; +__attribute__((used)) void* use65741 = (void*)&foo65741; +__attribute__((used)) void* use65742 = (void*)&foo65742; +__attribute__((used)) void* use65743 = (void*)&foo65743; +__attribute__((used)) void* use65744 = (void*)&foo65744; +__attribute__((used)) void* use65745 = (void*)&foo65745; +__attribute__((used)) void* use65746 = (void*)&foo65746; +__attribute__((used)) void* use65747 = (void*)&foo65747; +__attribute__((used)) void* use65748 = (void*)&foo65748; +__attribute__((used)) void* use65749 = (void*)&foo65749; +__attribute__((used)) void* use65750 = (void*)&foo65750; +__attribute__((used)) void* use65751 = (void*)&foo65751; +__attribute__((used)) void* use65752 = (void*)&foo65752; +__attribute__((used)) void* use65753 = (void*)&foo65753; +__attribute__((used)) void* use65754 = (void*)&foo65754; +__attribute__((used)) void* use65755 = (void*)&foo65755; +__attribute__((used)) void* use65756 = (void*)&foo65756; +__attribute__((used)) void* use65757 = (void*)&foo65757; +__attribute__((used)) void* use65758 = (void*)&foo65758; +__attribute__((used)) void* use65759 = (void*)&foo65759; +__attribute__((used)) void* use65760 = (void*)&foo65760; +__attribute__((used)) void* use65761 = (void*)&foo65761; +__attribute__((used)) void* use65762 = (void*)&foo65762; +__attribute__((used)) void* use65763 = (void*)&foo65763; +__attribute__((used)) void* use65764 = (void*)&foo65764; +__attribute__((used)) void* use65765 = (void*)&foo65765; +__attribute__((used)) void* use65766 = (void*)&foo65766; +__attribute__((used)) void* use65767 = (void*)&foo65767; +__attribute__((used)) void* use65768 = (void*)&foo65768; +__attribute__((used)) void* use65769 = (void*)&foo65769; +__attribute__((used)) void* use65770 = (void*)&foo65770; +__attribute__((used)) void* use65771 = (void*)&foo65771; +__attribute__((used)) void* use65772 = (void*)&foo65772; +__attribute__((used)) void* use65773 = (void*)&foo65773; +__attribute__((used)) void* use65774 = (void*)&foo65774; +__attribute__((used)) void* use65775 = (void*)&foo65775; +__attribute__((used)) void* use65776 = (void*)&foo65776; +__attribute__((used)) void* use65777 = (void*)&foo65777; +__attribute__((used)) void* use65778 = (void*)&foo65778; +__attribute__((used)) void* use65779 = (void*)&foo65779; +__attribute__((used)) void* use65780 = (void*)&foo65780; +__attribute__((used)) void* use65781 = (void*)&foo65781; +__attribute__((used)) void* use65782 = (void*)&foo65782; +__attribute__((used)) void* use65783 = (void*)&foo65783; +__attribute__((used)) void* use65784 = (void*)&foo65784; +__attribute__((used)) void* use65785 = (void*)&foo65785; +__attribute__((used)) void* use65786 = (void*)&foo65786; +__attribute__((used)) void* use65787 = (void*)&foo65787; +__attribute__((used)) void* use65788 = (void*)&foo65788; +__attribute__((used)) void* use65789 = (void*)&foo65789; +__attribute__((used)) void* use65790 = (void*)&foo65790; +__attribute__((used)) void* use65791 = (void*)&foo65791; +__attribute__((used)) void* use65792 = (void*)&foo65792; +__attribute__((used)) void* use65793 = (void*)&foo65793; +__attribute__((used)) void* use65794 = (void*)&foo65794; +__attribute__((used)) void* use65795 = (void*)&foo65795; +__attribute__((used)) void* use65796 = (void*)&foo65796; +__attribute__((used)) void* use65797 = (void*)&foo65797; +__attribute__((used)) void* use65798 = (void*)&foo65798; +__attribute__((used)) void* use65799 = (void*)&foo65799; +__attribute__((used)) void* use65800 = (void*)&foo65800; +__attribute__((used)) void* use65801 = (void*)&foo65801; +__attribute__((used)) void* use65802 = (void*)&foo65802; +__attribute__((used)) void* use65803 = (void*)&foo65803; +__attribute__((used)) void* use65804 = (void*)&foo65804; +__attribute__((used)) void* use65805 = (void*)&foo65805; +__attribute__((used)) void* use65806 = (void*)&foo65806; +__attribute__((used)) void* use65807 = (void*)&foo65807; +__attribute__((used)) void* use65808 = (void*)&foo65808; +__attribute__((used)) void* use65809 = (void*)&foo65809; +__attribute__((used)) void* use65810 = (void*)&foo65810; +__attribute__((used)) void* use65811 = (void*)&foo65811; +__attribute__((used)) void* use65812 = (void*)&foo65812; +__attribute__((used)) void* use65813 = (void*)&foo65813; +__attribute__((used)) void* use65814 = (void*)&foo65814; +__attribute__((used)) void* use65815 = (void*)&foo65815; +__attribute__((used)) void* use65816 = (void*)&foo65816; +__attribute__((used)) void* use65817 = (void*)&foo65817; +__attribute__((used)) void* use65818 = (void*)&foo65818; +__attribute__((used)) void* use65819 = (void*)&foo65819; +__attribute__((used)) void* use65820 = (void*)&foo65820; +__attribute__((used)) void* use65821 = (void*)&foo65821; +__attribute__((used)) void* use65822 = (void*)&foo65822; +__attribute__((used)) void* use65823 = (void*)&foo65823; +__attribute__((used)) void* use65824 = (void*)&foo65824; +__attribute__((used)) void* use65825 = (void*)&foo65825; +__attribute__((used)) void* use65826 = (void*)&foo65826; +__attribute__((used)) void* use65827 = (void*)&foo65827; +__attribute__((used)) void* use65828 = (void*)&foo65828; +__attribute__((used)) void* use65829 = (void*)&foo65829; +__attribute__((used)) void* use65830 = (void*)&foo65830; +__attribute__((used)) void* use65831 = (void*)&foo65831; +__attribute__((used)) void* use65832 = (void*)&foo65832; +__attribute__((used)) void* use65833 = (void*)&foo65833; +__attribute__((used)) void* use65834 = (void*)&foo65834; +__attribute__((used)) void* use65835 = (void*)&foo65835; +__attribute__((used)) void* use65836 = (void*)&foo65836; +__attribute__((used)) void* use65837 = (void*)&foo65837; +__attribute__((used)) void* use65838 = (void*)&foo65838; +__attribute__((used)) void* use65839 = (void*)&foo65839; +__attribute__((used)) void* use65840 = (void*)&foo65840; +__attribute__((used)) void* use65841 = (void*)&foo65841; +__attribute__((used)) void* use65842 = (void*)&foo65842; +__attribute__((used)) void* use65843 = (void*)&foo65843; +__attribute__((used)) void* use65844 = (void*)&foo65844; +__attribute__((used)) void* use65845 = (void*)&foo65845; +__attribute__((used)) void* use65846 = (void*)&foo65846; +__attribute__((used)) void* use65847 = (void*)&foo65847; +__attribute__((used)) void* use65848 = (void*)&foo65848; +__attribute__((used)) void* use65849 = (void*)&foo65849; +__attribute__((used)) void* use65850 = (void*)&foo65850; +__attribute__((used)) void* use65851 = (void*)&foo65851; +__attribute__((used)) void* use65852 = (void*)&foo65852; +__attribute__((used)) void* use65853 = (void*)&foo65853; +__attribute__((used)) void* use65854 = (void*)&foo65854; +__attribute__((used)) void* use65855 = (void*)&foo65855; +__attribute__((used)) void* use65856 = (void*)&foo65856; +__attribute__((used)) void* use65857 = (void*)&foo65857; +__attribute__((used)) void* use65858 = (void*)&foo65858; +__attribute__((used)) void* use65859 = (void*)&foo65859; +__attribute__((used)) void* use65860 = (void*)&foo65860; +__attribute__((used)) void* use65861 = (void*)&foo65861; +__attribute__((used)) void* use65862 = (void*)&foo65862; +__attribute__((used)) void* use65863 = (void*)&foo65863; +__attribute__((used)) void* use65864 = (void*)&foo65864; +__attribute__((used)) void* use65865 = (void*)&foo65865; +__attribute__((used)) void* use65866 = (void*)&foo65866; +__attribute__((used)) void* use65867 = (void*)&foo65867; +__attribute__((used)) void* use65868 = (void*)&foo65868; +__attribute__((used)) void* use65869 = (void*)&foo65869; +__attribute__((used)) void* use65870 = (void*)&foo65870; +__attribute__((used)) void* use65871 = (void*)&foo65871; +__attribute__((used)) void* use65872 = (void*)&foo65872; +__attribute__((used)) void* use65873 = (void*)&foo65873; +__attribute__((used)) void* use65874 = (void*)&foo65874; +__attribute__((used)) void* use65875 = (void*)&foo65875; +__attribute__((used)) void* use65876 = (void*)&foo65876; +__attribute__((used)) void* use65877 = (void*)&foo65877; +__attribute__((used)) void* use65878 = (void*)&foo65878; +__attribute__((used)) void* use65879 = (void*)&foo65879; +__attribute__((used)) void* use65880 = (void*)&foo65880; +__attribute__((used)) void* use65881 = (void*)&foo65881; +__attribute__((used)) void* use65882 = (void*)&foo65882; +__attribute__((used)) void* use65883 = (void*)&foo65883; +__attribute__((used)) void* use65884 = (void*)&foo65884; +__attribute__((used)) void* use65885 = (void*)&foo65885; +__attribute__((used)) void* use65886 = (void*)&foo65886; +__attribute__((used)) void* use65887 = (void*)&foo65887; +__attribute__((used)) void* use65888 = (void*)&foo65888; +__attribute__((used)) void* use65889 = (void*)&foo65889; +__attribute__((used)) void* use65890 = (void*)&foo65890; +__attribute__((used)) void* use65891 = (void*)&foo65891; +__attribute__((used)) void* use65892 = (void*)&foo65892; +__attribute__((used)) void* use65893 = (void*)&foo65893; +__attribute__((used)) void* use65894 = (void*)&foo65894; +__attribute__((used)) void* use65895 = (void*)&foo65895; +__attribute__((used)) void* use65896 = (void*)&foo65896; +__attribute__((used)) void* use65897 = (void*)&foo65897; +__attribute__((used)) void* use65898 = (void*)&foo65898; +__attribute__((used)) void* use65899 = (void*)&foo65899; +__attribute__((used)) void* use65900 = (void*)&foo65900; +__attribute__((used)) void* use65901 = (void*)&foo65901; +__attribute__((used)) void* use65902 = (void*)&foo65902; +__attribute__((used)) void* use65903 = (void*)&foo65903; +__attribute__((used)) void* use65904 = (void*)&foo65904; +__attribute__((used)) void* use65905 = (void*)&foo65905; +__attribute__((used)) void* use65906 = (void*)&foo65906; +__attribute__((used)) void* use65907 = (void*)&foo65907; +__attribute__((used)) void* use65908 = (void*)&foo65908; +__attribute__((used)) void* use65909 = (void*)&foo65909; +__attribute__((used)) void* use65910 = (void*)&foo65910; +__attribute__((used)) void* use65911 = (void*)&foo65911; +__attribute__((used)) void* use65912 = (void*)&foo65912; +__attribute__((used)) void* use65913 = (void*)&foo65913; +__attribute__((used)) void* use65914 = (void*)&foo65914; +__attribute__((used)) void* use65915 = (void*)&foo65915; +__attribute__((used)) void* use65916 = (void*)&foo65916; +__attribute__((used)) void* use65917 = (void*)&foo65917; +__attribute__((used)) void* use65918 = (void*)&foo65918; +__attribute__((used)) void* use65919 = (void*)&foo65919; +__attribute__((used)) void* use65920 = (void*)&foo65920; +__attribute__((used)) void* use65921 = (void*)&foo65921; +__attribute__((used)) void* use65922 = (void*)&foo65922; +__attribute__((used)) void* use65923 = (void*)&foo65923; +__attribute__((used)) void* use65924 = (void*)&foo65924; +__attribute__((used)) void* use65925 = (void*)&foo65925; +__attribute__((used)) void* use65926 = (void*)&foo65926; +__attribute__((used)) void* use65927 = (void*)&foo65927; +__attribute__((used)) void* use65928 = (void*)&foo65928; +__attribute__((used)) void* use65929 = (void*)&foo65929; +__attribute__((used)) void* use65930 = (void*)&foo65930; +__attribute__((used)) void* use65931 = (void*)&foo65931; +__attribute__((used)) void* use65932 = (void*)&foo65932; +__attribute__((used)) void* use65933 = (void*)&foo65933; +__attribute__((used)) void* use65934 = (void*)&foo65934; +__attribute__((used)) void* use65935 = (void*)&foo65935; +__attribute__((used)) void* use65936 = (void*)&foo65936; +__attribute__((used)) void* use65937 = (void*)&foo65937; +__attribute__((used)) void* use65938 = (void*)&foo65938; +__attribute__((used)) void* use65939 = (void*)&foo65939; +__attribute__((used)) void* use65940 = (void*)&foo65940; +__attribute__((used)) void* use65941 = (void*)&foo65941; +__attribute__((used)) void* use65942 = (void*)&foo65942; +__attribute__((used)) void* use65943 = (void*)&foo65943; +__attribute__((used)) void* use65944 = (void*)&foo65944; +__attribute__((used)) void* use65945 = (void*)&foo65945; +__attribute__((used)) void* use65946 = (void*)&foo65946; +__attribute__((used)) void* use65947 = (void*)&foo65947; +__attribute__((used)) void* use65948 = (void*)&foo65948; +__attribute__((used)) void* use65949 = (void*)&foo65949; +__attribute__((used)) void* use65950 = (void*)&foo65950; +__attribute__((used)) void* use65951 = (void*)&foo65951; +__attribute__((used)) void* use65952 = (void*)&foo65952; +__attribute__((used)) void* use65953 = (void*)&foo65953; +__attribute__((used)) void* use65954 = (void*)&foo65954; +__attribute__((used)) void* use65955 = (void*)&foo65955; +__attribute__((used)) void* use65956 = (void*)&foo65956; +__attribute__((used)) void* use65957 = (void*)&foo65957; +__attribute__((used)) void* use65958 = (void*)&foo65958; +__attribute__((used)) void* use65959 = (void*)&foo65959; +__attribute__((used)) void* use65960 = (void*)&foo65960; +__attribute__((used)) void* use65961 = (void*)&foo65961; +__attribute__((used)) void* use65962 = (void*)&foo65962; +__attribute__((used)) void* use65963 = (void*)&foo65963; +__attribute__((used)) void* use65964 = (void*)&foo65964; +__attribute__((used)) void* use65965 = (void*)&foo65965; +__attribute__((used)) void* use65966 = (void*)&foo65966; +__attribute__((used)) void* use65967 = (void*)&foo65967; +__attribute__((used)) void* use65968 = (void*)&foo65968; +__attribute__((used)) void* use65969 = (void*)&foo65969; +__attribute__((used)) void* use65970 = (void*)&foo65970; +__attribute__((used)) void* use65971 = (void*)&foo65971; +__attribute__((used)) void* use65972 = (void*)&foo65972; +__attribute__((used)) void* use65973 = (void*)&foo65973; +__attribute__((used)) void* use65974 = (void*)&foo65974; +__attribute__((used)) void* use65975 = (void*)&foo65975; +__attribute__((used)) void* use65976 = (void*)&foo65976; +__attribute__((used)) void* use65977 = (void*)&foo65977; +__attribute__((used)) void* use65978 = (void*)&foo65978; +__attribute__((used)) void* use65979 = (void*)&foo65979; +__attribute__((used)) void* use65980 = (void*)&foo65980; +__attribute__((used)) void* use65981 = (void*)&foo65981; +__attribute__((used)) void* use65982 = (void*)&foo65982; +__attribute__((used)) void* use65983 = (void*)&foo65983; +__attribute__((used)) void* use65984 = (void*)&foo65984; +__attribute__((used)) void* use65985 = (void*)&foo65985; +__attribute__((used)) void* use65986 = (void*)&foo65986; +__attribute__((used)) void* use65987 = (void*)&foo65987; +__attribute__((used)) void* use65988 = (void*)&foo65988; +__attribute__((used)) void* use65989 = (void*)&foo65989; +__attribute__((used)) void* use65990 = (void*)&foo65990; +__attribute__((used)) void* use65991 = (void*)&foo65991; +__attribute__((used)) void* use65992 = (void*)&foo65992; +__attribute__((used)) void* use65993 = (void*)&foo65993; +__attribute__((used)) void* use65994 = (void*)&foo65994; +__attribute__((used)) void* use65995 = (void*)&foo65995; +__attribute__((used)) void* use65996 = (void*)&foo65996; +__attribute__((used)) void* use65997 = (void*)&foo65997; +__attribute__((used)) void* use65998 = (void*)&foo65998; +__attribute__((used)) void* use65999 = (void*)&foo65999; +__attribute__((used)) void* use66000 = (void*)&foo66000; +__attribute__((used)) void* use66001 = (void*)&foo66001; +__attribute__((used)) void* use66002 = (void*)&foo66002; +__attribute__((used)) void* use66003 = (void*)&foo66003; +__attribute__((used)) void* use66004 = (void*)&foo66004; +__attribute__((used)) void* use66005 = (void*)&foo66005; +__attribute__((used)) void* use66006 = (void*)&foo66006; +__attribute__((used)) void* use66007 = (void*)&foo66007; +__attribute__((used)) void* use66008 = (void*)&foo66008; +__attribute__((used)) void* use66009 = (void*)&foo66009; +__attribute__((used)) void* use66010 = (void*)&foo66010; +__attribute__((used)) void* use66011 = (void*)&foo66011; +__attribute__((used)) void* use66012 = (void*)&foo66012; +__attribute__((used)) void* use66013 = (void*)&foo66013; +__attribute__((used)) void* use66014 = (void*)&foo66014; +__attribute__((used)) void* use66015 = (void*)&foo66015; +__attribute__((used)) void* use66016 = (void*)&foo66016; +__attribute__((used)) void* use66017 = (void*)&foo66017; +__attribute__((used)) void* use66018 = (void*)&foo66018; +__attribute__((used)) void* use66019 = (void*)&foo66019; +__attribute__((used)) void* use66020 = (void*)&foo66020; +__attribute__((used)) void* use66021 = (void*)&foo66021; +__attribute__((used)) void* use66022 = (void*)&foo66022; +__attribute__((used)) void* use66023 = (void*)&foo66023; +__attribute__((used)) void* use66024 = (void*)&foo66024; +__attribute__((used)) void* use66025 = (void*)&foo66025; +__attribute__((used)) void* use66026 = (void*)&foo66026; +__attribute__((used)) void* use66027 = (void*)&foo66027; +__attribute__((used)) void* use66028 = (void*)&foo66028; +__attribute__((used)) void* use66029 = (void*)&foo66029; +__attribute__((used)) void* use66030 = (void*)&foo66030; +__attribute__((used)) void* use66031 = (void*)&foo66031; +__attribute__((used)) void* use66032 = (void*)&foo66032; +__attribute__((used)) void* use66033 = (void*)&foo66033; +__attribute__((used)) void* use66034 = (void*)&foo66034; +__attribute__((used)) void* use66035 = (void*)&foo66035; +__attribute__((used)) void* use66036 = (void*)&foo66036; +__attribute__((used)) void* use66037 = (void*)&foo66037; +__attribute__((used)) void* use66038 = (void*)&foo66038; +__attribute__((used)) void* use66039 = (void*)&foo66039; +__attribute__((used)) void* use66040 = (void*)&foo66040; +__attribute__((used)) void* use66041 = (void*)&foo66041; +__attribute__((used)) void* use66042 = (void*)&foo66042; +__attribute__((used)) void* use66043 = (void*)&foo66043; +__attribute__((used)) void* use66044 = (void*)&foo66044; +__attribute__((used)) void* use66045 = (void*)&foo66045; +__attribute__((used)) void* use66046 = (void*)&foo66046; +__attribute__((used)) void* use66047 = (void*)&foo66047; +__attribute__((used)) void* use66048 = (void*)&foo66048; +__attribute__((used)) void* use66049 = (void*)&foo66049; +__attribute__((used)) void* use66050 = (void*)&foo66050; +__attribute__((used)) void* use66051 = (void*)&foo66051; +__attribute__((used)) void* use66052 = (void*)&foo66052; +__attribute__((used)) void* use66053 = (void*)&foo66053; +__attribute__((used)) void* use66054 = (void*)&foo66054; +__attribute__((used)) void* use66055 = (void*)&foo66055; +__attribute__((used)) void* use66056 = (void*)&foo66056; +__attribute__((used)) void* use66057 = (void*)&foo66057; +__attribute__((used)) void* use66058 = (void*)&foo66058; +__attribute__((used)) void* use66059 = (void*)&foo66059; +__attribute__((used)) void* use66060 = (void*)&foo66060; +__attribute__((used)) void* use66061 = (void*)&foo66061; +__attribute__((used)) void* use66062 = (void*)&foo66062; +__attribute__((used)) void* use66063 = (void*)&foo66063; +__attribute__((used)) void* use66064 = (void*)&foo66064; +__attribute__((used)) void* use66065 = (void*)&foo66065; +__attribute__((used)) void* use66066 = (void*)&foo66066; +__attribute__((used)) void* use66067 = (void*)&foo66067; +__attribute__((used)) void* use66068 = (void*)&foo66068; +__attribute__((used)) void* use66069 = (void*)&foo66069; +__attribute__((used)) void* use66070 = (void*)&foo66070; +__attribute__((used)) void* use66071 = (void*)&foo66071; +__attribute__((used)) void* use66072 = (void*)&foo66072; +__attribute__((used)) void* use66073 = (void*)&foo66073; +__attribute__((used)) void* use66074 = (void*)&foo66074; +__attribute__((used)) void* use66075 = (void*)&foo66075; +__attribute__((used)) void* use66076 = (void*)&foo66076; +__attribute__((used)) void* use66077 = (void*)&foo66077; +__attribute__((used)) void* use66078 = (void*)&foo66078; +__attribute__((used)) void* use66079 = (void*)&foo66079; +__attribute__((used)) void* use66080 = (void*)&foo66080; +__attribute__((used)) void* use66081 = (void*)&foo66081; +__attribute__((used)) void* use66082 = (void*)&foo66082; +__attribute__((used)) void* use66083 = (void*)&foo66083; +__attribute__((used)) void* use66084 = (void*)&foo66084; +__attribute__((used)) void* use66085 = (void*)&foo66085; +__attribute__((used)) void* use66086 = (void*)&foo66086; +__attribute__((used)) void* use66087 = (void*)&foo66087; +__attribute__((used)) void* use66088 = (void*)&foo66088; +__attribute__((used)) void* use66089 = (void*)&foo66089; +__attribute__((used)) void* use66090 = (void*)&foo66090; +__attribute__((used)) void* use66091 = (void*)&foo66091; +__attribute__((used)) void* use66092 = (void*)&foo66092; +__attribute__((used)) void* use66093 = (void*)&foo66093; +__attribute__((used)) void* use66094 = (void*)&foo66094; +__attribute__((used)) void* use66095 = (void*)&foo66095; +__attribute__((used)) void* use66096 = (void*)&foo66096; +__attribute__((used)) void* use66097 = (void*)&foo66097; +__attribute__((used)) void* use66098 = (void*)&foo66098; +__attribute__((used)) void* use66099 = (void*)&foo66099; +__attribute__((used)) void* use66100 = (void*)&foo66100; +__attribute__((used)) void* use66101 = (void*)&foo66101; +__attribute__((used)) void* use66102 = (void*)&foo66102; +__attribute__((used)) void* use66103 = (void*)&foo66103; +__attribute__((used)) void* use66104 = (void*)&foo66104; +__attribute__((used)) void* use66105 = (void*)&foo66105; +__attribute__((used)) void* use66106 = (void*)&foo66106; +__attribute__((used)) void* use66107 = (void*)&foo66107; +__attribute__((used)) void* use66108 = (void*)&foo66108; +__attribute__((used)) void* use66109 = (void*)&foo66109; +__attribute__((used)) void* use66110 = (void*)&foo66110; +__attribute__((used)) void* use66111 = (void*)&foo66111; +__attribute__((used)) void* use66112 = (void*)&foo66112; +__attribute__((used)) void* use66113 = (void*)&foo66113; +__attribute__((used)) void* use66114 = (void*)&foo66114; +__attribute__((used)) void* use66115 = (void*)&foo66115; +__attribute__((used)) void* use66116 = (void*)&foo66116; +__attribute__((used)) void* use66117 = (void*)&foo66117; +__attribute__((used)) void* use66118 = (void*)&foo66118; +__attribute__((used)) void* use66119 = (void*)&foo66119; +__attribute__((used)) void* use66120 = (void*)&foo66120; +__attribute__((used)) void* use66121 = (void*)&foo66121; +__attribute__((used)) void* use66122 = (void*)&foo66122; +__attribute__((used)) void* use66123 = (void*)&foo66123; +__attribute__((used)) void* use66124 = (void*)&foo66124; +__attribute__((used)) void* use66125 = (void*)&foo66125; +__attribute__((used)) void* use66126 = (void*)&foo66126; +__attribute__((used)) void* use66127 = (void*)&foo66127; +__attribute__((used)) void* use66128 = (void*)&foo66128; +__attribute__((used)) void* use66129 = (void*)&foo66129; +__attribute__((used)) void* use66130 = (void*)&foo66130; +__attribute__((used)) void* use66131 = (void*)&foo66131; +__attribute__((used)) void* use66132 = (void*)&foo66132; +__attribute__((used)) void* use66133 = (void*)&foo66133; +__attribute__((used)) void* use66134 = (void*)&foo66134; +__attribute__((used)) void* use66135 = (void*)&foo66135; +__attribute__((used)) void* use66136 = (void*)&foo66136; +__attribute__((used)) void* use66137 = (void*)&foo66137; +__attribute__((used)) void* use66138 = (void*)&foo66138; +__attribute__((used)) void* use66139 = (void*)&foo66139; +__attribute__((used)) void* use66140 = (void*)&foo66140; +__attribute__((used)) void* use66141 = (void*)&foo66141; +__attribute__((used)) void* use66142 = (void*)&foo66142; +__attribute__((used)) void* use66143 = (void*)&foo66143; +__attribute__((used)) void* use66144 = (void*)&foo66144; +__attribute__((used)) void* use66145 = (void*)&foo66145; +__attribute__((used)) void* use66146 = (void*)&foo66146; +__attribute__((used)) void* use66147 = (void*)&foo66147; +__attribute__((used)) void* use66148 = (void*)&foo66148; +__attribute__((used)) void* use66149 = (void*)&foo66149; +__attribute__((used)) void* use66150 = (void*)&foo66150; +__attribute__((used)) void* use66151 = (void*)&foo66151; +__attribute__((used)) void* use66152 = (void*)&foo66152; +__attribute__((used)) void* use66153 = (void*)&foo66153; +__attribute__((used)) void* use66154 = (void*)&foo66154; +__attribute__((used)) void* use66155 = (void*)&foo66155; +__attribute__((used)) void* use66156 = (void*)&foo66156; +__attribute__((used)) void* use66157 = (void*)&foo66157; +__attribute__((used)) void* use66158 = (void*)&foo66158; +__attribute__((used)) void* use66159 = (void*)&foo66159; +__attribute__((used)) void* use66160 = (void*)&foo66160; +__attribute__((used)) void* use66161 = (void*)&foo66161; +__attribute__((used)) void* use66162 = (void*)&foo66162; +__attribute__((used)) void* use66163 = (void*)&foo66163; +__attribute__((used)) void* use66164 = (void*)&foo66164; +__attribute__((used)) void* use66165 = (void*)&foo66165; +__attribute__((used)) void* use66166 = (void*)&foo66166; +__attribute__((used)) void* use66167 = (void*)&foo66167; +__attribute__((used)) void* use66168 = (void*)&foo66168; +__attribute__((used)) void* use66169 = (void*)&foo66169; +__attribute__((used)) void* use66170 = (void*)&foo66170; +__attribute__((used)) void* use66171 = (void*)&foo66171; +__attribute__((used)) void* use66172 = (void*)&foo66172; +__attribute__((used)) void* use66173 = (void*)&foo66173; +__attribute__((used)) void* use66174 = (void*)&foo66174; +__attribute__((used)) void* use66175 = (void*)&foo66175; +__attribute__((used)) void* use66176 = (void*)&foo66176; +__attribute__((used)) void* use66177 = (void*)&foo66177; +__attribute__((used)) void* use66178 = (void*)&foo66178; +__attribute__((used)) void* use66179 = (void*)&foo66179; +__attribute__((used)) void* use66180 = (void*)&foo66180; +__attribute__((used)) void* use66181 = (void*)&foo66181; +__attribute__((used)) void* use66182 = (void*)&foo66182; +__attribute__((used)) void* use66183 = (void*)&foo66183; +__attribute__((used)) void* use66184 = (void*)&foo66184; +__attribute__((used)) void* use66185 = (void*)&foo66185; +__attribute__((used)) void* use66186 = (void*)&foo66186; +__attribute__((used)) void* use66187 = (void*)&foo66187; +__attribute__((used)) void* use66188 = (void*)&foo66188; +__attribute__((used)) void* use66189 = (void*)&foo66189; +__attribute__((used)) void* use66190 = (void*)&foo66190; +__attribute__((used)) void* use66191 = (void*)&foo66191; +__attribute__((used)) void* use66192 = (void*)&foo66192; +__attribute__((used)) void* use66193 = (void*)&foo66193; +__attribute__((used)) void* use66194 = (void*)&foo66194; +__attribute__((used)) void* use66195 = (void*)&foo66195; +__attribute__((used)) void* use66196 = (void*)&foo66196; +__attribute__((used)) void* use66197 = (void*)&foo66197; +__attribute__((used)) void* use66198 = (void*)&foo66198; +__attribute__((used)) void* use66199 = (void*)&foo66199; +__attribute__((used)) void* use66200 = (void*)&foo66200; +__attribute__((used)) void* use66201 = (void*)&foo66201; +__attribute__((used)) void* use66202 = (void*)&foo66202; +__attribute__((used)) void* use66203 = (void*)&foo66203; +__attribute__((used)) void* use66204 = (void*)&foo66204; +__attribute__((used)) void* use66205 = (void*)&foo66205; +__attribute__((used)) void* use66206 = (void*)&foo66206; +__attribute__((used)) void* use66207 = (void*)&foo66207; +__attribute__((used)) void* use66208 = (void*)&foo66208; +__attribute__((used)) void* use66209 = (void*)&foo66209; +__attribute__((used)) void* use66210 = (void*)&foo66210; +__attribute__((used)) void* use66211 = (void*)&foo66211; +__attribute__((used)) void* use66212 = (void*)&foo66212; +__attribute__((used)) void* use66213 = (void*)&foo66213; +__attribute__((used)) void* use66214 = (void*)&foo66214; +__attribute__((used)) void* use66215 = (void*)&foo66215; +__attribute__((used)) void* use66216 = (void*)&foo66216; +__attribute__((used)) void* use66217 = (void*)&foo66217; +__attribute__((used)) void* use66218 = (void*)&foo66218; +__attribute__((used)) void* use66219 = (void*)&foo66219; +__attribute__((used)) void* use66220 = (void*)&foo66220; +__attribute__((used)) void* use66221 = (void*)&foo66221; +__attribute__((used)) void* use66222 = (void*)&foo66222; +__attribute__((used)) void* use66223 = (void*)&foo66223; +__attribute__((used)) void* use66224 = (void*)&foo66224; +__attribute__((used)) void* use66225 = (void*)&foo66225; +__attribute__((used)) void* use66226 = (void*)&foo66226; +__attribute__((used)) void* use66227 = (void*)&foo66227; +__attribute__((used)) void* use66228 = (void*)&foo66228; +__attribute__((used)) void* use66229 = (void*)&foo66229; +__attribute__((used)) void* use66230 = (void*)&foo66230; +__attribute__((used)) void* use66231 = (void*)&foo66231; +__attribute__((used)) void* use66232 = (void*)&foo66232; +__attribute__((used)) void* use66233 = (void*)&foo66233; +__attribute__((used)) void* use66234 = (void*)&foo66234; +__attribute__((used)) void* use66235 = (void*)&foo66235; +__attribute__((used)) void* use66236 = (void*)&foo66236; +__attribute__((used)) void* use66237 = (void*)&foo66237; +__attribute__((used)) void* use66238 = (void*)&foo66238; +__attribute__((used)) void* use66239 = (void*)&foo66239; +__attribute__((used)) void* use66240 = (void*)&foo66240; +__attribute__((used)) void* use66241 = (void*)&foo66241; +__attribute__((used)) void* use66242 = (void*)&foo66242; +__attribute__((used)) void* use66243 = (void*)&foo66243; +__attribute__((used)) void* use66244 = (void*)&foo66244; +__attribute__((used)) void* use66245 = (void*)&foo66245; +__attribute__((used)) void* use66246 = (void*)&foo66246; +__attribute__((used)) void* use66247 = (void*)&foo66247; +__attribute__((used)) void* use66248 = (void*)&foo66248; +__attribute__((used)) void* use66249 = (void*)&foo66249; +__attribute__((used)) void* use66250 = (void*)&foo66250; +__attribute__((used)) void* use66251 = (void*)&foo66251; +__attribute__((used)) void* use66252 = (void*)&foo66252; +__attribute__((used)) void* use66253 = (void*)&foo66253; +__attribute__((used)) void* use66254 = (void*)&foo66254; +__attribute__((used)) void* use66255 = (void*)&foo66255; +__attribute__((used)) void* use66256 = (void*)&foo66256; +__attribute__((used)) void* use66257 = (void*)&foo66257; +__attribute__((used)) void* use66258 = (void*)&foo66258; +__attribute__((used)) void* use66259 = (void*)&foo66259; +__attribute__((used)) void* use66260 = (void*)&foo66260; +__attribute__((used)) void* use66261 = (void*)&foo66261; +__attribute__((used)) void* use66262 = (void*)&foo66262; +__attribute__((used)) void* use66263 = (void*)&foo66263; +__attribute__((used)) void* use66264 = (void*)&foo66264; +__attribute__((used)) void* use66265 = (void*)&foo66265; +__attribute__((used)) void* use66266 = (void*)&foo66266; +__attribute__((used)) void* use66267 = (void*)&foo66267; +__attribute__((used)) void* use66268 = (void*)&foo66268; +__attribute__((used)) void* use66269 = (void*)&foo66269; +__attribute__((used)) void* use66270 = (void*)&foo66270; +__attribute__((used)) void* use66271 = (void*)&foo66271; +__attribute__((used)) void* use66272 = (void*)&foo66272; +__attribute__((used)) void* use66273 = (void*)&foo66273; +__attribute__((used)) void* use66274 = (void*)&foo66274; +__attribute__((used)) void* use66275 = (void*)&foo66275; +__attribute__((used)) void* use66276 = (void*)&foo66276; +__attribute__((used)) void* use66277 = (void*)&foo66277; +__attribute__((used)) void* use66278 = (void*)&foo66278; +__attribute__((used)) void* use66279 = (void*)&foo66279; +__attribute__((used)) void* use66280 = (void*)&foo66280; +__attribute__((used)) void* use66281 = (void*)&foo66281; +__attribute__((used)) void* use66282 = (void*)&foo66282; +__attribute__((used)) void* use66283 = (void*)&foo66283; +__attribute__((used)) void* use66284 = (void*)&foo66284; +__attribute__((used)) void* use66285 = (void*)&foo66285; +__attribute__((used)) void* use66286 = (void*)&foo66286; +__attribute__((used)) void* use66287 = (void*)&foo66287; +__attribute__((used)) void* use66288 = (void*)&foo66288; +__attribute__((used)) void* use66289 = (void*)&foo66289; +__attribute__((used)) void* use66290 = (void*)&foo66290; +__attribute__((used)) void* use66291 = (void*)&foo66291; +__attribute__((used)) void* use66292 = (void*)&foo66292; +__attribute__((used)) void* use66293 = (void*)&foo66293; +__attribute__((used)) void* use66294 = (void*)&foo66294; +__attribute__((used)) void* use66295 = (void*)&foo66295; +__attribute__((used)) void* use66296 = (void*)&foo66296; +__attribute__((used)) void* use66297 = (void*)&foo66297; +__attribute__((used)) void* use66298 = (void*)&foo66298; +__attribute__((used)) void* use66299 = (void*)&foo66299; +__attribute__((used)) void* use66300 = (void*)&foo66300; +__attribute__((used)) void* use66301 = (void*)&foo66301; +__attribute__((used)) void* use66302 = (void*)&foo66302; +__attribute__((used)) void* use66303 = (void*)&foo66303; +__attribute__((used)) void* use66304 = (void*)&foo66304; +__attribute__((used)) void* use66305 = (void*)&foo66305; +__attribute__((used)) void* use66306 = (void*)&foo66306; +__attribute__((used)) void* use66307 = (void*)&foo66307; +__attribute__((used)) void* use66308 = (void*)&foo66308; +__attribute__((used)) void* use66309 = (void*)&foo66309; +__attribute__((used)) void* use66310 = (void*)&foo66310; +__attribute__((used)) void* use66311 = (void*)&foo66311; +__attribute__((used)) void* use66312 = (void*)&foo66312; +__attribute__((used)) void* use66313 = (void*)&foo66313; +__attribute__((used)) void* use66314 = (void*)&foo66314; +__attribute__((used)) void* use66315 = (void*)&foo66315; +__attribute__((used)) void* use66316 = (void*)&foo66316; +__attribute__((used)) void* use66317 = (void*)&foo66317; +__attribute__((used)) void* use66318 = (void*)&foo66318; +__attribute__((used)) void* use66319 = (void*)&foo66319; +__attribute__((used)) void* use66320 = (void*)&foo66320; +__attribute__((used)) void* use66321 = (void*)&foo66321; +__attribute__((used)) void* use66322 = (void*)&foo66322; +__attribute__((used)) void* use66323 = (void*)&foo66323; +__attribute__((used)) void* use66324 = (void*)&foo66324; +__attribute__((used)) void* use66325 = (void*)&foo66325; +__attribute__((used)) void* use66326 = (void*)&foo66326; +__attribute__((used)) void* use66327 = (void*)&foo66327; +__attribute__((used)) void* use66328 = (void*)&foo66328; +__attribute__((used)) void* use66329 = (void*)&foo66329; +__attribute__((used)) void* use66330 = (void*)&foo66330; +__attribute__((used)) void* use66331 = (void*)&foo66331; +__attribute__((used)) void* use66332 = (void*)&foo66332; +__attribute__((used)) void* use66333 = (void*)&foo66333; +__attribute__((used)) void* use66334 = (void*)&foo66334; +__attribute__((used)) void* use66335 = (void*)&foo66335; +__attribute__((used)) void* use66336 = (void*)&foo66336; +__attribute__((used)) void* use66337 = (void*)&foo66337; +__attribute__((used)) void* use66338 = (void*)&foo66338; +__attribute__((used)) void* use66339 = (void*)&foo66339; +__attribute__((used)) void* use66340 = (void*)&foo66340; +__attribute__((used)) void* use66341 = (void*)&foo66341; +__attribute__((used)) void* use66342 = (void*)&foo66342; +__attribute__((used)) void* use66343 = (void*)&foo66343; +__attribute__((used)) void* use66344 = (void*)&foo66344; +__attribute__((used)) void* use66345 = (void*)&foo66345; +__attribute__((used)) void* use66346 = (void*)&foo66346; +__attribute__((used)) void* use66347 = (void*)&foo66347; +__attribute__((used)) void* use66348 = (void*)&foo66348; +__attribute__((used)) void* use66349 = (void*)&foo66349; +__attribute__((used)) void* use66350 = (void*)&foo66350; +__attribute__((used)) void* use66351 = (void*)&foo66351; +__attribute__((used)) void* use66352 = (void*)&foo66352; +__attribute__((used)) void* use66353 = (void*)&foo66353; +__attribute__((used)) void* use66354 = (void*)&foo66354; +__attribute__((used)) void* use66355 = (void*)&foo66355; +__attribute__((used)) void* use66356 = (void*)&foo66356; +__attribute__((used)) void* use66357 = (void*)&foo66357; +__attribute__((used)) void* use66358 = (void*)&foo66358; +__attribute__((used)) void* use66359 = (void*)&foo66359; +__attribute__((used)) void* use66360 = (void*)&foo66360; +__attribute__((used)) void* use66361 = (void*)&foo66361; +__attribute__((used)) void* use66362 = (void*)&foo66362; +__attribute__((used)) void* use66363 = (void*)&foo66363; +__attribute__((used)) void* use66364 = (void*)&foo66364; +__attribute__((used)) void* use66365 = (void*)&foo66365; +__attribute__((used)) void* use66366 = (void*)&foo66366; +__attribute__((used)) void* use66367 = (void*)&foo66367; +__attribute__((used)) void* use66368 = (void*)&foo66368; +__attribute__((used)) void* use66369 = (void*)&foo66369; +__attribute__((used)) void* use66370 = (void*)&foo66370; +__attribute__((used)) void* use66371 = (void*)&foo66371; +__attribute__((used)) void* use66372 = (void*)&foo66372; +__attribute__((used)) void* use66373 = (void*)&foo66373; +__attribute__((used)) void* use66374 = (void*)&foo66374; +__attribute__((used)) void* use66375 = (void*)&foo66375; +__attribute__((used)) void* use66376 = (void*)&foo66376; +__attribute__((used)) void* use66377 = (void*)&foo66377; +__attribute__((used)) void* use66378 = (void*)&foo66378; +__attribute__((used)) void* use66379 = (void*)&foo66379; +__attribute__((used)) void* use66380 = (void*)&foo66380; +__attribute__((used)) void* use66381 = (void*)&foo66381; +__attribute__((used)) void* use66382 = (void*)&foo66382; +__attribute__((used)) void* use66383 = (void*)&foo66383; +__attribute__((used)) void* use66384 = (void*)&foo66384; +__attribute__((used)) void* use66385 = (void*)&foo66385; +__attribute__((used)) void* use66386 = (void*)&foo66386; +__attribute__((used)) void* use66387 = (void*)&foo66387; +__attribute__((used)) void* use66388 = (void*)&foo66388; +__attribute__((used)) void* use66389 = (void*)&foo66389; +__attribute__((used)) void* use66390 = (void*)&foo66390; +__attribute__((used)) void* use66391 = (void*)&foo66391; +__attribute__((used)) void* use66392 = (void*)&foo66392; +__attribute__((used)) void* use66393 = (void*)&foo66393; +__attribute__((used)) void* use66394 = (void*)&foo66394; +__attribute__((used)) void* use66395 = (void*)&foo66395; +__attribute__((used)) void* use66396 = (void*)&foo66396; +__attribute__((used)) void* use66397 = (void*)&foo66397; +__attribute__((used)) void* use66398 = (void*)&foo66398; +__attribute__((used)) void* use66399 = (void*)&foo66399; +__attribute__((used)) void* use66400 = (void*)&foo66400; +__attribute__((used)) void* use66401 = (void*)&foo66401; +__attribute__((used)) void* use66402 = (void*)&foo66402; +__attribute__((used)) void* use66403 = (void*)&foo66403; +__attribute__((used)) void* use66404 = (void*)&foo66404; +__attribute__((used)) void* use66405 = (void*)&foo66405; +__attribute__((used)) void* use66406 = (void*)&foo66406; +__attribute__((used)) void* use66407 = (void*)&foo66407; +__attribute__((used)) void* use66408 = (void*)&foo66408; +__attribute__((used)) void* use66409 = (void*)&foo66409; +__attribute__((used)) void* use66410 = (void*)&foo66410; +__attribute__((used)) void* use66411 = (void*)&foo66411; +__attribute__((used)) void* use66412 = (void*)&foo66412; +__attribute__((used)) void* use66413 = (void*)&foo66413; +__attribute__((used)) void* use66414 = (void*)&foo66414; +__attribute__((used)) void* use66415 = (void*)&foo66415; +__attribute__((used)) void* use66416 = (void*)&foo66416; +__attribute__((used)) void* use66417 = (void*)&foo66417; +__attribute__((used)) void* use66418 = (void*)&foo66418; +__attribute__((used)) void* use66419 = (void*)&foo66419; +__attribute__((used)) void* use66420 = (void*)&foo66420; +__attribute__((used)) void* use66421 = (void*)&foo66421; +__attribute__((used)) void* use66422 = (void*)&foo66422; +__attribute__((used)) void* use66423 = (void*)&foo66423; +__attribute__((used)) void* use66424 = (void*)&foo66424; +__attribute__((used)) void* use66425 = (void*)&foo66425; +__attribute__((used)) void* use66426 = (void*)&foo66426; +__attribute__((used)) void* use66427 = (void*)&foo66427; +__attribute__((used)) void* use66428 = (void*)&foo66428; +__attribute__((used)) void* use66429 = (void*)&foo66429; +__attribute__((used)) void* use66430 = (void*)&foo66430; +__attribute__((used)) void* use66431 = (void*)&foo66431; +__attribute__((used)) void* use66432 = (void*)&foo66432; +__attribute__((used)) void* use66433 = (void*)&foo66433; +__attribute__((used)) void* use66434 = (void*)&foo66434; +__attribute__((used)) void* use66435 = (void*)&foo66435; +__attribute__((used)) void* use66436 = (void*)&foo66436; +__attribute__((used)) void* use66437 = (void*)&foo66437; +__attribute__((used)) void* use66438 = (void*)&foo66438; +__attribute__((used)) void* use66439 = (void*)&foo66439; +__attribute__((used)) void* use66440 = (void*)&foo66440; +__attribute__((used)) void* use66441 = (void*)&foo66441; +__attribute__((used)) void* use66442 = (void*)&foo66442; +__attribute__((used)) void* use66443 = (void*)&foo66443; +__attribute__((used)) void* use66444 = (void*)&foo66444; +__attribute__((used)) void* use66445 = (void*)&foo66445; +__attribute__((used)) void* use66446 = (void*)&foo66446; +__attribute__((used)) void* use66447 = (void*)&foo66447; +__attribute__((used)) void* use66448 = (void*)&foo66448; +__attribute__((used)) void* use66449 = (void*)&foo66449; +__attribute__((used)) void* use66450 = (void*)&foo66450; +__attribute__((used)) void* use66451 = (void*)&foo66451; +__attribute__((used)) void* use66452 = (void*)&foo66452; +__attribute__((used)) void* use66453 = (void*)&foo66453; +__attribute__((used)) void* use66454 = (void*)&foo66454; +__attribute__((used)) void* use66455 = (void*)&foo66455; +__attribute__((used)) void* use66456 = (void*)&foo66456; +__attribute__((used)) void* use66457 = (void*)&foo66457; +__attribute__((used)) void* use66458 = (void*)&foo66458; +__attribute__((used)) void* use66459 = (void*)&foo66459; +__attribute__((used)) void* use66460 = (void*)&foo66460; +__attribute__((used)) void* use66461 = (void*)&foo66461; +__attribute__((used)) void* use66462 = (void*)&foo66462; +__attribute__((used)) void* use66463 = (void*)&foo66463; +__attribute__((used)) void* use66464 = (void*)&foo66464; +__attribute__((used)) void* use66465 = (void*)&foo66465; +__attribute__((used)) void* use66466 = (void*)&foo66466; +__attribute__((used)) void* use66467 = (void*)&foo66467; +__attribute__((used)) void* use66468 = (void*)&foo66468; +__attribute__((used)) void* use66469 = (void*)&foo66469; +__attribute__((used)) void* use66470 = (void*)&foo66470; +__attribute__((used)) void* use66471 = (void*)&foo66471; +__attribute__((used)) void* use66472 = (void*)&foo66472; +__attribute__((used)) void* use66473 = (void*)&foo66473; +__attribute__((used)) void* use66474 = (void*)&foo66474; +__attribute__((used)) void* use66475 = (void*)&foo66475; +__attribute__((used)) void* use66476 = (void*)&foo66476; +__attribute__((used)) void* use66477 = (void*)&foo66477; +__attribute__((used)) void* use66478 = (void*)&foo66478; +__attribute__((used)) void* use66479 = (void*)&foo66479; +__attribute__((used)) void* use66480 = (void*)&foo66480; +__attribute__((used)) void* use66481 = (void*)&foo66481; +__attribute__((used)) void* use66482 = (void*)&foo66482; +__attribute__((used)) void* use66483 = (void*)&foo66483; +__attribute__((used)) void* use66484 = (void*)&foo66484; +__attribute__((used)) void* use66485 = (void*)&foo66485; +__attribute__((used)) void* use66486 = (void*)&foo66486; +__attribute__((used)) void* use66487 = (void*)&foo66487; +__attribute__((used)) void* use66488 = (void*)&foo66488; +__attribute__((used)) void* use66489 = (void*)&foo66489; +__attribute__((used)) void* use66490 = (void*)&foo66490; +__attribute__((used)) void* use66491 = (void*)&foo66491; +__attribute__((used)) void* use66492 = (void*)&foo66492; +__attribute__((used)) void* use66493 = (void*)&foo66493; +__attribute__((used)) void* use66494 = (void*)&foo66494; +__attribute__((used)) void* use66495 = (void*)&foo66495; +__attribute__((used)) void* use66496 = (void*)&foo66496; +__attribute__((used)) void* use66497 = (void*)&foo66497; +__attribute__((used)) void* use66498 = (void*)&foo66498; +__attribute__((used)) void* use66499 = (void*)&foo66499; +__attribute__((used)) void* use66500 = (void*)&foo66500; +__attribute__((used)) void* use66501 = (void*)&foo66501; +__attribute__((used)) void* use66502 = (void*)&foo66502; +__attribute__((used)) void* use66503 = (void*)&foo66503; +__attribute__((used)) void* use66504 = (void*)&foo66504; +__attribute__((used)) void* use66505 = (void*)&foo66505; +__attribute__((used)) void* use66506 = (void*)&foo66506; +__attribute__((used)) void* use66507 = (void*)&foo66507; +__attribute__((used)) void* use66508 = (void*)&foo66508; +__attribute__((used)) void* use66509 = (void*)&foo66509; +__attribute__((used)) void* use66510 = (void*)&foo66510; +__attribute__((used)) void* use66511 = (void*)&foo66511; +__attribute__((used)) void* use66512 = (void*)&foo66512; +__attribute__((used)) void* use66513 = (void*)&foo66513; +__attribute__((used)) void* use66514 = (void*)&foo66514; +__attribute__((used)) void* use66515 = (void*)&foo66515; +__attribute__((used)) void* use66516 = (void*)&foo66516; +__attribute__((used)) void* use66517 = (void*)&foo66517; +__attribute__((used)) void* use66518 = (void*)&foo66518; +__attribute__((used)) void* use66519 = (void*)&foo66519; +__attribute__((used)) void* use66520 = (void*)&foo66520; +__attribute__((used)) void* use66521 = (void*)&foo66521; +__attribute__((used)) void* use66522 = (void*)&foo66522; +__attribute__((used)) void* use66523 = (void*)&foo66523; +__attribute__((used)) void* use66524 = (void*)&foo66524; +__attribute__((used)) void* use66525 = (void*)&foo66525; +__attribute__((used)) void* use66526 = (void*)&foo66526; +__attribute__((used)) void* use66527 = (void*)&foo66527; +__attribute__((used)) void* use66528 = (void*)&foo66528; +__attribute__((used)) void* use66529 = (void*)&foo66529; +__attribute__((used)) void* use66530 = (void*)&foo66530; +__attribute__((used)) void* use66531 = (void*)&foo66531; +__attribute__((used)) void* use66532 = (void*)&foo66532; +__attribute__((used)) void* use66533 = (void*)&foo66533; +__attribute__((used)) void* use66534 = (void*)&foo66534; +__attribute__((used)) void* use66535 = (void*)&foo66535; +__attribute__((used)) void* use66536 = (void*)&foo66536; +__attribute__((used)) void* use66537 = (void*)&foo66537; +__attribute__((used)) void* use66538 = (void*)&foo66538; +__attribute__((used)) void* use66539 = (void*)&foo66539; +__attribute__((used)) void* use66540 = (void*)&foo66540; +__attribute__((used)) void* use66541 = (void*)&foo66541; +__attribute__((used)) void* use66542 = (void*)&foo66542; +__attribute__((used)) void* use66543 = (void*)&foo66543; +__attribute__((used)) void* use66544 = (void*)&foo66544; +__attribute__((used)) void* use66545 = (void*)&foo66545; +__attribute__((used)) void* use66546 = (void*)&foo66546; +__attribute__((used)) void* use66547 = (void*)&foo66547; +__attribute__((used)) void* use66548 = (void*)&foo66548; +__attribute__((used)) void* use66549 = (void*)&foo66549; +__attribute__((used)) void* use66550 = (void*)&foo66550; +__attribute__((used)) void* use66551 = (void*)&foo66551; +__attribute__((used)) void* use66552 = (void*)&foo66552; +__attribute__((used)) void* use66553 = (void*)&foo66553; +__attribute__((used)) void* use66554 = (void*)&foo66554; +__attribute__((used)) void* use66555 = (void*)&foo66555; +__attribute__((used)) void* use66556 = (void*)&foo66556; +__attribute__((used)) void* use66557 = (void*)&foo66557; +__attribute__((used)) void* use66558 = (void*)&foo66558; +__attribute__((used)) void* use66559 = (void*)&foo66559; +__attribute__((used)) void* use66560 = (void*)&foo66560; +__attribute__((used)) void* use66561 = (void*)&foo66561; +__attribute__((used)) void* use66562 = (void*)&foo66562; +__attribute__((used)) void* use66563 = (void*)&foo66563; +__attribute__((used)) void* use66564 = (void*)&foo66564; +__attribute__((used)) void* use66565 = (void*)&foo66565; +__attribute__((used)) void* use66566 = (void*)&foo66566; +__attribute__((used)) void* use66567 = (void*)&foo66567; +__attribute__((used)) void* use66568 = (void*)&foo66568; +__attribute__((used)) void* use66569 = (void*)&foo66569; +__attribute__((used)) void* use66570 = (void*)&foo66570; +__attribute__((used)) void* use66571 = (void*)&foo66571; +__attribute__((used)) void* use66572 = (void*)&foo66572; +__attribute__((used)) void* use66573 = (void*)&foo66573; +__attribute__((used)) void* use66574 = (void*)&foo66574; +__attribute__((used)) void* use66575 = (void*)&foo66575; +__attribute__((used)) void* use66576 = (void*)&foo66576; +__attribute__((used)) void* use66577 = (void*)&foo66577; +__attribute__((used)) void* use66578 = (void*)&foo66578; +__attribute__((used)) void* use66579 = (void*)&foo66579; +__attribute__((used)) void* use66580 = (void*)&foo66580; +__attribute__((used)) void* use66581 = (void*)&foo66581; +__attribute__((used)) void* use66582 = (void*)&foo66582; +__attribute__((used)) void* use66583 = (void*)&foo66583; +__attribute__((used)) void* use66584 = (void*)&foo66584; +__attribute__((used)) void* use66585 = (void*)&foo66585; +__attribute__((used)) void* use66586 = (void*)&foo66586; +__attribute__((used)) void* use66587 = (void*)&foo66587; +__attribute__((used)) void* use66588 = (void*)&foo66588; +__attribute__((used)) void* use66589 = (void*)&foo66589; +__attribute__((used)) void* use66590 = (void*)&foo66590; +__attribute__((used)) void* use66591 = (void*)&foo66591; +__attribute__((used)) void* use66592 = (void*)&foo66592; +__attribute__((used)) void* use66593 = (void*)&foo66593; +__attribute__((used)) void* use66594 = (void*)&foo66594; +__attribute__((used)) void* use66595 = (void*)&foo66595; +__attribute__((used)) void* use66596 = (void*)&foo66596; +__attribute__((used)) void* use66597 = (void*)&foo66597; +__attribute__((used)) void* use66598 = (void*)&foo66598; +__attribute__((used)) void* use66599 = (void*)&foo66599; +__attribute__((used)) void* use66600 = (void*)&foo66600; +__attribute__((used)) void* use66601 = (void*)&foo66601; +__attribute__((used)) void* use66602 = (void*)&foo66602; +__attribute__((used)) void* use66603 = (void*)&foo66603; +__attribute__((used)) void* use66604 = (void*)&foo66604; +__attribute__((used)) void* use66605 = (void*)&foo66605; +__attribute__((used)) void* use66606 = (void*)&foo66606; +__attribute__((used)) void* use66607 = (void*)&foo66607; +__attribute__((used)) void* use66608 = (void*)&foo66608; +__attribute__((used)) void* use66609 = (void*)&foo66609; +__attribute__((used)) void* use66610 = (void*)&foo66610; +__attribute__((used)) void* use66611 = (void*)&foo66611; +__attribute__((used)) void* use66612 = (void*)&foo66612; +__attribute__((used)) void* use66613 = (void*)&foo66613; +__attribute__((used)) void* use66614 = (void*)&foo66614; +__attribute__((used)) void* use66615 = (void*)&foo66615; +__attribute__((used)) void* use66616 = (void*)&foo66616; +__attribute__((used)) void* use66617 = (void*)&foo66617; +__attribute__((used)) void* use66618 = (void*)&foo66618; +__attribute__((used)) void* use66619 = (void*)&foo66619; +__attribute__((used)) void* use66620 = (void*)&foo66620; +__attribute__((used)) void* use66621 = (void*)&foo66621; +__attribute__((used)) void* use66622 = (void*)&foo66622; +__attribute__((used)) void* use66623 = (void*)&foo66623; +__attribute__((used)) void* use66624 = (void*)&foo66624; +__attribute__((used)) void* use66625 = (void*)&foo66625; +__attribute__((used)) void* use66626 = (void*)&foo66626; +__attribute__((used)) void* use66627 = (void*)&foo66627; +__attribute__((used)) void* use66628 = (void*)&foo66628; +__attribute__((used)) void* use66629 = (void*)&foo66629; +__attribute__((used)) void* use66630 = (void*)&foo66630; +__attribute__((used)) void* use66631 = (void*)&foo66631; +__attribute__((used)) void* use66632 = (void*)&foo66632; +__attribute__((used)) void* use66633 = (void*)&foo66633; +__attribute__((used)) void* use66634 = (void*)&foo66634; +__attribute__((used)) void* use66635 = (void*)&foo66635; +__attribute__((used)) void* use66636 = (void*)&foo66636; +__attribute__((used)) void* use66637 = (void*)&foo66637; +__attribute__((used)) void* use66638 = (void*)&foo66638; +__attribute__((used)) void* use66639 = (void*)&foo66639; +__attribute__((used)) void* use66640 = (void*)&foo66640; +__attribute__((used)) void* use66641 = (void*)&foo66641; +__attribute__((used)) void* use66642 = (void*)&foo66642; +__attribute__((used)) void* use66643 = (void*)&foo66643; +__attribute__((used)) void* use66644 = (void*)&foo66644; +__attribute__((used)) void* use66645 = (void*)&foo66645; +__attribute__((used)) void* use66646 = (void*)&foo66646; +__attribute__((used)) void* use66647 = (void*)&foo66647; +__attribute__((used)) void* use66648 = (void*)&foo66648; +__attribute__((used)) void* use66649 = (void*)&foo66649; +__attribute__((used)) void* use66650 = (void*)&foo66650; +__attribute__((used)) void* use66651 = (void*)&foo66651; +__attribute__((used)) void* use66652 = (void*)&foo66652; +__attribute__((used)) void* use66653 = (void*)&foo66653; +__attribute__((used)) void* use66654 = (void*)&foo66654; +__attribute__((used)) void* use66655 = (void*)&foo66655; +__attribute__((used)) void* use66656 = (void*)&foo66656; +__attribute__((used)) void* use66657 = (void*)&foo66657; +__attribute__((used)) void* use66658 = (void*)&foo66658; +__attribute__((used)) void* use66659 = (void*)&foo66659; +__attribute__((used)) void* use66660 = (void*)&foo66660; +__attribute__((used)) void* use66661 = (void*)&foo66661; +__attribute__((used)) void* use66662 = (void*)&foo66662; +__attribute__((used)) void* use66663 = (void*)&foo66663; +__attribute__((used)) void* use66664 = (void*)&foo66664; +__attribute__((used)) void* use66665 = (void*)&foo66665; +__attribute__((used)) void* use66666 = (void*)&foo66666; +__attribute__((used)) void* use66667 = (void*)&foo66667; +__attribute__((used)) void* use66668 = (void*)&foo66668; +__attribute__((used)) void* use66669 = (void*)&foo66669; +__attribute__((used)) void* use66670 = (void*)&foo66670; +__attribute__((used)) void* use66671 = (void*)&foo66671; +__attribute__((used)) void* use66672 = (void*)&foo66672; +__attribute__((used)) void* use66673 = (void*)&foo66673; +__attribute__((used)) void* use66674 = (void*)&foo66674; +__attribute__((used)) void* use66675 = (void*)&foo66675; +__attribute__((used)) void* use66676 = (void*)&foo66676; +__attribute__((used)) void* use66677 = (void*)&foo66677; +__attribute__((used)) void* use66678 = (void*)&foo66678; +__attribute__((used)) void* use66679 = (void*)&foo66679; +__attribute__((used)) void* use66680 = (void*)&foo66680; +__attribute__((used)) void* use66681 = (void*)&foo66681; +__attribute__((used)) void* use66682 = (void*)&foo66682; +__attribute__((used)) void* use66683 = (void*)&foo66683; +__attribute__((used)) void* use66684 = (void*)&foo66684; +__attribute__((used)) void* use66685 = (void*)&foo66685; +__attribute__((used)) void* use66686 = (void*)&foo66686; +__attribute__((used)) void* use66687 = (void*)&foo66687; +__attribute__((used)) void* use66688 = (void*)&foo66688; +__attribute__((used)) void* use66689 = (void*)&foo66689; +__attribute__((used)) void* use66690 = (void*)&foo66690; +__attribute__((used)) void* use66691 = (void*)&foo66691; +__attribute__((used)) void* use66692 = (void*)&foo66692; +__attribute__((used)) void* use66693 = (void*)&foo66693; +__attribute__((used)) void* use66694 = (void*)&foo66694; +__attribute__((used)) void* use66695 = (void*)&foo66695; +__attribute__((used)) void* use66696 = (void*)&foo66696; +__attribute__((used)) void* use66697 = (void*)&foo66697; +__attribute__((used)) void* use66698 = (void*)&foo66698; +__attribute__((used)) void* use66699 = (void*)&foo66699; +__attribute__((used)) void* use66700 = (void*)&foo66700; +__attribute__((used)) void* use66701 = (void*)&foo66701; +__attribute__((used)) void* use66702 = (void*)&foo66702; +__attribute__((used)) void* use66703 = (void*)&foo66703; +__attribute__((used)) void* use66704 = (void*)&foo66704; +__attribute__((used)) void* use66705 = (void*)&foo66705; +__attribute__((used)) void* use66706 = (void*)&foo66706; +__attribute__((used)) void* use66707 = (void*)&foo66707; +__attribute__((used)) void* use66708 = (void*)&foo66708; +__attribute__((used)) void* use66709 = (void*)&foo66709; +__attribute__((used)) void* use66710 = (void*)&foo66710; +__attribute__((used)) void* use66711 = (void*)&foo66711; +__attribute__((used)) void* use66712 = (void*)&foo66712; +__attribute__((used)) void* use66713 = (void*)&foo66713; +__attribute__((used)) void* use66714 = (void*)&foo66714; +__attribute__((used)) void* use66715 = (void*)&foo66715; +__attribute__((used)) void* use66716 = (void*)&foo66716; +__attribute__((used)) void* use66717 = (void*)&foo66717; +__attribute__((used)) void* use66718 = (void*)&foo66718; +__attribute__((used)) void* use66719 = (void*)&foo66719; +__attribute__((used)) void* use66720 = (void*)&foo66720; +__attribute__((used)) void* use66721 = (void*)&foo66721; +__attribute__((used)) void* use66722 = (void*)&foo66722; +__attribute__((used)) void* use66723 = (void*)&foo66723; +__attribute__((used)) void* use66724 = (void*)&foo66724; +__attribute__((used)) void* use66725 = (void*)&foo66725; +__attribute__((used)) void* use66726 = (void*)&foo66726; +__attribute__((used)) void* use66727 = (void*)&foo66727; +__attribute__((used)) void* use66728 = (void*)&foo66728; +__attribute__((used)) void* use66729 = (void*)&foo66729; +__attribute__((used)) void* use66730 = (void*)&foo66730; +__attribute__((used)) void* use66731 = (void*)&foo66731; +__attribute__((used)) void* use66732 = (void*)&foo66732; +__attribute__((used)) void* use66733 = (void*)&foo66733; +__attribute__((used)) void* use66734 = (void*)&foo66734; +__attribute__((used)) void* use66735 = (void*)&foo66735; +__attribute__((used)) void* use66736 = (void*)&foo66736; +__attribute__((used)) void* use66737 = (void*)&foo66737; +__attribute__((used)) void* use66738 = (void*)&foo66738; +__attribute__((used)) void* use66739 = (void*)&foo66739; +__attribute__((used)) void* use66740 = (void*)&foo66740; +__attribute__((used)) void* use66741 = (void*)&foo66741; +__attribute__((used)) void* use66742 = (void*)&foo66742; +__attribute__((used)) void* use66743 = (void*)&foo66743; +__attribute__((used)) void* use66744 = (void*)&foo66744; +__attribute__((used)) void* use66745 = (void*)&foo66745; +__attribute__((used)) void* use66746 = (void*)&foo66746; +__attribute__((used)) void* use66747 = (void*)&foo66747; +__attribute__((used)) void* use66748 = (void*)&foo66748; +__attribute__((used)) void* use66749 = (void*)&foo66749; +__attribute__((used)) void* use66750 = (void*)&foo66750; +__attribute__((used)) void* use66751 = (void*)&foo66751; +__attribute__((used)) void* use66752 = (void*)&foo66752; +__attribute__((used)) void* use66753 = (void*)&foo66753; +__attribute__((used)) void* use66754 = (void*)&foo66754; +__attribute__((used)) void* use66755 = (void*)&foo66755; +__attribute__((used)) void* use66756 = (void*)&foo66756; +__attribute__((used)) void* use66757 = (void*)&foo66757; +__attribute__((used)) void* use66758 = (void*)&foo66758; +__attribute__((used)) void* use66759 = (void*)&foo66759; +__attribute__((used)) void* use66760 = (void*)&foo66760; +__attribute__((used)) void* use66761 = (void*)&foo66761; +__attribute__((used)) void* use66762 = (void*)&foo66762; +__attribute__((used)) void* use66763 = (void*)&foo66763; +__attribute__((used)) void* use66764 = (void*)&foo66764; +__attribute__((used)) void* use66765 = (void*)&foo66765; +__attribute__((used)) void* use66766 = (void*)&foo66766; +__attribute__((used)) void* use66767 = (void*)&foo66767; +__attribute__((used)) void* use66768 = (void*)&foo66768; +__attribute__((used)) void* use66769 = (void*)&foo66769; +__attribute__((used)) void* use66770 = (void*)&foo66770; +__attribute__((used)) void* use66771 = (void*)&foo66771; +__attribute__((used)) void* use66772 = (void*)&foo66772; +__attribute__((used)) void* use66773 = (void*)&foo66773; +__attribute__((used)) void* use66774 = (void*)&foo66774; +__attribute__((used)) void* use66775 = (void*)&foo66775; +__attribute__((used)) void* use66776 = (void*)&foo66776; +__attribute__((used)) void* use66777 = (void*)&foo66777; +__attribute__((used)) void* use66778 = (void*)&foo66778; +__attribute__((used)) void* use66779 = (void*)&foo66779; +__attribute__((used)) void* use66780 = (void*)&foo66780; +__attribute__((used)) void* use66781 = (void*)&foo66781; +__attribute__((used)) void* use66782 = (void*)&foo66782; +__attribute__((used)) void* use66783 = (void*)&foo66783; +__attribute__((used)) void* use66784 = (void*)&foo66784; +__attribute__((used)) void* use66785 = (void*)&foo66785; +__attribute__((used)) void* use66786 = (void*)&foo66786; +__attribute__((used)) void* use66787 = (void*)&foo66787; +__attribute__((used)) void* use66788 = (void*)&foo66788; +__attribute__((used)) void* use66789 = (void*)&foo66789; +__attribute__((used)) void* use66790 = (void*)&foo66790; +__attribute__((used)) void* use66791 = (void*)&foo66791; +__attribute__((used)) void* use66792 = (void*)&foo66792; +__attribute__((used)) void* use66793 = (void*)&foo66793; +__attribute__((used)) void* use66794 = (void*)&foo66794; +__attribute__((used)) void* use66795 = (void*)&foo66795; +__attribute__((used)) void* use66796 = (void*)&foo66796; +__attribute__((used)) void* use66797 = (void*)&foo66797; +__attribute__((used)) void* use66798 = (void*)&foo66798; +__attribute__((used)) void* use66799 = (void*)&foo66799; +__attribute__((used)) void* use66800 = (void*)&foo66800; +__attribute__((used)) void* use66801 = (void*)&foo66801; +__attribute__((used)) void* use66802 = (void*)&foo66802; +__attribute__((used)) void* use66803 = (void*)&foo66803; +__attribute__((used)) void* use66804 = (void*)&foo66804; +__attribute__((used)) void* use66805 = (void*)&foo66805; +__attribute__((used)) void* use66806 = (void*)&foo66806; +__attribute__((used)) void* use66807 = (void*)&foo66807; +__attribute__((used)) void* use66808 = (void*)&foo66808; +__attribute__((used)) void* use66809 = (void*)&foo66809; +__attribute__((used)) void* use66810 = (void*)&foo66810; +__attribute__((used)) void* use66811 = (void*)&foo66811; +__attribute__((used)) void* use66812 = (void*)&foo66812; +__attribute__((used)) void* use66813 = (void*)&foo66813; +__attribute__((used)) void* use66814 = (void*)&foo66814; +__attribute__((used)) void* use66815 = (void*)&foo66815; +__attribute__((used)) void* use66816 = (void*)&foo66816; +__attribute__((used)) void* use66817 = (void*)&foo66817; +__attribute__((used)) void* use66818 = (void*)&foo66818; +__attribute__((used)) void* use66819 = (void*)&foo66819; +__attribute__((used)) void* use66820 = (void*)&foo66820; +__attribute__((used)) void* use66821 = (void*)&foo66821; +__attribute__((used)) void* use66822 = (void*)&foo66822; +__attribute__((used)) void* use66823 = (void*)&foo66823; +__attribute__((used)) void* use66824 = (void*)&foo66824; +__attribute__((used)) void* use66825 = (void*)&foo66825; +__attribute__((used)) void* use66826 = (void*)&foo66826; +__attribute__((used)) void* use66827 = (void*)&foo66827; +__attribute__((used)) void* use66828 = (void*)&foo66828; +__attribute__((used)) void* use66829 = (void*)&foo66829; +__attribute__((used)) void* use66830 = (void*)&foo66830; +__attribute__((used)) void* use66831 = (void*)&foo66831; +__attribute__((used)) void* use66832 = (void*)&foo66832; +__attribute__((used)) void* use66833 = (void*)&foo66833; +__attribute__((used)) void* use66834 = (void*)&foo66834; +__attribute__((used)) void* use66835 = (void*)&foo66835; +__attribute__((used)) void* use66836 = (void*)&foo66836; +__attribute__((used)) void* use66837 = (void*)&foo66837; +__attribute__((used)) void* use66838 = (void*)&foo66838; +__attribute__((used)) void* use66839 = (void*)&foo66839; +__attribute__((used)) void* use66840 = (void*)&foo66840; +__attribute__((used)) void* use66841 = (void*)&foo66841; +__attribute__((used)) void* use66842 = (void*)&foo66842; +__attribute__((used)) void* use66843 = (void*)&foo66843; +__attribute__((used)) void* use66844 = (void*)&foo66844; +__attribute__((used)) void* use66845 = (void*)&foo66845; +__attribute__((used)) void* use66846 = (void*)&foo66846; +__attribute__((used)) void* use66847 = (void*)&foo66847; +__attribute__((used)) void* use66848 = (void*)&foo66848; +__attribute__((used)) void* use66849 = (void*)&foo66849; +__attribute__((used)) void* use66850 = (void*)&foo66850; +__attribute__((used)) void* use66851 = (void*)&foo66851; +__attribute__((used)) void* use66852 = (void*)&foo66852; +__attribute__((used)) void* use66853 = (void*)&foo66853; +__attribute__((used)) void* use66854 = (void*)&foo66854; +__attribute__((used)) void* use66855 = (void*)&foo66855; +__attribute__((used)) void* use66856 = (void*)&foo66856; +__attribute__((used)) void* use66857 = (void*)&foo66857; +__attribute__((used)) void* use66858 = (void*)&foo66858; +__attribute__((used)) void* use66859 = (void*)&foo66859; +__attribute__((used)) void* use66860 = (void*)&foo66860; +__attribute__((used)) void* use66861 = (void*)&foo66861; +__attribute__((used)) void* use66862 = (void*)&foo66862; +__attribute__((used)) void* use66863 = (void*)&foo66863; +__attribute__((used)) void* use66864 = (void*)&foo66864; +__attribute__((used)) void* use66865 = (void*)&foo66865; +__attribute__((used)) void* use66866 = (void*)&foo66866; +__attribute__((used)) void* use66867 = (void*)&foo66867; +__attribute__((used)) void* use66868 = (void*)&foo66868; +__attribute__((used)) void* use66869 = (void*)&foo66869; +__attribute__((used)) void* use66870 = (void*)&foo66870; +__attribute__((used)) void* use66871 = (void*)&foo66871; +__attribute__((used)) void* use66872 = (void*)&foo66872; +__attribute__((used)) void* use66873 = (void*)&foo66873; +__attribute__((used)) void* use66874 = (void*)&foo66874; +__attribute__((used)) void* use66875 = (void*)&foo66875; +__attribute__((used)) void* use66876 = (void*)&foo66876; +__attribute__((used)) void* use66877 = (void*)&foo66877; +__attribute__((used)) void* use66878 = (void*)&foo66878; +__attribute__((used)) void* use66879 = (void*)&foo66879; +__attribute__((used)) void* use66880 = (void*)&foo66880; +__attribute__((used)) void* use66881 = (void*)&foo66881; +__attribute__((used)) void* use66882 = (void*)&foo66882; +__attribute__((used)) void* use66883 = (void*)&foo66883; +__attribute__((used)) void* use66884 = (void*)&foo66884; +__attribute__((used)) void* use66885 = (void*)&foo66885; +__attribute__((used)) void* use66886 = (void*)&foo66886; +__attribute__((used)) void* use66887 = (void*)&foo66887; +__attribute__((used)) void* use66888 = (void*)&foo66888; +__attribute__((used)) void* use66889 = (void*)&foo66889; +__attribute__((used)) void* use66890 = (void*)&foo66890; +__attribute__((used)) void* use66891 = (void*)&foo66891; +__attribute__((used)) void* use66892 = (void*)&foo66892; +__attribute__((used)) void* use66893 = (void*)&foo66893; +__attribute__((used)) void* use66894 = (void*)&foo66894; +__attribute__((used)) void* use66895 = (void*)&foo66895; +__attribute__((used)) void* use66896 = (void*)&foo66896; +__attribute__((used)) void* use66897 = (void*)&foo66897; +__attribute__((used)) void* use66898 = (void*)&foo66898; +__attribute__((used)) void* use66899 = (void*)&foo66899; +__attribute__((used)) void* use66900 = (void*)&foo66900; +__attribute__((used)) void* use66901 = (void*)&foo66901; +__attribute__((used)) void* use66902 = (void*)&foo66902; +__attribute__((used)) void* use66903 = (void*)&foo66903; +__attribute__((used)) void* use66904 = (void*)&foo66904; +__attribute__((used)) void* use66905 = (void*)&foo66905; +__attribute__((used)) void* use66906 = (void*)&foo66906; +__attribute__((used)) void* use66907 = (void*)&foo66907; +__attribute__((used)) void* use66908 = (void*)&foo66908; +__attribute__((used)) void* use66909 = (void*)&foo66909; +__attribute__((used)) void* use66910 = (void*)&foo66910; +__attribute__((used)) void* use66911 = (void*)&foo66911; +__attribute__((used)) void* use66912 = (void*)&foo66912; +__attribute__((used)) void* use66913 = (void*)&foo66913; +__attribute__((used)) void* use66914 = (void*)&foo66914; +__attribute__((used)) void* use66915 = (void*)&foo66915; +__attribute__((used)) void* use66916 = (void*)&foo66916; +__attribute__((used)) void* use66917 = (void*)&foo66917; +__attribute__((used)) void* use66918 = (void*)&foo66918; +__attribute__((used)) void* use66919 = (void*)&foo66919; +__attribute__((used)) void* use66920 = (void*)&foo66920; +__attribute__((used)) void* use66921 = (void*)&foo66921; +__attribute__((used)) void* use66922 = (void*)&foo66922; +__attribute__((used)) void* use66923 = (void*)&foo66923; +__attribute__((used)) void* use66924 = (void*)&foo66924; +__attribute__((used)) void* use66925 = (void*)&foo66925; +__attribute__((used)) void* use66926 = (void*)&foo66926; +__attribute__((used)) void* use66927 = (void*)&foo66927; +__attribute__((used)) void* use66928 = (void*)&foo66928; +__attribute__((used)) void* use66929 = (void*)&foo66929; +__attribute__((used)) void* use66930 = (void*)&foo66930; +__attribute__((used)) void* use66931 = (void*)&foo66931; +__attribute__((used)) void* use66932 = (void*)&foo66932; +__attribute__((used)) void* use66933 = (void*)&foo66933; +__attribute__((used)) void* use66934 = (void*)&foo66934; +__attribute__((used)) void* use66935 = (void*)&foo66935; +__attribute__((used)) void* use66936 = (void*)&foo66936; +__attribute__((used)) void* use66937 = (void*)&foo66937; +__attribute__((used)) void* use66938 = (void*)&foo66938; +__attribute__((used)) void* use66939 = (void*)&foo66939; +__attribute__((used)) void* use66940 = (void*)&foo66940; +__attribute__((used)) void* use66941 = (void*)&foo66941; +__attribute__((used)) void* use66942 = (void*)&foo66942; +__attribute__((used)) void* use66943 = (void*)&foo66943; +__attribute__((used)) void* use66944 = (void*)&foo66944; +__attribute__((used)) void* use66945 = (void*)&foo66945; +__attribute__((used)) void* use66946 = (void*)&foo66946; +__attribute__((used)) void* use66947 = (void*)&foo66947; +__attribute__((used)) void* use66948 = (void*)&foo66948; +__attribute__((used)) void* use66949 = (void*)&foo66949; +__attribute__((used)) void* use66950 = (void*)&foo66950; +__attribute__((used)) void* use66951 = (void*)&foo66951; +__attribute__((used)) void* use66952 = (void*)&foo66952; +__attribute__((used)) void* use66953 = (void*)&foo66953; +__attribute__((used)) void* use66954 = (void*)&foo66954; +__attribute__((used)) void* use66955 = (void*)&foo66955; +__attribute__((used)) void* use66956 = (void*)&foo66956; +__attribute__((used)) void* use66957 = (void*)&foo66957; +__attribute__((used)) void* use66958 = (void*)&foo66958; +__attribute__((used)) void* use66959 = (void*)&foo66959; +__attribute__((used)) void* use66960 = (void*)&foo66960; +__attribute__((used)) void* use66961 = (void*)&foo66961; +__attribute__((used)) void* use66962 = (void*)&foo66962; +__attribute__((used)) void* use66963 = (void*)&foo66963; +__attribute__((used)) void* use66964 = (void*)&foo66964; +__attribute__((used)) void* use66965 = (void*)&foo66965; +__attribute__((used)) void* use66966 = (void*)&foo66966; +__attribute__((used)) void* use66967 = (void*)&foo66967; +__attribute__((used)) void* use66968 = (void*)&foo66968; +__attribute__((used)) void* use66969 = (void*)&foo66969; +__attribute__((used)) void* use66970 = (void*)&foo66970; +__attribute__((used)) void* use66971 = (void*)&foo66971; +__attribute__((used)) void* use66972 = (void*)&foo66972; +__attribute__((used)) void* use66973 = (void*)&foo66973; +__attribute__((used)) void* use66974 = (void*)&foo66974; +__attribute__((used)) void* use66975 = (void*)&foo66975; +__attribute__((used)) void* use66976 = (void*)&foo66976; +__attribute__((used)) void* use66977 = (void*)&foo66977; +__attribute__((used)) void* use66978 = (void*)&foo66978; +__attribute__((used)) void* use66979 = (void*)&foo66979; +__attribute__((used)) void* use66980 = (void*)&foo66980; +__attribute__((used)) void* use66981 = (void*)&foo66981; +__attribute__((used)) void* use66982 = (void*)&foo66982; +__attribute__((used)) void* use66983 = (void*)&foo66983; +__attribute__((used)) void* use66984 = (void*)&foo66984; +__attribute__((used)) void* use66985 = (void*)&foo66985; +__attribute__((used)) void* use66986 = (void*)&foo66986; +__attribute__((used)) void* use66987 = (void*)&foo66987; +__attribute__((used)) void* use66988 = (void*)&foo66988; +__attribute__((used)) void* use66989 = (void*)&foo66989; +__attribute__((used)) void* use66990 = (void*)&foo66990; +__attribute__((used)) void* use66991 = (void*)&foo66991; +__attribute__((used)) void* use66992 = (void*)&foo66992; +__attribute__((used)) void* use66993 = (void*)&foo66993; +__attribute__((used)) void* use66994 = (void*)&foo66994; +__attribute__((used)) void* use66995 = (void*)&foo66995; +__attribute__((used)) void* use66996 = (void*)&foo66996; +__attribute__((used)) void* use66997 = (void*)&foo66997; +__attribute__((used)) void* use66998 = (void*)&foo66998; +__attribute__((used)) void* use66999 = (void*)&foo66999; +__attribute__((used)) void* use67000 = (void*)&foo67000; +__attribute__((used)) void* use67001 = (void*)&foo67001; +__attribute__((used)) void* use67002 = (void*)&foo67002; +__attribute__((used)) void* use67003 = (void*)&foo67003; +__attribute__((used)) void* use67004 = (void*)&foo67004; +__attribute__((used)) void* use67005 = (void*)&foo67005; +__attribute__((used)) void* use67006 = (void*)&foo67006; +__attribute__((used)) void* use67007 = (void*)&foo67007; +__attribute__((used)) void* use67008 = (void*)&foo67008; +__attribute__((used)) void* use67009 = (void*)&foo67009; +__attribute__((used)) void* use67010 = (void*)&foo67010; +__attribute__((used)) void* use67011 = (void*)&foo67011; +__attribute__((used)) void* use67012 = (void*)&foo67012; +__attribute__((used)) void* use67013 = (void*)&foo67013; +__attribute__((used)) void* use67014 = (void*)&foo67014; +__attribute__((used)) void* use67015 = (void*)&foo67015; +__attribute__((used)) void* use67016 = (void*)&foo67016; +__attribute__((used)) void* use67017 = (void*)&foo67017; +__attribute__((used)) void* use67018 = (void*)&foo67018; +__attribute__((used)) void* use67019 = (void*)&foo67019; +__attribute__((used)) void* use67020 = (void*)&foo67020; +__attribute__((used)) void* use67021 = (void*)&foo67021; +__attribute__((used)) void* use67022 = (void*)&foo67022; +__attribute__((used)) void* use67023 = (void*)&foo67023; +__attribute__((used)) void* use67024 = (void*)&foo67024; +__attribute__((used)) void* use67025 = (void*)&foo67025; +__attribute__((used)) void* use67026 = (void*)&foo67026; +__attribute__((used)) void* use67027 = (void*)&foo67027; +__attribute__((used)) void* use67028 = (void*)&foo67028; +__attribute__((used)) void* use67029 = (void*)&foo67029; +__attribute__((used)) void* use67030 = (void*)&foo67030; +__attribute__((used)) void* use67031 = (void*)&foo67031; +__attribute__((used)) void* use67032 = (void*)&foo67032; +__attribute__((used)) void* use67033 = (void*)&foo67033; +__attribute__((used)) void* use67034 = (void*)&foo67034; +__attribute__((used)) void* use67035 = (void*)&foo67035; +__attribute__((used)) void* use67036 = (void*)&foo67036; +__attribute__((used)) void* use67037 = (void*)&foo67037; +__attribute__((used)) void* use67038 = (void*)&foo67038; +__attribute__((used)) void* use67039 = (void*)&foo67039; +__attribute__((used)) void* use67040 = (void*)&foo67040; +__attribute__((used)) void* use67041 = (void*)&foo67041; +__attribute__((used)) void* use67042 = (void*)&foo67042; +__attribute__((used)) void* use67043 = (void*)&foo67043; +__attribute__((used)) void* use67044 = (void*)&foo67044; +__attribute__((used)) void* use67045 = (void*)&foo67045; +__attribute__((used)) void* use67046 = (void*)&foo67046; +__attribute__((used)) void* use67047 = (void*)&foo67047; +__attribute__((used)) void* use67048 = (void*)&foo67048; +__attribute__((used)) void* use67049 = (void*)&foo67049; +__attribute__((used)) void* use67050 = (void*)&foo67050; +__attribute__((used)) void* use67051 = (void*)&foo67051; +__attribute__((used)) void* use67052 = (void*)&foo67052; +__attribute__((used)) void* use67053 = (void*)&foo67053; +__attribute__((used)) void* use67054 = (void*)&foo67054; +__attribute__((used)) void* use67055 = (void*)&foo67055; +__attribute__((used)) void* use67056 = (void*)&foo67056; +__attribute__((used)) void* use67057 = (void*)&foo67057; +__attribute__((used)) void* use67058 = (void*)&foo67058; +__attribute__((used)) void* use67059 = (void*)&foo67059; +__attribute__((used)) void* use67060 = (void*)&foo67060; +__attribute__((used)) void* use67061 = (void*)&foo67061; +__attribute__((used)) void* use67062 = (void*)&foo67062; +__attribute__((used)) void* use67063 = (void*)&foo67063; +__attribute__((used)) void* use67064 = (void*)&foo67064; +__attribute__((used)) void* use67065 = (void*)&foo67065; +__attribute__((used)) void* use67066 = (void*)&foo67066; +__attribute__((used)) void* use67067 = (void*)&foo67067; +__attribute__((used)) void* use67068 = (void*)&foo67068; +__attribute__((used)) void* use67069 = (void*)&foo67069; +__attribute__((used)) void* use67070 = (void*)&foo67070; +__attribute__((used)) void* use67071 = (void*)&foo67071; +__attribute__((used)) void* use67072 = (void*)&foo67072; +__attribute__((used)) void* use67073 = (void*)&foo67073; +__attribute__((used)) void* use67074 = (void*)&foo67074; +__attribute__((used)) void* use67075 = (void*)&foo67075; +__attribute__((used)) void* use67076 = (void*)&foo67076; +__attribute__((used)) void* use67077 = (void*)&foo67077; +__attribute__((used)) void* use67078 = (void*)&foo67078; +__attribute__((used)) void* use67079 = (void*)&foo67079; +__attribute__((used)) void* use67080 = (void*)&foo67080; +__attribute__((used)) void* use67081 = (void*)&foo67081; +__attribute__((used)) void* use67082 = (void*)&foo67082; +__attribute__((used)) void* use67083 = (void*)&foo67083; +__attribute__((used)) void* use67084 = (void*)&foo67084; +__attribute__((used)) void* use67085 = (void*)&foo67085; +__attribute__((used)) void* use67086 = (void*)&foo67086; +__attribute__((used)) void* use67087 = (void*)&foo67087; +__attribute__((used)) void* use67088 = (void*)&foo67088; +__attribute__((used)) void* use67089 = (void*)&foo67089; +__attribute__((used)) void* use67090 = (void*)&foo67090; +__attribute__((used)) void* use67091 = (void*)&foo67091; +__attribute__((used)) void* use67092 = (void*)&foo67092; +__attribute__((used)) void* use67093 = (void*)&foo67093; +__attribute__((used)) void* use67094 = (void*)&foo67094; +__attribute__((used)) void* use67095 = (void*)&foo67095; +__attribute__((used)) void* use67096 = (void*)&foo67096; +__attribute__((used)) void* use67097 = (void*)&foo67097; +__attribute__((used)) void* use67098 = (void*)&foo67098; +__attribute__((used)) void* use67099 = (void*)&foo67099; +__attribute__((used)) void* use67100 = (void*)&foo67100; +__attribute__((used)) void* use67101 = (void*)&foo67101; +__attribute__((used)) void* use67102 = (void*)&foo67102; +__attribute__((used)) void* use67103 = (void*)&foo67103; +__attribute__((used)) void* use67104 = (void*)&foo67104; +__attribute__((used)) void* use67105 = (void*)&foo67105; +__attribute__((used)) void* use67106 = (void*)&foo67106; +__attribute__((used)) void* use67107 = (void*)&foo67107; +__attribute__((used)) void* use67108 = (void*)&foo67108; +__attribute__((used)) void* use67109 = (void*)&foo67109; +__attribute__((used)) void* use67110 = (void*)&foo67110; +__attribute__((used)) void* use67111 = (void*)&foo67111; +__attribute__((used)) void* use67112 = (void*)&foo67112; +__attribute__((used)) void* use67113 = (void*)&foo67113; +__attribute__((used)) void* use67114 = (void*)&foo67114; +__attribute__((used)) void* use67115 = (void*)&foo67115; +__attribute__((used)) void* use67116 = (void*)&foo67116; +__attribute__((used)) void* use67117 = (void*)&foo67117; +__attribute__((used)) void* use67118 = (void*)&foo67118; +__attribute__((used)) void* use67119 = (void*)&foo67119; +__attribute__((used)) void* use67120 = (void*)&foo67120; +__attribute__((used)) void* use67121 = (void*)&foo67121; +__attribute__((used)) void* use67122 = (void*)&foo67122; +__attribute__((used)) void* use67123 = (void*)&foo67123; +__attribute__((used)) void* use67124 = (void*)&foo67124; +__attribute__((used)) void* use67125 = (void*)&foo67125; +__attribute__((used)) void* use67126 = (void*)&foo67126; +__attribute__((used)) void* use67127 = (void*)&foo67127; +__attribute__((used)) void* use67128 = (void*)&foo67128; +__attribute__((used)) void* use67129 = (void*)&foo67129; +__attribute__((used)) void* use67130 = (void*)&foo67130; +__attribute__((used)) void* use67131 = (void*)&foo67131; +__attribute__((used)) void* use67132 = (void*)&foo67132; +__attribute__((used)) void* use67133 = (void*)&foo67133; +__attribute__((used)) void* use67134 = (void*)&foo67134; +__attribute__((used)) void* use67135 = (void*)&foo67135; +__attribute__((used)) void* use67136 = (void*)&foo67136; +__attribute__((used)) void* use67137 = (void*)&foo67137; +__attribute__((used)) void* use67138 = (void*)&foo67138; +__attribute__((used)) void* use67139 = (void*)&foo67139; +__attribute__((used)) void* use67140 = (void*)&foo67140; +__attribute__((used)) void* use67141 = (void*)&foo67141; +__attribute__((used)) void* use67142 = (void*)&foo67142; +__attribute__((used)) void* use67143 = (void*)&foo67143; +__attribute__((used)) void* use67144 = (void*)&foo67144; +__attribute__((used)) void* use67145 = (void*)&foo67145; +__attribute__((used)) void* use67146 = (void*)&foo67146; +__attribute__((used)) void* use67147 = (void*)&foo67147; +__attribute__((used)) void* use67148 = (void*)&foo67148; +__attribute__((used)) void* use67149 = (void*)&foo67149; +__attribute__((used)) void* use67150 = (void*)&foo67150; +__attribute__((used)) void* use67151 = (void*)&foo67151; +__attribute__((used)) void* use67152 = (void*)&foo67152; +__attribute__((used)) void* use67153 = (void*)&foo67153; +__attribute__((used)) void* use67154 = (void*)&foo67154; +__attribute__((used)) void* use67155 = (void*)&foo67155; +__attribute__((used)) void* use67156 = (void*)&foo67156; +__attribute__((used)) void* use67157 = (void*)&foo67157; +__attribute__((used)) void* use67158 = (void*)&foo67158; +__attribute__((used)) void* use67159 = (void*)&foo67159; +__attribute__((used)) void* use67160 = (void*)&foo67160; +__attribute__((used)) void* use67161 = (void*)&foo67161; +__attribute__((used)) void* use67162 = (void*)&foo67162; +__attribute__((used)) void* use67163 = (void*)&foo67163; +__attribute__((used)) void* use67164 = (void*)&foo67164; +__attribute__((used)) void* use67165 = (void*)&foo67165; +__attribute__((used)) void* use67166 = (void*)&foo67166; +__attribute__((used)) void* use67167 = (void*)&foo67167; +__attribute__((used)) void* use67168 = (void*)&foo67168; +__attribute__((used)) void* use67169 = (void*)&foo67169; +__attribute__((used)) void* use67170 = (void*)&foo67170; +__attribute__((used)) void* use67171 = (void*)&foo67171; +__attribute__((used)) void* use67172 = (void*)&foo67172; +__attribute__((used)) void* use67173 = (void*)&foo67173; +__attribute__((used)) void* use67174 = (void*)&foo67174; +__attribute__((used)) void* use67175 = (void*)&foo67175; +__attribute__((used)) void* use67176 = (void*)&foo67176; +__attribute__((used)) void* use67177 = (void*)&foo67177; +__attribute__((used)) void* use67178 = (void*)&foo67178; +__attribute__((used)) void* use67179 = (void*)&foo67179; +__attribute__((used)) void* use67180 = (void*)&foo67180; +__attribute__((used)) void* use67181 = (void*)&foo67181; +__attribute__((used)) void* use67182 = (void*)&foo67182; +__attribute__((used)) void* use67183 = (void*)&foo67183; +__attribute__((used)) void* use67184 = (void*)&foo67184; +__attribute__((used)) void* use67185 = (void*)&foo67185; +__attribute__((used)) void* use67186 = (void*)&foo67186; +__attribute__((used)) void* use67187 = (void*)&foo67187; +__attribute__((used)) void* use67188 = (void*)&foo67188; +__attribute__((used)) void* use67189 = (void*)&foo67189; +__attribute__((used)) void* use67190 = (void*)&foo67190; +__attribute__((used)) void* use67191 = (void*)&foo67191; +__attribute__((used)) void* use67192 = (void*)&foo67192; +__attribute__((used)) void* use67193 = (void*)&foo67193; +__attribute__((used)) void* use67194 = (void*)&foo67194; +__attribute__((used)) void* use67195 = (void*)&foo67195; +__attribute__((used)) void* use67196 = (void*)&foo67196; +__attribute__((used)) void* use67197 = (void*)&foo67197; +__attribute__((used)) void* use67198 = (void*)&foo67198; +__attribute__((used)) void* use67199 = (void*)&foo67199; +__attribute__((used)) void* use67200 = (void*)&foo67200; +__attribute__((used)) void* use67201 = (void*)&foo67201; +__attribute__((used)) void* use67202 = (void*)&foo67202; +__attribute__((used)) void* use67203 = (void*)&foo67203; +__attribute__((used)) void* use67204 = (void*)&foo67204; +__attribute__((used)) void* use67205 = (void*)&foo67205; +__attribute__((used)) void* use67206 = (void*)&foo67206; +__attribute__((used)) void* use67207 = (void*)&foo67207; +__attribute__((used)) void* use67208 = (void*)&foo67208; +__attribute__((used)) void* use67209 = (void*)&foo67209; +__attribute__((used)) void* use67210 = (void*)&foo67210; +__attribute__((used)) void* use67211 = (void*)&foo67211; +__attribute__((used)) void* use67212 = (void*)&foo67212; +__attribute__((used)) void* use67213 = (void*)&foo67213; +__attribute__((used)) void* use67214 = (void*)&foo67214; +__attribute__((used)) void* use67215 = (void*)&foo67215; +__attribute__((used)) void* use67216 = (void*)&foo67216; +__attribute__((used)) void* use67217 = (void*)&foo67217; +__attribute__((used)) void* use67218 = (void*)&foo67218; +__attribute__((used)) void* use67219 = (void*)&foo67219; +__attribute__((used)) void* use67220 = (void*)&foo67220; +__attribute__((used)) void* use67221 = (void*)&foo67221; +__attribute__((used)) void* use67222 = (void*)&foo67222; +__attribute__((used)) void* use67223 = (void*)&foo67223; +__attribute__((used)) void* use67224 = (void*)&foo67224; +__attribute__((used)) void* use67225 = (void*)&foo67225; +__attribute__((used)) void* use67226 = (void*)&foo67226; +__attribute__((used)) void* use67227 = (void*)&foo67227; +__attribute__((used)) void* use67228 = (void*)&foo67228; +__attribute__((used)) void* use67229 = (void*)&foo67229; +__attribute__((used)) void* use67230 = (void*)&foo67230; +__attribute__((used)) void* use67231 = (void*)&foo67231; +__attribute__((used)) void* use67232 = (void*)&foo67232; +__attribute__((used)) void* use67233 = (void*)&foo67233; +__attribute__((used)) void* use67234 = (void*)&foo67234; +__attribute__((used)) void* use67235 = (void*)&foo67235; +__attribute__((used)) void* use67236 = (void*)&foo67236; +__attribute__((used)) void* use67237 = (void*)&foo67237; +__attribute__((used)) void* use67238 = (void*)&foo67238; +__attribute__((used)) void* use67239 = (void*)&foo67239; +__attribute__((used)) void* use67240 = (void*)&foo67240; +__attribute__((used)) void* use67241 = (void*)&foo67241; +__attribute__((used)) void* use67242 = (void*)&foo67242; +__attribute__((used)) void* use67243 = (void*)&foo67243; +__attribute__((used)) void* use67244 = (void*)&foo67244; +__attribute__((used)) void* use67245 = (void*)&foo67245; +__attribute__((used)) void* use67246 = (void*)&foo67246; +__attribute__((used)) void* use67247 = (void*)&foo67247; +__attribute__((used)) void* use67248 = (void*)&foo67248; +__attribute__((used)) void* use67249 = (void*)&foo67249; +__attribute__((used)) void* use67250 = (void*)&foo67250; +__attribute__((used)) void* use67251 = (void*)&foo67251; +__attribute__((used)) void* use67252 = (void*)&foo67252; +__attribute__((used)) void* use67253 = (void*)&foo67253; +__attribute__((used)) void* use67254 = (void*)&foo67254; +__attribute__((used)) void* use67255 = (void*)&foo67255; +__attribute__((used)) void* use67256 = (void*)&foo67256; +__attribute__((used)) void* use67257 = (void*)&foo67257; +__attribute__((used)) void* use67258 = (void*)&foo67258; +__attribute__((used)) void* use67259 = (void*)&foo67259; +__attribute__((used)) void* use67260 = (void*)&foo67260; +__attribute__((used)) void* use67261 = (void*)&foo67261; +__attribute__((used)) void* use67262 = (void*)&foo67262; +__attribute__((used)) void* use67263 = (void*)&foo67263; +__attribute__((used)) void* use67264 = (void*)&foo67264; +__attribute__((used)) void* use67265 = (void*)&foo67265; +__attribute__((used)) void* use67266 = (void*)&foo67266; +__attribute__((used)) void* use67267 = (void*)&foo67267; +__attribute__((used)) void* use67268 = (void*)&foo67268; +__attribute__((used)) void* use67269 = (void*)&foo67269; +__attribute__((used)) void* use67270 = (void*)&foo67270; +__attribute__((used)) void* use67271 = (void*)&foo67271; +__attribute__((used)) void* use67272 = (void*)&foo67272; +__attribute__((used)) void* use67273 = (void*)&foo67273; +__attribute__((used)) void* use67274 = (void*)&foo67274; +__attribute__((used)) void* use67275 = (void*)&foo67275; +__attribute__((used)) void* use67276 = (void*)&foo67276; +__attribute__((used)) void* use67277 = (void*)&foo67277; +__attribute__((used)) void* use67278 = (void*)&foo67278; +__attribute__((used)) void* use67279 = (void*)&foo67279; +__attribute__((used)) void* use67280 = (void*)&foo67280; +__attribute__((used)) void* use67281 = (void*)&foo67281; +__attribute__((used)) void* use67282 = (void*)&foo67282; +__attribute__((used)) void* use67283 = (void*)&foo67283; +__attribute__((used)) void* use67284 = (void*)&foo67284; +__attribute__((used)) void* use67285 = (void*)&foo67285; +__attribute__((used)) void* use67286 = (void*)&foo67286; +__attribute__((used)) void* use67287 = (void*)&foo67287; +__attribute__((used)) void* use67288 = (void*)&foo67288; +__attribute__((used)) void* use67289 = (void*)&foo67289; +__attribute__((used)) void* use67290 = (void*)&foo67290; +__attribute__((used)) void* use67291 = (void*)&foo67291; +__attribute__((used)) void* use67292 = (void*)&foo67292; +__attribute__((used)) void* use67293 = (void*)&foo67293; +__attribute__((used)) void* use67294 = (void*)&foo67294; +__attribute__((used)) void* use67295 = (void*)&foo67295; +__attribute__((used)) void* use67296 = (void*)&foo67296; +__attribute__((used)) void* use67297 = (void*)&foo67297; +__attribute__((used)) void* use67298 = (void*)&foo67298; +__attribute__((used)) void* use67299 = (void*)&foo67299; +__attribute__((used)) void* use67300 = (void*)&foo67300; +__attribute__((used)) void* use67301 = (void*)&foo67301; +__attribute__((used)) void* use67302 = (void*)&foo67302; +__attribute__((used)) void* use67303 = (void*)&foo67303; +__attribute__((used)) void* use67304 = (void*)&foo67304; +__attribute__((used)) void* use67305 = (void*)&foo67305; +__attribute__((used)) void* use67306 = (void*)&foo67306; +__attribute__((used)) void* use67307 = (void*)&foo67307; +__attribute__((used)) void* use67308 = (void*)&foo67308; +__attribute__((used)) void* use67309 = (void*)&foo67309; +__attribute__((used)) void* use67310 = (void*)&foo67310; +__attribute__((used)) void* use67311 = (void*)&foo67311; +__attribute__((used)) void* use67312 = (void*)&foo67312; +__attribute__((used)) void* use67313 = (void*)&foo67313; +__attribute__((used)) void* use67314 = (void*)&foo67314; +__attribute__((used)) void* use67315 = (void*)&foo67315; +__attribute__((used)) void* use67316 = (void*)&foo67316; +__attribute__((used)) void* use67317 = (void*)&foo67317; +__attribute__((used)) void* use67318 = (void*)&foo67318; +__attribute__((used)) void* use67319 = (void*)&foo67319; +__attribute__((used)) void* use67320 = (void*)&foo67320; +__attribute__((used)) void* use67321 = (void*)&foo67321; +__attribute__((used)) void* use67322 = (void*)&foo67322; +__attribute__((used)) void* use67323 = (void*)&foo67323; +__attribute__((used)) void* use67324 = (void*)&foo67324; +__attribute__((used)) void* use67325 = (void*)&foo67325; +__attribute__((used)) void* use67326 = (void*)&foo67326; +__attribute__((used)) void* use67327 = (void*)&foo67327; +__attribute__((used)) void* use67328 = (void*)&foo67328; +__attribute__((used)) void* use67329 = (void*)&foo67329; +__attribute__((used)) void* use67330 = (void*)&foo67330; +__attribute__((used)) void* use67331 = (void*)&foo67331; +__attribute__((used)) void* use67332 = (void*)&foo67332; +__attribute__((used)) void* use67333 = (void*)&foo67333; +__attribute__((used)) void* use67334 = (void*)&foo67334; +__attribute__((used)) void* use67335 = (void*)&foo67335; +__attribute__((used)) void* use67336 = (void*)&foo67336; +__attribute__((used)) void* use67337 = (void*)&foo67337; +__attribute__((used)) void* use67338 = (void*)&foo67338; +__attribute__((used)) void* use67339 = (void*)&foo67339; +__attribute__((used)) void* use67340 = (void*)&foo67340; +__attribute__((used)) void* use67341 = (void*)&foo67341; +__attribute__((used)) void* use67342 = (void*)&foo67342; +__attribute__((used)) void* use67343 = (void*)&foo67343; +__attribute__((used)) void* use67344 = (void*)&foo67344; +__attribute__((used)) void* use67345 = (void*)&foo67345; +__attribute__((used)) void* use67346 = (void*)&foo67346; +__attribute__((used)) void* use67347 = (void*)&foo67347; +__attribute__((used)) void* use67348 = (void*)&foo67348; +__attribute__((used)) void* use67349 = (void*)&foo67349; +__attribute__((used)) void* use67350 = (void*)&foo67350; +__attribute__((used)) void* use67351 = (void*)&foo67351; +__attribute__((used)) void* use67352 = (void*)&foo67352; +__attribute__((used)) void* use67353 = (void*)&foo67353; +__attribute__((used)) void* use67354 = (void*)&foo67354; +__attribute__((used)) void* use67355 = (void*)&foo67355; +__attribute__((used)) void* use67356 = (void*)&foo67356; +__attribute__((used)) void* use67357 = (void*)&foo67357; +__attribute__((used)) void* use67358 = (void*)&foo67358; +__attribute__((used)) void* use67359 = (void*)&foo67359; +__attribute__((used)) void* use67360 = (void*)&foo67360; +__attribute__((used)) void* use67361 = (void*)&foo67361; +__attribute__((used)) void* use67362 = (void*)&foo67362; +__attribute__((used)) void* use67363 = (void*)&foo67363; +__attribute__((used)) void* use67364 = (void*)&foo67364; +__attribute__((used)) void* use67365 = (void*)&foo67365; +__attribute__((used)) void* use67366 = (void*)&foo67366; +__attribute__((used)) void* use67367 = (void*)&foo67367; +__attribute__((used)) void* use67368 = (void*)&foo67368; +__attribute__((used)) void* use67369 = (void*)&foo67369; +__attribute__((used)) void* use67370 = (void*)&foo67370; +__attribute__((used)) void* use67371 = (void*)&foo67371; +__attribute__((used)) void* use67372 = (void*)&foo67372; +__attribute__((used)) void* use67373 = (void*)&foo67373; +__attribute__((used)) void* use67374 = (void*)&foo67374; +__attribute__((used)) void* use67375 = (void*)&foo67375; +__attribute__((used)) void* use67376 = (void*)&foo67376; +__attribute__((used)) void* use67377 = (void*)&foo67377; +__attribute__((used)) void* use67378 = (void*)&foo67378; +__attribute__((used)) void* use67379 = (void*)&foo67379; +__attribute__((used)) void* use67380 = (void*)&foo67380; +__attribute__((used)) void* use67381 = (void*)&foo67381; +__attribute__((used)) void* use67382 = (void*)&foo67382; +__attribute__((used)) void* use67383 = (void*)&foo67383; +__attribute__((used)) void* use67384 = (void*)&foo67384; +__attribute__((used)) void* use67385 = (void*)&foo67385; +__attribute__((used)) void* use67386 = (void*)&foo67386; +__attribute__((used)) void* use67387 = (void*)&foo67387; +__attribute__((used)) void* use67388 = (void*)&foo67388; +__attribute__((used)) void* use67389 = (void*)&foo67389; +__attribute__((used)) void* use67390 = (void*)&foo67390; +__attribute__((used)) void* use67391 = (void*)&foo67391; +__attribute__((used)) void* use67392 = (void*)&foo67392; +__attribute__((used)) void* use67393 = (void*)&foo67393; +__attribute__((used)) void* use67394 = (void*)&foo67394; +__attribute__((used)) void* use67395 = (void*)&foo67395; +__attribute__((used)) void* use67396 = (void*)&foo67396; +__attribute__((used)) void* use67397 = (void*)&foo67397; +__attribute__((used)) void* use67398 = (void*)&foo67398; +__attribute__((used)) void* use67399 = (void*)&foo67399; +__attribute__((used)) void* use67400 = (void*)&foo67400; +__attribute__((used)) void* use67401 = (void*)&foo67401; +__attribute__((used)) void* use67402 = (void*)&foo67402; +__attribute__((used)) void* use67403 = (void*)&foo67403; +__attribute__((used)) void* use67404 = (void*)&foo67404; +__attribute__((used)) void* use67405 = (void*)&foo67405; +__attribute__((used)) void* use67406 = (void*)&foo67406; +__attribute__((used)) void* use67407 = (void*)&foo67407; +__attribute__((used)) void* use67408 = (void*)&foo67408; +__attribute__((used)) void* use67409 = (void*)&foo67409; +__attribute__((used)) void* use67410 = (void*)&foo67410; +__attribute__((used)) void* use67411 = (void*)&foo67411; +__attribute__((used)) void* use67412 = (void*)&foo67412; +__attribute__((used)) void* use67413 = (void*)&foo67413; +__attribute__((used)) void* use67414 = (void*)&foo67414; +__attribute__((used)) void* use67415 = (void*)&foo67415; +__attribute__((used)) void* use67416 = (void*)&foo67416; +__attribute__((used)) void* use67417 = (void*)&foo67417; +__attribute__((used)) void* use67418 = (void*)&foo67418; +__attribute__((used)) void* use67419 = (void*)&foo67419; +__attribute__((used)) void* use67420 = (void*)&foo67420; +__attribute__((used)) void* use67421 = (void*)&foo67421; +__attribute__((used)) void* use67422 = (void*)&foo67422; +__attribute__((used)) void* use67423 = (void*)&foo67423; +__attribute__((used)) void* use67424 = (void*)&foo67424; +__attribute__((used)) void* use67425 = (void*)&foo67425; +__attribute__((used)) void* use67426 = (void*)&foo67426; +__attribute__((used)) void* use67427 = (void*)&foo67427; +__attribute__((used)) void* use67428 = (void*)&foo67428; +__attribute__((used)) void* use67429 = (void*)&foo67429; +__attribute__((used)) void* use67430 = (void*)&foo67430; +__attribute__((used)) void* use67431 = (void*)&foo67431; +__attribute__((used)) void* use67432 = (void*)&foo67432; +__attribute__((used)) void* use67433 = (void*)&foo67433; +__attribute__((used)) void* use67434 = (void*)&foo67434; +__attribute__((used)) void* use67435 = (void*)&foo67435; +__attribute__((used)) void* use67436 = (void*)&foo67436; +__attribute__((used)) void* use67437 = (void*)&foo67437; +__attribute__((used)) void* use67438 = (void*)&foo67438; +__attribute__((used)) void* use67439 = (void*)&foo67439; +__attribute__((used)) void* use67440 = (void*)&foo67440; +__attribute__((used)) void* use67441 = (void*)&foo67441; +__attribute__((used)) void* use67442 = (void*)&foo67442; +__attribute__((used)) void* use67443 = (void*)&foo67443; +__attribute__((used)) void* use67444 = (void*)&foo67444; +__attribute__((used)) void* use67445 = (void*)&foo67445; +__attribute__((used)) void* use67446 = (void*)&foo67446; +__attribute__((used)) void* use67447 = (void*)&foo67447; +__attribute__((used)) void* use67448 = (void*)&foo67448; +__attribute__((used)) void* use67449 = (void*)&foo67449; +__attribute__((used)) void* use67450 = (void*)&foo67450; +__attribute__((used)) void* use67451 = (void*)&foo67451; +__attribute__((used)) void* use67452 = (void*)&foo67452; +__attribute__((used)) void* use67453 = (void*)&foo67453; +__attribute__((used)) void* use67454 = (void*)&foo67454; +__attribute__((used)) void* use67455 = (void*)&foo67455; +__attribute__((used)) void* use67456 = (void*)&foo67456; +__attribute__((used)) void* use67457 = (void*)&foo67457; +__attribute__((used)) void* use67458 = (void*)&foo67458; +__attribute__((used)) void* use67459 = (void*)&foo67459; +__attribute__((used)) void* use67460 = (void*)&foo67460; +__attribute__((used)) void* use67461 = (void*)&foo67461; +__attribute__((used)) void* use67462 = (void*)&foo67462; +__attribute__((used)) void* use67463 = (void*)&foo67463; +__attribute__((used)) void* use67464 = (void*)&foo67464; +__attribute__((used)) void* use67465 = (void*)&foo67465; +__attribute__((used)) void* use67466 = (void*)&foo67466; +__attribute__((used)) void* use67467 = (void*)&foo67467; +__attribute__((used)) void* use67468 = (void*)&foo67468; +__attribute__((used)) void* use67469 = (void*)&foo67469; +__attribute__((used)) void* use67470 = (void*)&foo67470; +__attribute__((used)) void* use67471 = (void*)&foo67471; +__attribute__((used)) void* use67472 = (void*)&foo67472; +__attribute__((used)) void* use67473 = (void*)&foo67473; +__attribute__((used)) void* use67474 = (void*)&foo67474; +__attribute__((used)) void* use67475 = (void*)&foo67475; +__attribute__((used)) void* use67476 = (void*)&foo67476; +__attribute__((used)) void* use67477 = (void*)&foo67477; +__attribute__((used)) void* use67478 = (void*)&foo67478; +__attribute__((used)) void* use67479 = (void*)&foo67479; +__attribute__((used)) void* use67480 = (void*)&foo67480; +__attribute__((used)) void* use67481 = (void*)&foo67481; +__attribute__((used)) void* use67482 = (void*)&foo67482; +__attribute__((used)) void* use67483 = (void*)&foo67483; +__attribute__((used)) void* use67484 = (void*)&foo67484; +__attribute__((used)) void* use67485 = (void*)&foo67485; +__attribute__((used)) void* use67486 = (void*)&foo67486; +__attribute__((used)) void* use67487 = (void*)&foo67487; +__attribute__((used)) void* use67488 = (void*)&foo67488; +__attribute__((used)) void* use67489 = (void*)&foo67489; +__attribute__((used)) void* use67490 = (void*)&foo67490; +__attribute__((used)) void* use67491 = (void*)&foo67491; +__attribute__((used)) void* use67492 = (void*)&foo67492; +__attribute__((used)) void* use67493 = (void*)&foo67493; +__attribute__((used)) void* use67494 = (void*)&foo67494; +__attribute__((used)) void* use67495 = (void*)&foo67495; +__attribute__((used)) void* use67496 = (void*)&foo67496; +__attribute__((used)) void* use67497 = (void*)&foo67497; +__attribute__((used)) void* use67498 = (void*)&foo67498; +__attribute__((used)) void* use67499 = (void*)&foo67499; +__attribute__((used)) void* use67500 = (void*)&foo67500; +__attribute__((used)) void* use67501 = (void*)&foo67501; +__attribute__((used)) void* use67502 = (void*)&foo67502; +__attribute__((used)) void* use67503 = (void*)&foo67503; +__attribute__((used)) void* use67504 = (void*)&foo67504; +__attribute__((used)) void* use67505 = (void*)&foo67505; +__attribute__((used)) void* use67506 = (void*)&foo67506; +__attribute__((used)) void* use67507 = (void*)&foo67507; +__attribute__((used)) void* use67508 = (void*)&foo67508; +__attribute__((used)) void* use67509 = (void*)&foo67509; +__attribute__((used)) void* use67510 = (void*)&foo67510; +__attribute__((used)) void* use67511 = (void*)&foo67511; +__attribute__((used)) void* use67512 = (void*)&foo67512; +__attribute__((used)) void* use67513 = (void*)&foo67513; +__attribute__((used)) void* use67514 = (void*)&foo67514; +__attribute__((used)) void* use67515 = (void*)&foo67515; +__attribute__((used)) void* use67516 = (void*)&foo67516; +__attribute__((used)) void* use67517 = (void*)&foo67517; +__attribute__((used)) void* use67518 = (void*)&foo67518; +__attribute__((used)) void* use67519 = (void*)&foo67519; +__attribute__((used)) void* use67520 = (void*)&foo67520; +__attribute__((used)) void* use67521 = (void*)&foo67521; +__attribute__((used)) void* use67522 = (void*)&foo67522; +__attribute__((used)) void* use67523 = (void*)&foo67523; +__attribute__((used)) void* use67524 = (void*)&foo67524; +__attribute__((used)) void* use67525 = (void*)&foo67525; +__attribute__((used)) void* use67526 = (void*)&foo67526; +__attribute__((used)) void* use67527 = (void*)&foo67527; +__attribute__((used)) void* use67528 = (void*)&foo67528; +__attribute__((used)) void* use67529 = (void*)&foo67529; +__attribute__((used)) void* use67530 = (void*)&foo67530; +__attribute__((used)) void* use67531 = (void*)&foo67531; +__attribute__((used)) void* use67532 = (void*)&foo67532; +__attribute__((used)) void* use67533 = (void*)&foo67533; +__attribute__((used)) void* use67534 = (void*)&foo67534; +__attribute__((used)) void* use67535 = (void*)&foo67535; +__attribute__((used)) void* use67536 = (void*)&foo67536; +__attribute__((used)) void* use67537 = (void*)&foo67537; +__attribute__((used)) void* use67538 = (void*)&foo67538; +__attribute__((used)) void* use67539 = (void*)&foo67539; +__attribute__((used)) void* use67540 = (void*)&foo67540; +__attribute__((used)) void* use67541 = (void*)&foo67541; +__attribute__((used)) void* use67542 = (void*)&foo67542; +__attribute__((used)) void* use67543 = (void*)&foo67543; +__attribute__((used)) void* use67544 = (void*)&foo67544; +__attribute__((used)) void* use67545 = (void*)&foo67545; +__attribute__((used)) void* use67546 = (void*)&foo67546; +__attribute__((used)) void* use67547 = (void*)&foo67547; +__attribute__((used)) void* use67548 = (void*)&foo67548; +__attribute__((used)) void* use67549 = (void*)&foo67549; +__attribute__((used)) void* use67550 = (void*)&foo67550; +__attribute__((used)) void* use67551 = (void*)&foo67551; +__attribute__((used)) void* use67552 = (void*)&foo67552; +__attribute__((used)) void* use67553 = (void*)&foo67553; +__attribute__((used)) void* use67554 = (void*)&foo67554; +__attribute__((used)) void* use67555 = (void*)&foo67555; +__attribute__((used)) void* use67556 = (void*)&foo67556; +__attribute__((used)) void* use67557 = (void*)&foo67557; +__attribute__((used)) void* use67558 = (void*)&foo67558; +__attribute__((used)) void* use67559 = (void*)&foo67559; +__attribute__((used)) void* use67560 = (void*)&foo67560; +__attribute__((used)) void* use67561 = (void*)&foo67561; +__attribute__((used)) void* use67562 = (void*)&foo67562; +__attribute__((used)) void* use67563 = (void*)&foo67563; +__attribute__((used)) void* use67564 = (void*)&foo67564; +__attribute__((used)) void* use67565 = (void*)&foo67565; +__attribute__((used)) void* use67566 = (void*)&foo67566; +__attribute__((used)) void* use67567 = (void*)&foo67567; +__attribute__((used)) void* use67568 = (void*)&foo67568; +__attribute__((used)) void* use67569 = (void*)&foo67569; +__attribute__((used)) void* use67570 = (void*)&foo67570; +__attribute__((used)) void* use67571 = (void*)&foo67571; +__attribute__((used)) void* use67572 = (void*)&foo67572; +__attribute__((used)) void* use67573 = (void*)&foo67573; +__attribute__((used)) void* use67574 = (void*)&foo67574; +__attribute__((used)) void* use67575 = (void*)&foo67575; +__attribute__((used)) void* use67576 = (void*)&foo67576; +__attribute__((used)) void* use67577 = (void*)&foo67577; +__attribute__((used)) void* use67578 = (void*)&foo67578; +__attribute__((used)) void* use67579 = (void*)&foo67579; +__attribute__((used)) void* use67580 = (void*)&foo67580; +__attribute__((used)) void* use67581 = (void*)&foo67581; +__attribute__((used)) void* use67582 = (void*)&foo67582; +__attribute__((used)) void* use67583 = (void*)&foo67583; +__attribute__((used)) void* use67584 = (void*)&foo67584; +__attribute__((used)) void* use67585 = (void*)&foo67585; +__attribute__((used)) void* use67586 = (void*)&foo67586; +__attribute__((used)) void* use67587 = (void*)&foo67587; +__attribute__((used)) void* use67588 = (void*)&foo67588; +__attribute__((used)) void* use67589 = (void*)&foo67589; +__attribute__((used)) void* use67590 = (void*)&foo67590; +__attribute__((used)) void* use67591 = (void*)&foo67591; +__attribute__((used)) void* use67592 = (void*)&foo67592; +__attribute__((used)) void* use67593 = (void*)&foo67593; +__attribute__((used)) void* use67594 = (void*)&foo67594; +__attribute__((used)) void* use67595 = (void*)&foo67595; +__attribute__((used)) void* use67596 = (void*)&foo67596; +__attribute__((used)) void* use67597 = (void*)&foo67597; +__attribute__((used)) void* use67598 = (void*)&foo67598; +__attribute__((used)) void* use67599 = (void*)&foo67599; +__attribute__((used)) void* use67600 = (void*)&foo67600; +__attribute__((used)) void* use67601 = (void*)&foo67601; +__attribute__((used)) void* use67602 = (void*)&foo67602; +__attribute__((used)) void* use67603 = (void*)&foo67603; +__attribute__((used)) void* use67604 = (void*)&foo67604; +__attribute__((used)) void* use67605 = (void*)&foo67605; +__attribute__((used)) void* use67606 = (void*)&foo67606; +__attribute__((used)) void* use67607 = (void*)&foo67607; +__attribute__((used)) void* use67608 = (void*)&foo67608; +__attribute__((used)) void* use67609 = (void*)&foo67609; +__attribute__((used)) void* use67610 = (void*)&foo67610; +__attribute__((used)) void* use67611 = (void*)&foo67611; +__attribute__((used)) void* use67612 = (void*)&foo67612; +__attribute__((used)) void* use67613 = (void*)&foo67613; +__attribute__((used)) void* use67614 = (void*)&foo67614; +__attribute__((used)) void* use67615 = (void*)&foo67615; +__attribute__((used)) void* use67616 = (void*)&foo67616; +__attribute__((used)) void* use67617 = (void*)&foo67617; +__attribute__((used)) void* use67618 = (void*)&foo67618; +__attribute__((used)) void* use67619 = (void*)&foo67619; +__attribute__((used)) void* use67620 = (void*)&foo67620; +__attribute__((used)) void* use67621 = (void*)&foo67621; +__attribute__((used)) void* use67622 = (void*)&foo67622; +__attribute__((used)) void* use67623 = (void*)&foo67623; +__attribute__((used)) void* use67624 = (void*)&foo67624; +__attribute__((used)) void* use67625 = (void*)&foo67625; +__attribute__((used)) void* use67626 = (void*)&foo67626; +__attribute__((used)) void* use67627 = (void*)&foo67627; +__attribute__((used)) void* use67628 = (void*)&foo67628; +__attribute__((used)) void* use67629 = (void*)&foo67629; +__attribute__((used)) void* use67630 = (void*)&foo67630; +__attribute__((used)) void* use67631 = (void*)&foo67631; +__attribute__((used)) void* use67632 = (void*)&foo67632; +__attribute__((used)) void* use67633 = (void*)&foo67633; +__attribute__((used)) void* use67634 = (void*)&foo67634; +__attribute__((used)) void* use67635 = (void*)&foo67635; +__attribute__((used)) void* use67636 = (void*)&foo67636; +__attribute__((used)) void* use67637 = (void*)&foo67637; +__attribute__((used)) void* use67638 = (void*)&foo67638; +__attribute__((used)) void* use67639 = (void*)&foo67639; +__attribute__((used)) void* use67640 = (void*)&foo67640; +__attribute__((used)) void* use67641 = (void*)&foo67641; +__attribute__((used)) void* use67642 = (void*)&foo67642; +__attribute__((used)) void* use67643 = (void*)&foo67643; +__attribute__((used)) void* use67644 = (void*)&foo67644; +__attribute__((used)) void* use67645 = (void*)&foo67645; +__attribute__((used)) void* use67646 = (void*)&foo67646; +__attribute__((used)) void* use67647 = (void*)&foo67647; +__attribute__((used)) void* use67648 = (void*)&foo67648; +__attribute__((used)) void* use67649 = (void*)&foo67649; +__attribute__((used)) void* use67650 = (void*)&foo67650; +__attribute__((used)) void* use67651 = (void*)&foo67651; +__attribute__((used)) void* use67652 = (void*)&foo67652; +__attribute__((used)) void* use67653 = (void*)&foo67653; +__attribute__((used)) void* use67654 = (void*)&foo67654; +__attribute__((used)) void* use67655 = (void*)&foo67655; +__attribute__((used)) void* use67656 = (void*)&foo67656; +__attribute__((used)) void* use67657 = (void*)&foo67657; +__attribute__((used)) void* use67658 = (void*)&foo67658; +__attribute__((used)) void* use67659 = (void*)&foo67659; +__attribute__((used)) void* use67660 = (void*)&foo67660; +__attribute__((used)) void* use67661 = (void*)&foo67661; +__attribute__((used)) void* use67662 = (void*)&foo67662; +__attribute__((used)) void* use67663 = (void*)&foo67663; +__attribute__((used)) void* use67664 = (void*)&foo67664; +__attribute__((used)) void* use67665 = (void*)&foo67665; +__attribute__((used)) void* use67666 = (void*)&foo67666; +__attribute__((used)) void* use67667 = (void*)&foo67667; +__attribute__((used)) void* use67668 = (void*)&foo67668; +__attribute__((used)) void* use67669 = (void*)&foo67669; +__attribute__((used)) void* use67670 = (void*)&foo67670; +__attribute__((used)) void* use67671 = (void*)&foo67671; +__attribute__((used)) void* use67672 = (void*)&foo67672; +__attribute__((used)) void* use67673 = (void*)&foo67673; +__attribute__((used)) void* use67674 = (void*)&foo67674; +__attribute__((used)) void* use67675 = (void*)&foo67675; +__attribute__((used)) void* use67676 = (void*)&foo67676; +__attribute__((used)) void* use67677 = (void*)&foo67677; +__attribute__((used)) void* use67678 = (void*)&foo67678; +__attribute__((used)) void* use67679 = (void*)&foo67679; +__attribute__((used)) void* use67680 = (void*)&foo67680; +__attribute__((used)) void* use67681 = (void*)&foo67681; +__attribute__((used)) void* use67682 = (void*)&foo67682; +__attribute__((used)) void* use67683 = (void*)&foo67683; +__attribute__((used)) void* use67684 = (void*)&foo67684; +__attribute__((used)) void* use67685 = (void*)&foo67685; +__attribute__((used)) void* use67686 = (void*)&foo67686; +__attribute__((used)) void* use67687 = (void*)&foo67687; +__attribute__((used)) void* use67688 = (void*)&foo67688; +__attribute__((used)) void* use67689 = (void*)&foo67689; +__attribute__((used)) void* use67690 = (void*)&foo67690; +__attribute__((used)) void* use67691 = (void*)&foo67691; +__attribute__((used)) void* use67692 = (void*)&foo67692; +__attribute__((used)) void* use67693 = (void*)&foo67693; +__attribute__((used)) void* use67694 = (void*)&foo67694; +__attribute__((used)) void* use67695 = (void*)&foo67695; +__attribute__((used)) void* use67696 = (void*)&foo67696; +__attribute__((used)) void* use67697 = (void*)&foo67697; +__attribute__((used)) void* use67698 = (void*)&foo67698; +__attribute__((used)) void* use67699 = (void*)&foo67699; +__attribute__((used)) void* use67700 = (void*)&foo67700; +__attribute__((used)) void* use67701 = (void*)&foo67701; +__attribute__((used)) void* use67702 = (void*)&foo67702; +__attribute__((used)) void* use67703 = (void*)&foo67703; +__attribute__((used)) void* use67704 = (void*)&foo67704; +__attribute__((used)) void* use67705 = (void*)&foo67705; +__attribute__((used)) void* use67706 = (void*)&foo67706; +__attribute__((used)) void* use67707 = (void*)&foo67707; +__attribute__((used)) void* use67708 = (void*)&foo67708; +__attribute__((used)) void* use67709 = (void*)&foo67709; +__attribute__((used)) void* use67710 = (void*)&foo67710; +__attribute__((used)) void* use67711 = (void*)&foo67711; +__attribute__((used)) void* use67712 = (void*)&foo67712; +__attribute__((used)) void* use67713 = (void*)&foo67713; +__attribute__((used)) void* use67714 = (void*)&foo67714; +__attribute__((used)) void* use67715 = (void*)&foo67715; +__attribute__((used)) void* use67716 = (void*)&foo67716; +__attribute__((used)) void* use67717 = (void*)&foo67717; +__attribute__((used)) void* use67718 = (void*)&foo67718; +__attribute__((used)) void* use67719 = (void*)&foo67719; +__attribute__((used)) void* use67720 = (void*)&foo67720; +__attribute__((used)) void* use67721 = (void*)&foo67721; +__attribute__((used)) void* use67722 = (void*)&foo67722; +__attribute__((used)) void* use67723 = (void*)&foo67723; +__attribute__((used)) void* use67724 = (void*)&foo67724; +__attribute__((used)) void* use67725 = (void*)&foo67725; +__attribute__((used)) void* use67726 = (void*)&foo67726; +__attribute__((used)) void* use67727 = (void*)&foo67727; +__attribute__((used)) void* use67728 = (void*)&foo67728; +__attribute__((used)) void* use67729 = (void*)&foo67729; +__attribute__((used)) void* use67730 = (void*)&foo67730; +__attribute__((used)) void* use67731 = (void*)&foo67731; +__attribute__((used)) void* use67732 = (void*)&foo67732; +__attribute__((used)) void* use67733 = (void*)&foo67733; +__attribute__((used)) void* use67734 = (void*)&foo67734; +__attribute__((used)) void* use67735 = (void*)&foo67735; +__attribute__((used)) void* use67736 = (void*)&foo67736; +__attribute__((used)) void* use67737 = (void*)&foo67737; +__attribute__((used)) void* use67738 = (void*)&foo67738; +__attribute__((used)) void* use67739 = (void*)&foo67739; +__attribute__((used)) void* use67740 = (void*)&foo67740; +__attribute__((used)) void* use67741 = (void*)&foo67741; +__attribute__((used)) void* use67742 = (void*)&foo67742; +__attribute__((used)) void* use67743 = (void*)&foo67743; +__attribute__((used)) void* use67744 = (void*)&foo67744; +__attribute__((used)) void* use67745 = (void*)&foo67745; +__attribute__((used)) void* use67746 = (void*)&foo67746; +__attribute__((used)) void* use67747 = (void*)&foo67747; +__attribute__((used)) void* use67748 = (void*)&foo67748; +__attribute__((used)) void* use67749 = (void*)&foo67749; +__attribute__((used)) void* use67750 = (void*)&foo67750; +__attribute__((used)) void* use67751 = (void*)&foo67751; +__attribute__((used)) void* use67752 = (void*)&foo67752; +__attribute__((used)) void* use67753 = (void*)&foo67753; +__attribute__((used)) void* use67754 = (void*)&foo67754; +__attribute__((used)) void* use67755 = (void*)&foo67755; +__attribute__((used)) void* use67756 = (void*)&foo67756; +__attribute__((used)) void* use67757 = (void*)&foo67757; +__attribute__((used)) void* use67758 = (void*)&foo67758; +__attribute__((used)) void* use67759 = (void*)&foo67759; +__attribute__((used)) void* use67760 = (void*)&foo67760; +__attribute__((used)) void* use67761 = (void*)&foo67761; +__attribute__((used)) void* use67762 = (void*)&foo67762; +__attribute__((used)) void* use67763 = (void*)&foo67763; +__attribute__((used)) void* use67764 = (void*)&foo67764; +__attribute__((used)) void* use67765 = (void*)&foo67765; +__attribute__((used)) void* use67766 = (void*)&foo67766; +__attribute__((used)) void* use67767 = (void*)&foo67767; +__attribute__((used)) void* use67768 = (void*)&foo67768; +__attribute__((used)) void* use67769 = (void*)&foo67769; +__attribute__((used)) void* use67770 = (void*)&foo67770; +__attribute__((used)) void* use67771 = (void*)&foo67771; +__attribute__((used)) void* use67772 = (void*)&foo67772; +__attribute__((used)) void* use67773 = (void*)&foo67773; +__attribute__((used)) void* use67774 = (void*)&foo67774; +__attribute__((used)) void* use67775 = (void*)&foo67775; +__attribute__((used)) void* use67776 = (void*)&foo67776; +__attribute__((used)) void* use67777 = (void*)&foo67777; +__attribute__((used)) void* use67778 = (void*)&foo67778; +__attribute__((used)) void* use67779 = (void*)&foo67779; +__attribute__((used)) void* use67780 = (void*)&foo67780; +__attribute__((used)) void* use67781 = (void*)&foo67781; +__attribute__((used)) void* use67782 = (void*)&foo67782; +__attribute__((used)) void* use67783 = (void*)&foo67783; +__attribute__((used)) void* use67784 = (void*)&foo67784; +__attribute__((used)) void* use67785 = (void*)&foo67785; +__attribute__((used)) void* use67786 = (void*)&foo67786; +__attribute__((used)) void* use67787 = (void*)&foo67787; +__attribute__((used)) void* use67788 = (void*)&foo67788; +__attribute__((used)) void* use67789 = (void*)&foo67789; +__attribute__((used)) void* use67790 = (void*)&foo67790; +__attribute__((used)) void* use67791 = (void*)&foo67791; +__attribute__((used)) void* use67792 = (void*)&foo67792; +__attribute__((used)) void* use67793 = (void*)&foo67793; +__attribute__((used)) void* use67794 = (void*)&foo67794; +__attribute__((used)) void* use67795 = (void*)&foo67795; +__attribute__((used)) void* use67796 = (void*)&foo67796; +__attribute__((used)) void* use67797 = (void*)&foo67797; +__attribute__((used)) void* use67798 = (void*)&foo67798; +__attribute__((used)) void* use67799 = (void*)&foo67799; +__attribute__((used)) void* use67800 = (void*)&foo67800; +__attribute__((used)) void* use67801 = (void*)&foo67801; +__attribute__((used)) void* use67802 = (void*)&foo67802; +__attribute__((used)) void* use67803 = (void*)&foo67803; +__attribute__((used)) void* use67804 = (void*)&foo67804; +__attribute__((used)) void* use67805 = (void*)&foo67805; +__attribute__((used)) void* use67806 = (void*)&foo67806; +__attribute__((used)) void* use67807 = (void*)&foo67807; +__attribute__((used)) void* use67808 = (void*)&foo67808; +__attribute__((used)) void* use67809 = (void*)&foo67809; +__attribute__((used)) void* use67810 = (void*)&foo67810; +__attribute__((used)) void* use67811 = (void*)&foo67811; +__attribute__((used)) void* use67812 = (void*)&foo67812; +__attribute__((used)) void* use67813 = (void*)&foo67813; +__attribute__((used)) void* use67814 = (void*)&foo67814; +__attribute__((used)) void* use67815 = (void*)&foo67815; +__attribute__((used)) void* use67816 = (void*)&foo67816; +__attribute__((used)) void* use67817 = (void*)&foo67817; +__attribute__((used)) void* use67818 = (void*)&foo67818; +__attribute__((used)) void* use67819 = (void*)&foo67819; +__attribute__((used)) void* use67820 = (void*)&foo67820; +__attribute__((used)) void* use67821 = (void*)&foo67821; +__attribute__((used)) void* use67822 = (void*)&foo67822; +__attribute__((used)) void* use67823 = (void*)&foo67823; +__attribute__((used)) void* use67824 = (void*)&foo67824; +__attribute__((used)) void* use67825 = (void*)&foo67825; +__attribute__((used)) void* use67826 = (void*)&foo67826; +__attribute__((used)) void* use67827 = (void*)&foo67827; +__attribute__((used)) void* use67828 = (void*)&foo67828; +__attribute__((used)) void* use67829 = (void*)&foo67829; +__attribute__((used)) void* use67830 = (void*)&foo67830; +__attribute__((used)) void* use67831 = (void*)&foo67831; +__attribute__((used)) void* use67832 = (void*)&foo67832; +__attribute__((used)) void* use67833 = (void*)&foo67833; +__attribute__((used)) void* use67834 = (void*)&foo67834; +__attribute__((used)) void* use67835 = (void*)&foo67835; +__attribute__((used)) void* use67836 = (void*)&foo67836; +__attribute__((used)) void* use67837 = (void*)&foo67837; +__attribute__((used)) void* use67838 = (void*)&foo67838; +__attribute__((used)) void* use67839 = (void*)&foo67839; +__attribute__((used)) void* use67840 = (void*)&foo67840; +__attribute__((used)) void* use67841 = (void*)&foo67841; +__attribute__((used)) void* use67842 = (void*)&foo67842; +__attribute__((used)) void* use67843 = (void*)&foo67843; +__attribute__((used)) void* use67844 = (void*)&foo67844; +__attribute__((used)) void* use67845 = (void*)&foo67845; +__attribute__((used)) void* use67846 = (void*)&foo67846; +__attribute__((used)) void* use67847 = (void*)&foo67847; +__attribute__((used)) void* use67848 = (void*)&foo67848; +__attribute__((used)) void* use67849 = (void*)&foo67849; +__attribute__((used)) void* use67850 = (void*)&foo67850; +__attribute__((used)) void* use67851 = (void*)&foo67851; +__attribute__((used)) void* use67852 = (void*)&foo67852; +__attribute__((used)) void* use67853 = (void*)&foo67853; +__attribute__((used)) void* use67854 = (void*)&foo67854; +__attribute__((used)) void* use67855 = (void*)&foo67855; +__attribute__((used)) void* use67856 = (void*)&foo67856; +__attribute__((used)) void* use67857 = (void*)&foo67857; +__attribute__((used)) void* use67858 = (void*)&foo67858; +__attribute__((used)) void* use67859 = (void*)&foo67859; +__attribute__((used)) void* use67860 = (void*)&foo67860; +__attribute__((used)) void* use67861 = (void*)&foo67861; +__attribute__((used)) void* use67862 = (void*)&foo67862; +__attribute__((used)) void* use67863 = (void*)&foo67863; +__attribute__((used)) void* use67864 = (void*)&foo67864; +__attribute__((used)) void* use67865 = (void*)&foo67865; +__attribute__((used)) void* use67866 = (void*)&foo67866; +__attribute__((used)) void* use67867 = (void*)&foo67867; +__attribute__((used)) void* use67868 = (void*)&foo67868; +__attribute__((used)) void* use67869 = (void*)&foo67869; +__attribute__((used)) void* use67870 = (void*)&foo67870; +__attribute__((used)) void* use67871 = (void*)&foo67871; +__attribute__((used)) void* use67872 = (void*)&foo67872; +__attribute__((used)) void* use67873 = (void*)&foo67873; +__attribute__((used)) void* use67874 = (void*)&foo67874; +__attribute__((used)) void* use67875 = (void*)&foo67875; +__attribute__((used)) void* use67876 = (void*)&foo67876; +__attribute__((used)) void* use67877 = (void*)&foo67877; +__attribute__((used)) void* use67878 = (void*)&foo67878; +__attribute__((used)) void* use67879 = (void*)&foo67879; +__attribute__((used)) void* use67880 = (void*)&foo67880; +__attribute__((used)) void* use67881 = (void*)&foo67881; +__attribute__((used)) void* use67882 = (void*)&foo67882; +__attribute__((used)) void* use67883 = (void*)&foo67883; +__attribute__((used)) void* use67884 = (void*)&foo67884; +__attribute__((used)) void* use67885 = (void*)&foo67885; +__attribute__((used)) void* use67886 = (void*)&foo67886; +__attribute__((used)) void* use67887 = (void*)&foo67887; +__attribute__((used)) void* use67888 = (void*)&foo67888; +__attribute__((used)) void* use67889 = (void*)&foo67889; +__attribute__((used)) void* use67890 = (void*)&foo67890; +__attribute__((used)) void* use67891 = (void*)&foo67891; +__attribute__((used)) void* use67892 = (void*)&foo67892; +__attribute__((used)) void* use67893 = (void*)&foo67893; +__attribute__((used)) void* use67894 = (void*)&foo67894; +__attribute__((used)) void* use67895 = (void*)&foo67895; +__attribute__((used)) void* use67896 = (void*)&foo67896; +__attribute__((used)) void* use67897 = (void*)&foo67897; +__attribute__((used)) void* use67898 = (void*)&foo67898; +__attribute__((used)) void* use67899 = (void*)&foo67899; +__attribute__((used)) void* use67900 = (void*)&foo67900; +__attribute__((used)) void* use67901 = (void*)&foo67901; +__attribute__((used)) void* use67902 = (void*)&foo67902; +__attribute__((used)) void* use67903 = (void*)&foo67903; +__attribute__((used)) void* use67904 = (void*)&foo67904; +__attribute__((used)) void* use67905 = (void*)&foo67905; +__attribute__((used)) void* use67906 = (void*)&foo67906; +__attribute__((used)) void* use67907 = (void*)&foo67907; +__attribute__((used)) void* use67908 = (void*)&foo67908; +__attribute__((used)) void* use67909 = (void*)&foo67909; +__attribute__((used)) void* use67910 = (void*)&foo67910; +__attribute__((used)) void* use67911 = (void*)&foo67911; +__attribute__((used)) void* use67912 = (void*)&foo67912; +__attribute__((used)) void* use67913 = (void*)&foo67913; +__attribute__((used)) void* use67914 = (void*)&foo67914; +__attribute__((used)) void* use67915 = (void*)&foo67915; +__attribute__((used)) void* use67916 = (void*)&foo67916; +__attribute__((used)) void* use67917 = (void*)&foo67917; +__attribute__((used)) void* use67918 = (void*)&foo67918; +__attribute__((used)) void* use67919 = (void*)&foo67919; +__attribute__((used)) void* use67920 = (void*)&foo67920; +__attribute__((used)) void* use67921 = (void*)&foo67921; +__attribute__((used)) void* use67922 = (void*)&foo67922; +__attribute__((used)) void* use67923 = (void*)&foo67923; +__attribute__((used)) void* use67924 = (void*)&foo67924; +__attribute__((used)) void* use67925 = (void*)&foo67925; +__attribute__((used)) void* use67926 = (void*)&foo67926; +__attribute__((used)) void* use67927 = (void*)&foo67927; +__attribute__((used)) void* use67928 = (void*)&foo67928; +__attribute__((used)) void* use67929 = (void*)&foo67929; +__attribute__((used)) void* use67930 = (void*)&foo67930; +__attribute__((used)) void* use67931 = (void*)&foo67931; +__attribute__((used)) void* use67932 = (void*)&foo67932; +__attribute__((used)) void* use67933 = (void*)&foo67933; +__attribute__((used)) void* use67934 = (void*)&foo67934; +__attribute__((used)) void* use67935 = (void*)&foo67935; +__attribute__((used)) void* use67936 = (void*)&foo67936; +__attribute__((used)) void* use67937 = (void*)&foo67937; +__attribute__((used)) void* use67938 = (void*)&foo67938; +__attribute__((used)) void* use67939 = (void*)&foo67939; +__attribute__((used)) void* use67940 = (void*)&foo67940; +__attribute__((used)) void* use67941 = (void*)&foo67941; +__attribute__((used)) void* use67942 = (void*)&foo67942; +__attribute__((used)) void* use67943 = (void*)&foo67943; +__attribute__((used)) void* use67944 = (void*)&foo67944; +__attribute__((used)) void* use67945 = (void*)&foo67945; +__attribute__((used)) void* use67946 = (void*)&foo67946; +__attribute__((used)) void* use67947 = (void*)&foo67947; +__attribute__((used)) void* use67948 = (void*)&foo67948; +__attribute__((used)) void* use67949 = (void*)&foo67949; +__attribute__((used)) void* use67950 = (void*)&foo67950; +__attribute__((used)) void* use67951 = (void*)&foo67951; +__attribute__((used)) void* use67952 = (void*)&foo67952; +__attribute__((used)) void* use67953 = (void*)&foo67953; +__attribute__((used)) void* use67954 = (void*)&foo67954; +__attribute__((used)) void* use67955 = (void*)&foo67955; +__attribute__((used)) void* use67956 = (void*)&foo67956; +__attribute__((used)) void* use67957 = (void*)&foo67957; +__attribute__((used)) void* use67958 = (void*)&foo67958; +__attribute__((used)) void* use67959 = (void*)&foo67959; +__attribute__((used)) void* use67960 = (void*)&foo67960; +__attribute__((used)) void* use67961 = (void*)&foo67961; +__attribute__((used)) void* use67962 = (void*)&foo67962; +__attribute__((used)) void* use67963 = (void*)&foo67963; +__attribute__((used)) void* use67964 = (void*)&foo67964; +__attribute__((used)) void* use67965 = (void*)&foo67965; +__attribute__((used)) void* use67966 = (void*)&foo67966; +__attribute__((used)) void* use67967 = (void*)&foo67967; +__attribute__((used)) void* use67968 = (void*)&foo67968; +__attribute__((used)) void* use67969 = (void*)&foo67969; +__attribute__((used)) void* use67970 = (void*)&foo67970; +__attribute__((used)) void* use67971 = (void*)&foo67971; +__attribute__((used)) void* use67972 = (void*)&foo67972; +__attribute__((used)) void* use67973 = (void*)&foo67973; +__attribute__((used)) void* use67974 = (void*)&foo67974; +__attribute__((used)) void* use67975 = (void*)&foo67975; +__attribute__((used)) void* use67976 = (void*)&foo67976; +__attribute__((used)) void* use67977 = (void*)&foo67977; +__attribute__((used)) void* use67978 = (void*)&foo67978; +__attribute__((used)) void* use67979 = (void*)&foo67979; +__attribute__((used)) void* use67980 = (void*)&foo67980; +__attribute__((used)) void* use67981 = (void*)&foo67981; +__attribute__((used)) void* use67982 = (void*)&foo67982; +__attribute__((used)) void* use67983 = (void*)&foo67983; +__attribute__((used)) void* use67984 = (void*)&foo67984; +__attribute__((used)) void* use67985 = (void*)&foo67985; +__attribute__((used)) void* use67986 = (void*)&foo67986; +__attribute__((used)) void* use67987 = (void*)&foo67987; +__attribute__((used)) void* use67988 = (void*)&foo67988; +__attribute__((used)) void* use67989 = (void*)&foo67989; +__attribute__((used)) void* use67990 = (void*)&foo67990; +__attribute__((used)) void* use67991 = (void*)&foo67991; +__attribute__((used)) void* use67992 = (void*)&foo67992; +__attribute__((used)) void* use67993 = (void*)&foo67993; +__attribute__((used)) void* use67994 = (void*)&foo67994; +__attribute__((used)) void* use67995 = (void*)&foo67995; +__attribute__((used)) void* use67996 = (void*)&foo67996; +__attribute__((used)) void* use67997 = (void*)&foo67997; +__attribute__((used)) void* use67998 = (void*)&foo67998; +__attribute__((used)) void* use67999 = (void*)&foo67999; +__attribute__((used)) void* use68000 = (void*)&foo68000; +__attribute__((used)) void* use68001 = (void*)&foo68001; +__attribute__((used)) void* use68002 = (void*)&foo68002; +__attribute__((used)) void* use68003 = (void*)&foo68003; +__attribute__((used)) void* use68004 = (void*)&foo68004; +__attribute__((used)) void* use68005 = (void*)&foo68005; +__attribute__((used)) void* use68006 = (void*)&foo68006; +__attribute__((used)) void* use68007 = (void*)&foo68007; +__attribute__((used)) void* use68008 = (void*)&foo68008; +__attribute__((used)) void* use68009 = (void*)&foo68009; +__attribute__((used)) void* use68010 = (void*)&foo68010; +__attribute__((used)) void* use68011 = (void*)&foo68011; +__attribute__((used)) void* use68012 = (void*)&foo68012; +__attribute__((used)) void* use68013 = (void*)&foo68013; +__attribute__((used)) void* use68014 = (void*)&foo68014; +__attribute__((used)) void* use68015 = (void*)&foo68015; +__attribute__((used)) void* use68016 = (void*)&foo68016; +__attribute__((used)) void* use68017 = (void*)&foo68017; +__attribute__((used)) void* use68018 = (void*)&foo68018; +__attribute__((used)) void* use68019 = (void*)&foo68019; +__attribute__((used)) void* use68020 = (void*)&foo68020; +__attribute__((used)) void* use68021 = (void*)&foo68021; +__attribute__((used)) void* use68022 = (void*)&foo68022; +__attribute__((used)) void* use68023 = (void*)&foo68023; +__attribute__((used)) void* use68024 = (void*)&foo68024; +__attribute__((used)) void* use68025 = (void*)&foo68025; +__attribute__((used)) void* use68026 = (void*)&foo68026; +__attribute__((used)) void* use68027 = (void*)&foo68027; +__attribute__((used)) void* use68028 = (void*)&foo68028; +__attribute__((used)) void* use68029 = (void*)&foo68029; +__attribute__((used)) void* use68030 = (void*)&foo68030; +__attribute__((used)) void* use68031 = (void*)&foo68031; +__attribute__((used)) void* use68032 = (void*)&foo68032; +__attribute__((used)) void* use68033 = (void*)&foo68033; +__attribute__((used)) void* use68034 = (void*)&foo68034; +__attribute__((used)) void* use68035 = (void*)&foo68035; +__attribute__((used)) void* use68036 = (void*)&foo68036; +__attribute__((used)) void* use68037 = (void*)&foo68037; +__attribute__((used)) void* use68038 = (void*)&foo68038; +__attribute__((used)) void* use68039 = (void*)&foo68039; +__attribute__((used)) void* use68040 = (void*)&foo68040; +__attribute__((used)) void* use68041 = (void*)&foo68041; +__attribute__((used)) void* use68042 = (void*)&foo68042; +__attribute__((used)) void* use68043 = (void*)&foo68043; +__attribute__((used)) void* use68044 = (void*)&foo68044; +__attribute__((used)) void* use68045 = (void*)&foo68045; +__attribute__((used)) void* use68046 = (void*)&foo68046; +__attribute__((used)) void* use68047 = (void*)&foo68047; +__attribute__((used)) void* use68048 = (void*)&foo68048; +__attribute__((used)) void* use68049 = (void*)&foo68049; +__attribute__((used)) void* use68050 = (void*)&foo68050; +__attribute__((used)) void* use68051 = (void*)&foo68051; +__attribute__((used)) void* use68052 = (void*)&foo68052; +__attribute__((used)) void* use68053 = (void*)&foo68053; +__attribute__((used)) void* use68054 = (void*)&foo68054; +__attribute__((used)) void* use68055 = (void*)&foo68055; +__attribute__((used)) void* use68056 = (void*)&foo68056; +__attribute__((used)) void* use68057 = (void*)&foo68057; +__attribute__((used)) void* use68058 = (void*)&foo68058; +__attribute__((used)) void* use68059 = (void*)&foo68059; +__attribute__((used)) void* use68060 = (void*)&foo68060; +__attribute__((used)) void* use68061 = (void*)&foo68061; +__attribute__((used)) void* use68062 = (void*)&foo68062; +__attribute__((used)) void* use68063 = (void*)&foo68063; +__attribute__((used)) void* use68064 = (void*)&foo68064; +__attribute__((used)) void* use68065 = (void*)&foo68065; +__attribute__((used)) void* use68066 = (void*)&foo68066; +__attribute__((used)) void* use68067 = (void*)&foo68067; +__attribute__((used)) void* use68068 = (void*)&foo68068; +__attribute__((used)) void* use68069 = (void*)&foo68069; +__attribute__((used)) void* use68070 = (void*)&foo68070; +__attribute__((used)) void* use68071 = (void*)&foo68071; +__attribute__((used)) void* use68072 = (void*)&foo68072; +__attribute__((used)) void* use68073 = (void*)&foo68073; +__attribute__((used)) void* use68074 = (void*)&foo68074; +__attribute__((used)) void* use68075 = (void*)&foo68075; +__attribute__((used)) void* use68076 = (void*)&foo68076; +__attribute__((used)) void* use68077 = (void*)&foo68077; +__attribute__((used)) void* use68078 = (void*)&foo68078; +__attribute__((used)) void* use68079 = (void*)&foo68079; +__attribute__((used)) void* use68080 = (void*)&foo68080; +__attribute__((used)) void* use68081 = (void*)&foo68081; +__attribute__((used)) void* use68082 = (void*)&foo68082; +__attribute__((used)) void* use68083 = (void*)&foo68083; +__attribute__((used)) void* use68084 = (void*)&foo68084; +__attribute__((used)) void* use68085 = (void*)&foo68085; +__attribute__((used)) void* use68086 = (void*)&foo68086; +__attribute__((used)) void* use68087 = (void*)&foo68087; +__attribute__((used)) void* use68088 = (void*)&foo68088; +__attribute__((used)) void* use68089 = (void*)&foo68089; +__attribute__((used)) void* use68090 = (void*)&foo68090; +__attribute__((used)) void* use68091 = (void*)&foo68091; +__attribute__((used)) void* use68092 = (void*)&foo68092; +__attribute__((used)) void* use68093 = (void*)&foo68093; +__attribute__((used)) void* use68094 = (void*)&foo68094; +__attribute__((used)) void* use68095 = (void*)&foo68095; +__attribute__((used)) void* use68096 = (void*)&foo68096; +__attribute__((used)) void* use68097 = (void*)&foo68097; +__attribute__((used)) void* use68098 = (void*)&foo68098; +__attribute__((used)) void* use68099 = (void*)&foo68099; +__attribute__((used)) void* use68100 = (void*)&foo68100; +__attribute__((used)) void* use68101 = (void*)&foo68101; +__attribute__((used)) void* use68102 = (void*)&foo68102; +__attribute__((used)) void* use68103 = (void*)&foo68103; +__attribute__((used)) void* use68104 = (void*)&foo68104; +__attribute__((used)) void* use68105 = (void*)&foo68105; +__attribute__((used)) void* use68106 = (void*)&foo68106; +__attribute__((used)) void* use68107 = (void*)&foo68107; +__attribute__((used)) void* use68108 = (void*)&foo68108; +__attribute__((used)) void* use68109 = (void*)&foo68109; +__attribute__((used)) void* use68110 = (void*)&foo68110; +__attribute__((used)) void* use68111 = (void*)&foo68111; +__attribute__((used)) void* use68112 = (void*)&foo68112; +__attribute__((used)) void* use68113 = (void*)&foo68113; +__attribute__((used)) void* use68114 = (void*)&foo68114; +__attribute__((used)) void* use68115 = (void*)&foo68115; +__attribute__((used)) void* use68116 = (void*)&foo68116; +__attribute__((used)) void* use68117 = (void*)&foo68117; +__attribute__((used)) void* use68118 = (void*)&foo68118; +__attribute__((used)) void* use68119 = (void*)&foo68119; +__attribute__((used)) void* use68120 = (void*)&foo68120; +__attribute__((used)) void* use68121 = (void*)&foo68121; +__attribute__((used)) void* use68122 = (void*)&foo68122; +__attribute__((used)) void* use68123 = (void*)&foo68123; +__attribute__((used)) void* use68124 = (void*)&foo68124; +__attribute__((used)) void* use68125 = (void*)&foo68125; +__attribute__((used)) void* use68126 = (void*)&foo68126; +__attribute__((used)) void* use68127 = (void*)&foo68127; +__attribute__((used)) void* use68128 = (void*)&foo68128; +__attribute__((used)) void* use68129 = (void*)&foo68129; +__attribute__((used)) void* use68130 = (void*)&foo68130; +__attribute__((used)) void* use68131 = (void*)&foo68131; +__attribute__((used)) void* use68132 = (void*)&foo68132; +__attribute__((used)) void* use68133 = (void*)&foo68133; +__attribute__((used)) void* use68134 = (void*)&foo68134; +__attribute__((used)) void* use68135 = (void*)&foo68135; +__attribute__((used)) void* use68136 = (void*)&foo68136; +__attribute__((used)) void* use68137 = (void*)&foo68137; +__attribute__((used)) void* use68138 = (void*)&foo68138; +__attribute__((used)) void* use68139 = (void*)&foo68139; +__attribute__((used)) void* use68140 = (void*)&foo68140; +__attribute__((used)) void* use68141 = (void*)&foo68141; +__attribute__((used)) void* use68142 = (void*)&foo68142; +__attribute__((used)) void* use68143 = (void*)&foo68143; +__attribute__((used)) void* use68144 = (void*)&foo68144; +__attribute__((used)) void* use68145 = (void*)&foo68145; +__attribute__((used)) void* use68146 = (void*)&foo68146; +__attribute__((used)) void* use68147 = (void*)&foo68147; +__attribute__((used)) void* use68148 = (void*)&foo68148; +__attribute__((used)) void* use68149 = (void*)&foo68149; +__attribute__((used)) void* use68150 = (void*)&foo68150; +__attribute__((used)) void* use68151 = (void*)&foo68151; +__attribute__((used)) void* use68152 = (void*)&foo68152; +__attribute__((used)) void* use68153 = (void*)&foo68153; +__attribute__((used)) void* use68154 = (void*)&foo68154; +__attribute__((used)) void* use68155 = (void*)&foo68155; +__attribute__((used)) void* use68156 = (void*)&foo68156; +__attribute__((used)) void* use68157 = (void*)&foo68157; +__attribute__((used)) void* use68158 = (void*)&foo68158; +__attribute__((used)) void* use68159 = (void*)&foo68159; +__attribute__((used)) void* use68160 = (void*)&foo68160; +__attribute__((used)) void* use68161 = (void*)&foo68161; +__attribute__((used)) void* use68162 = (void*)&foo68162; +__attribute__((used)) void* use68163 = (void*)&foo68163; +__attribute__((used)) void* use68164 = (void*)&foo68164; +__attribute__((used)) void* use68165 = (void*)&foo68165; +__attribute__((used)) void* use68166 = (void*)&foo68166; +__attribute__((used)) void* use68167 = (void*)&foo68167; +__attribute__((used)) void* use68168 = (void*)&foo68168; +__attribute__((used)) void* use68169 = (void*)&foo68169; +__attribute__((used)) void* use68170 = (void*)&foo68170; +__attribute__((used)) void* use68171 = (void*)&foo68171; +__attribute__((used)) void* use68172 = (void*)&foo68172; +__attribute__((used)) void* use68173 = (void*)&foo68173; +__attribute__((used)) void* use68174 = (void*)&foo68174; +__attribute__((used)) void* use68175 = (void*)&foo68175; +__attribute__((used)) void* use68176 = (void*)&foo68176; +__attribute__((used)) void* use68177 = (void*)&foo68177; +__attribute__((used)) void* use68178 = (void*)&foo68178; +__attribute__((used)) void* use68179 = (void*)&foo68179; +__attribute__((used)) void* use68180 = (void*)&foo68180; +__attribute__((used)) void* use68181 = (void*)&foo68181; +__attribute__((used)) void* use68182 = (void*)&foo68182; +__attribute__((used)) void* use68183 = (void*)&foo68183; +__attribute__((used)) void* use68184 = (void*)&foo68184; +__attribute__((used)) void* use68185 = (void*)&foo68185; +__attribute__((used)) void* use68186 = (void*)&foo68186; +__attribute__((used)) void* use68187 = (void*)&foo68187; +__attribute__((used)) void* use68188 = (void*)&foo68188; +__attribute__((used)) void* use68189 = (void*)&foo68189; +__attribute__((used)) void* use68190 = (void*)&foo68190; +__attribute__((used)) void* use68191 = (void*)&foo68191; +__attribute__((used)) void* use68192 = (void*)&foo68192; +__attribute__((used)) void* use68193 = (void*)&foo68193; +__attribute__((used)) void* use68194 = (void*)&foo68194; +__attribute__((used)) void* use68195 = (void*)&foo68195; +__attribute__((used)) void* use68196 = (void*)&foo68196; +__attribute__((used)) void* use68197 = (void*)&foo68197; +__attribute__((used)) void* use68198 = (void*)&foo68198; +__attribute__((used)) void* use68199 = (void*)&foo68199; +__attribute__((used)) void* use68200 = (void*)&foo68200; +__attribute__((used)) void* use68201 = (void*)&foo68201; +__attribute__((used)) void* use68202 = (void*)&foo68202; +__attribute__((used)) void* use68203 = (void*)&foo68203; +__attribute__((used)) void* use68204 = (void*)&foo68204; +__attribute__((used)) void* use68205 = (void*)&foo68205; +__attribute__((used)) void* use68206 = (void*)&foo68206; +__attribute__((used)) void* use68207 = (void*)&foo68207; +__attribute__((used)) void* use68208 = (void*)&foo68208; +__attribute__((used)) void* use68209 = (void*)&foo68209; +__attribute__((used)) void* use68210 = (void*)&foo68210; +__attribute__((used)) void* use68211 = (void*)&foo68211; +__attribute__((used)) void* use68212 = (void*)&foo68212; +__attribute__((used)) void* use68213 = (void*)&foo68213; +__attribute__((used)) void* use68214 = (void*)&foo68214; +__attribute__((used)) void* use68215 = (void*)&foo68215; +__attribute__((used)) void* use68216 = (void*)&foo68216; +__attribute__((used)) void* use68217 = (void*)&foo68217; +__attribute__((used)) void* use68218 = (void*)&foo68218; +__attribute__((used)) void* use68219 = (void*)&foo68219; +__attribute__((used)) void* use68220 = (void*)&foo68220; +__attribute__((used)) void* use68221 = (void*)&foo68221; +__attribute__((used)) void* use68222 = (void*)&foo68222; +__attribute__((used)) void* use68223 = (void*)&foo68223; +__attribute__((used)) void* use68224 = (void*)&foo68224; +__attribute__((used)) void* use68225 = (void*)&foo68225; +__attribute__((used)) void* use68226 = (void*)&foo68226; +__attribute__((used)) void* use68227 = (void*)&foo68227; +__attribute__((used)) void* use68228 = (void*)&foo68228; +__attribute__((used)) void* use68229 = (void*)&foo68229; +__attribute__((used)) void* use68230 = (void*)&foo68230; +__attribute__((used)) void* use68231 = (void*)&foo68231; +__attribute__((used)) void* use68232 = (void*)&foo68232; +__attribute__((used)) void* use68233 = (void*)&foo68233; +__attribute__((used)) void* use68234 = (void*)&foo68234; +__attribute__((used)) void* use68235 = (void*)&foo68235; +__attribute__((used)) void* use68236 = (void*)&foo68236; +__attribute__((used)) void* use68237 = (void*)&foo68237; +__attribute__((used)) void* use68238 = (void*)&foo68238; +__attribute__((used)) void* use68239 = (void*)&foo68239; +__attribute__((used)) void* use68240 = (void*)&foo68240; +__attribute__((used)) void* use68241 = (void*)&foo68241; +__attribute__((used)) void* use68242 = (void*)&foo68242; +__attribute__((used)) void* use68243 = (void*)&foo68243; +__attribute__((used)) void* use68244 = (void*)&foo68244; +__attribute__((used)) void* use68245 = (void*)&foo68245; +__attribute__((used)) void* use68246 = (void*)&foo68246; +__attribute__((used)) void* use68247 = (void*)&foo68247; +__attribute__((used)) void* use68248 = (void*)&foo68248; +__attribute__((used)) void* use68249 = (void*)&foo68249; +__attribute__((used)) void* use68250 = (void*)&foo68250; +__attribute__((used)) void* use68251 = (void*)&foo68251; +__attribute__((used)) void* use68252 = (void*)&foo68252; +__attribute__((used)) void* use68253 = (void*)&foo68253; +__attribute__((used)) void* use68254 = (void*)&foo68254; +__attribute__((used)) void* use68255 = (void*)&foo68255; +__attribute__((used)) void* use68256 = (void*)&foo68256; +__attribute__((used)) void* use68257 = (void*)&foo68257; +__attribute__((used)) void* use68258 = (void*)&foo68258; +__attribute__((used)) void* use68259 = (void*)&foo68259; +__attribute__((used)) void* use68260 = (void*)&foo68260; +__attribute__((used)) void* use68261 = (void*)&foo68261; +__attribute__((used)) void* use68262 = (void*)&foo68262; +__attribute__((used)) void* use68263 = (void*)&foo68263; +__attribute__((used)) void* use68264 = (void*)&foo68264; +__attribute__((used)) void* use68265 = (void*)&foo68265; +__attribute__((used)) void* use68266 = (void*)&foo68266; +__attribute__((used)) void* use68267 = (void*)&foo68267; +__attribute__((used)) void* use68268 = (void*)&foo68268; +__attribute__((used)) void* use68269 = (void*)&foo68269; +__attribute__((used)) void* use68270 = (void*)&foo68270; +__attribute__((used)) void* use68271 = (void*)&foo68271; +__attribute__((used)) void* use68272 = (void*)&foo68272; +__attribute__((used)) void* use68273 = (void*)&foo68273; +__attribute__((used)) void* use68274 = (void*)&foo68274; +__attribute__((used)) void* use68275 = (void*)&foo68275; +__attribute__((used)) void* use68276 = (void*)&foo68276; +__attribute__((used)) void* use68277 = (void*)&foo68277; +__attribute__((used)) void* use68278 = (void*)&foo68278; +__attribute__((used)) void* use68279 = (void*)&foo68279; +__attribute__((used)) void* use68280 = (void*)&foo68280; +__attribute__((used)) void* use68281 = (void*)&foo68281; +__attribute__((used)) void* use68282 = (void*)&foo68282; +__attribute__((used)) void* use68283 = (void*)&foo68283; +__attribute__((used)) void* use68284 = (void*)&foo68284; +__attribute__((used)) void* use68285 = (void*)&foo68285; +__attribute__((used)) void* use68286 = (void*)&foo68286; +__attribute__((used)) void* use68287 = (void*)&foo68287; +__attribute__((used)) void* use68288 = (void*)&foo68288; +__attribute__((used)) void* use68289 = (void*)&foo68289; +__attribute__((used)) void* use68290 = (void*)&foo68290; +__attribute__((used)) void* use68291 = (void*)&foo68291; +__attribute__((used)) void* use68292 = (void*)&foo68292; +__attribute__((used)) void* use68293 = (void*)&foo68293; +__attribute__((used)) void* use68294 = (void*)&foo68294; +__attribute__((used)) void* use68295 = (void*)&foo68295; +__attribute__((used)) void* use68296 = (void*)&foo68296; +__attribute__((used)) void* use68297 = (void*)&foo68297; +__attribute__((used)) void* use68298 = (void*)&foo68298; +__attribute__((used)) void* use68299 = (void*)&foo68299; +__attribute__((used)) void* use68300 = (void*)&foo68300; +__attribute__((used)) void* use68301 = (void*)&foo68301; +__attribute__((used)) void* use68302 = (void*)&foo68302; +__attribute__((used)) void* use68303 = (void*)&foo68303; +__attribute__((used)) void* use68304 = (void*)&foo68304; +__attribute__((used)) void* use68305 = (void*)&foo68305; +__attribute__((used)) void* use68306 = (void*)&foo68306; +__attribute__((used)) void* use68307 = (void*)&foo68307; +__attribute__((used)) void* use68308 = (void*)&foo68308; +__attribute__((used)) void* use68309 = (void*)&foo68309; +__attribute__((used)) void* use68310 = (void*)&foo68310; +__attribute__((used)) void* use68311 = (void*)&foo68311; +__attribute__((used)) void* use68312 = (void*)&foo68312; +__attribute__((used)) void* use68313 = (void*)&foo68313; +__attribute__((used)) void* use68314 = (void*)&foo68314; +__attribute__((used)) void* use68315 = (void*)&foo68315; +__attribute__((used)) void* use68316 = (void*)&foo68316; +__attribute__((used)) void* use68317 = (void*)&foo68317; +__attribute__((used)) void* use68318 = (void*)&foo68318; +__attribute__((used)) void* use68319 = (void*)&foo68319; +__attribute__((used)) void* use68320 = (void*)&foo68320; +__attribute__((used)) void* use68321 = (void*)&foo68321; +__attribute__((used)) void* use68322 = (void*)&foo68322; +__attribute__((used)) void* use68323 = (void*)&foo68323; +__attribute__((used)) void* use68324 = (void*)&foo68324; +__attribute__((used)) void* use68325 = (void*)&foo68325; +__attribute__((used)) void* use68326 = (void*)&foo68326; +__attribute__((used)) void* use68327 = (void*)&foo68327; +__attribute__((used)) void* use68328 = (void*)&foo68328; +__attribute__((used)) void* use68329 = (void*)&foo68329; +__attribute__((used)) void* use68330 = (void*)&foo68330; +__attribute__((used)) void* use68331 = (void*)&foo68331; +__attribute__((used)) void* use68332 = (void*)&foo68332; +__attribute__((used)) void* use68333 = (void*)&foo68333; +__attribute__((used)) void* use68334 = (void*)&foo68334; +__attribute__((used)) void* use68335 = (void*)&foo68335; +__attribute__((used)) void* use68336 = (void*)&foo68336; +__attribute__((used)) void* use68337 = (void*)&foo68337; +__attribute__((used)) void* use68338 = (void*)&foo68338; +__attribute__((used)) void* use68339 = (void*)&foo68339; +__attribute__((used)) void* use68340 = (void*)&foo68340; +__attribute__((used)) void* use68341 = (void*)&foo68341; +__attribute__((used)) void* use68342 = (void*)&foo68342; +__attribute__((used)) void* use68343 = (void*)&foo68343; +__attribute__((used)) void* use68344 = (void*)&foo68344; +__attribute__((used)) void* use68345 = (void*)&foo68345; +__attribute__((used)) void* use68346 = (void*)&foo68346; +__attribute__((used)) void* use68347 = (void*)&foo68347; +__attribute__((used)) void* use68348 = (void*)&foo68348; +__attribute__((used)) void* use68349 = (void*)&foo68349; +__attribute__((used)) void* use68350 = (void*)&foo68350; +__attribute__((used)) void* use68351 = (void*)&foo68351; +__attribute__((used)) void* use68352 = (void*)&foo68352; +__attribute__((used)) void* use68353 = (void*)&foo68353; +__attribute__((used)) void* use68354 = (void*)&foo68354; +__attribute__((used)) void* use68355 = (void*)&foo68355; +__attribute__((used)) void* use68356 = (void*)&foo68356; +__attribute__((used)) void* use68357 = (void*)&foo68357; +__attribute__((used)) void* use68358 = (void*)&foo68358; +__attribute__((used)) void* use68359 = (void*)&foo68359; +__attribute__((used)) void* use68360 = (void*)&foo68360; +__attribute__((used)) void* use68361 = (void*)&foo68361; +__attribute__((used)) void* use68362 = (void*)&foo68362; +__attribute__((used)) void* use68363 = (void*)&foo68363; +__attribute__((used)) void* use68364 = (void*)&foo68364; +__attribute__((used)) void* use68365 = (void*)&foo68365; +__attribute__((used)) void* use68366 = (void*)&foo68366; +__attribute__((used)) void* use68367 = (void*)&foo68367; +__attribute__((used)) void* use68368 = (void*)&foo68368; +__attribute__((used)) void* use68369 = (void*)&foo68369; +__attribute__((used)) void* use68370 = (void*)&foo68370; +__attribute__((used)) void* use68371 = (void*)&foo68371; +__attribute__((used)) void* use68372 = (void*)&foo68372; +__attribute__((used)) void* use68373 = (void*)&foo68373; +__attribute__((used)) void* use68374 = (void*)&foo68374; +__attribute__((used)) void* use68375 = (void*)&foo68375; +__attribute__((used)) void* use68376 = (void*)&foo68376; +__attribute__((used)) void* use68377 = (void*)&foo68377; +__attribute__((used)) void* use68378 = (void*)&foo68378; +__attribute__((used)) void* use68379 = (void*)&foo68379; +__attribute__((used)) void* use68380 = (void*)&foo68380; +__attribute__((used)) void* use68381 = (void*)&foo68381; +__attribute__((used)) void* use68382 = (void*)&foo68382; +__attribute__((used)) void* use68383 = (void*)&foo68383; +__attribute__((used)) void* use68384 = (void*)&foo68384; +__attribute__((used)) void* use68385 = (void*)&foo68385; +__attribute__((used)) void* use68386 = (void*)&foo68386; +__attribute__((used)) void* use68387 = (void*)&foo68387; +__attribute__((used)) void* use68388 = (void*)&foo68388; +__attribute__((used)) void* use68389 = (void*)&foo68389; +__attribute__((used)) void* use68390 = (void*)&foo68390; +__attribute__((used)) void* use68391 = (void*)&foo68391; +__attribute__((used)) void* use68392 = (void*)&foo68392; +__attribute__((used)) void* use68393 = (void*)&foo68393; +__attribute__((used)) void* use68394 = (void*)&foo68394; +__attribute__((used)) void* use68395 = (void*)&foo68395; +__attribute__((used)) void* use68396 = (void*)&foo68396; +__attribute__((used)) void* use68397 = (void*)&foo68397; +__attribute__((used)) void* use68398 = (void*)&foo68398; +__attribute__((used)) void* use68399 = (void*)&foo68399; +__attribute__((used)) void* use68400 = (void*)&foo68400; +__attribute__((used)) void* use68401 = (void*)&foo68401; +__attribute__((used)) void* use68402 = (void*)&foo68402; +__attribute__((used)) void* use68403 = (void*)&foo68403; +__attribute__((used)) void* use68404 = (void*)&foo68404; +__attribute__((used)) void* use68405 = (void*)&foo68405; +__attribute__((used)) void* use68406 = (void*)&foo68406; +__attribute__((used)) void* use68407 = (void*)&foo68407; +__attribute__((used)) void* use68408 = (void*)&foo68408; +__attribute__((used)) void* use68409 = (void*)&foo68409; +__attribute__((used)) void* use68410 = (void*)&foo68410; +__attribute__((used)) void* use68411 = (void*)&foo68411; +__attribute__((used)) void* use68412 = (void*)&foo68412; +__attribute__((used)) void* use68413 = (void*)&foo68413; +__attribute__((used)) void* use68414 = (void*)&foo68414; +__attribute__((used)) void* use68415 = (void*)&foo68415; +__attribute__((used)) void* use68416 = (void*)&foo68416; +__attribute__((used)) void* use68417 = (void*)&foo68417; +__attribute__((used)) void* use68418 = (void*)&foo68418; +__attribute__((used)) void* use68419 = (void*)&foo68419; +__attribute__((used)) void* use68420 = (void*)&foo68420; +__attribute__((used)) void* use68421 = (void*)&foo68421; +__attribute__((used)) void* use68422 = (void*)&foo68422; +__attribute__((used)) void* use68423 = (void*)&foo68423; +__attribute__((used)) void* use68424 = (void*)&foo68424; +__attribute__((used)) void* use68425 = (void*)&foo68425; +__attribute__((used)) void* use68426 = (void*)&foo68426; +__attribute__((used)) void* use68427 = (void*)&foo68427; +__attribute__((used)) void* use68428 = (void*)&foo68428; +__attribute__((used)) void* use68429 = (void*)&foo68429; +__attribute__((used)) void* use68430 = (void*)&foo68430; +__attribute__((used)) void* use68431 = (void*)&foo68431; +__attribute__((used)) void* use68432 = (void*)&foo68432; +__attribute__((used)) void* use68433 = (void*)&foo68433; +__attribute__((used)) void* use68434 = (void*)&foo68434; +__attribute__((used)) void* use68435 = (void*)&foo68435; +__attribute__((used)) void* use68436 = (void*)&foo68436; +__attribute__((used)) void* use68437 = (void*)&foo68437; +__attribute__((used)) void* use68438 = (void*)&foo68438; +__attribute__((used)) void* use68439 = (void*)&foo68439; +__attribute__((used)) void* use68440 = (void*)&foo68440; +__attribute__((used)) void* use68441 = (void*)&foo68441; +__attribute__((used)) void* use68442 = (void*)&foo68442; +__attribute__((used)) void* use68443 = (void*)&foo68443; +__attribute__((used)) void* use68444 = (void*)&foo68444; +__attribute__((used)) void* use68445 = (void*)&foo68445; +__attribute__((used)) void* use68446 = (void*)&foo68446; +__attribute__((used)) void* use68447 = (void*)&foo68447; +__attribute__((used)) void* use68448 = (void*)&foo68448; +__attribute__((used)) void* use68449 = (void*)&foo68449; +__attribute__((used)) void* use68450 = (void*)&foo68450; +__attribute__((used)) void* use68451 = (void*)&foo68451; +__attribute__((used)) void* use68452 = (void*)&foo68452; +__attribute__((used)) void* use68453 = (void*)&foo68453; +__attribute__((used)) void* use68454 = (void*)&foo68454; +__attribute__((used)) void* use68455 = (void*)&foo68455; +__attribute__((used)) void* use68456 = (void*)&foo68456; +__attribute__((used)) void* use68457 = (void*)&foo68457; +__attribute__((used)) void* use68458 = (void*)&foo68458; +__attribute__((used)) void* use68459 = (void*)&foo68459; +__attribute__((used)) void* use68460 = (void*)&foo68460; +__attribute__((used)) void* use68461 = (void*)&foo68461; +__attribute__((used)) void* use68462 = (void*)&foo68462; +__attribute__((used)) void* use68463 = (void*)&foo68463; +__attribute__((used)) void* use68464 = (void*)&foo68464; +__attribute__((used)) void* use68465 = (void*)&foo68465; +__attribute__((used)) void* use68466 = (void*)&foo68466; +__attribute__((used)) void* use68467 = (void*)&foo68467; +__attribute__((used)) void* use68468 = (void*)&foo68468; +__attribute__((used)) void* use68469 = (void*)&foo68469; +__attribute__((used)) void* use68470 = (void*)&foo68470; +__attribute__((used)) void* use68471 = (void*)&foo68471; +__attribute__((used)) void* use68472 = (void*)&foo68472; +__attribute__((used)) void* use68473 = (void*)&foo68473; +__attribute__((used)) void* use68474 = (void*)&foo68474; +__attribute__((used)) void* use68475 = (void*)&foo68475; +__attribute__((used)) void* use68476 = (void*)&foo68476; +__attribute__((used)) void* use68477 = (void*)&foo68477; +__attribute__((used)) void* use68478 = (void*)&foo68478; +__attribute__((used)) void* use68479 = (void*)&foo68479; +__attribute__((used)) void* use68480 = (void*)&foo68480; +__attribute__((used)) void* use68481 = (void*)&foo68481; +__attribute__((used)) void* use68482 = (void*)&foo68482; +__attribute__((used)) void* use68483 = (void*)&foo68483; +__attribute__((used)) void* use68484 = (void*)&foo68484; +__attribute__((used)) void* use68485 = (void*)&foo68485; +__attribute__((used)) void* use68486 = (void*)&foo68486; +__attribute__((used)) void* use68487 = (void*)&foo68487; +__attribute__((used)) void* use68488 = (void*)&foo68488; +__attribute__((used)) void* use68489 = (void*)&foo68489; +__attribute__((used)) void* use68490 = (void*)&foo68490; +__attribute__((used)) void* use68491 = (void*)&foo68491; +__attribute__((used)) void* use68492 = (void*)&foo68492; +__attribute__((used)) void* use68493 = (void*)&foo68493; +__attribute__((used)) void* use68494 = (void*)&foo68494; +__attribute__((used)) void* use68495 = (void*)&foo68495; +__attribute__((used)) void* use68496 = (void*)&foo68496; +__attribute__((used)) void* use68497 = (void*)&foo68497; +__attribute__((used)) void* use68498 = (void*)&foo68498; +__attribute__((used)) void* use68499 = (void*)&foo68499; +__attribute__((used)) void* use68500 = (void*)&foo68500; +__attribute__((used)) void* use68501 = (void*)&foo68501; +__attribute__((used)) void* use68502 = (void*)&foo68502; +__attribute__((used)) void* use68503 = (void*)&foo68503; +__attribute__((used)) void* use68504 = (void*)&foo68504; +__attribute__((used)) void* use68505 = (void*)&foo68505; +__attribute__((used)) void* use68506 = (void*)&foo68506; +__attribute__((used)) void* use68507 = (void*)&foo68507; +__attribute__((used)) void* use68508 = (void*)&foo68508; +__attribute__((used)) void* use68509 = (void*)&foo68509; +__attribute__((used)) void* use68510 = (void*)&foo68510; +__attribute__((used)) void* use68511 = (void*)&foo68511; +__attribute__((used)) void* use68512 = (void*)&foo68512; +__attribute__((used)) void* use68513 = (void*)&foo68513; +__attribute__((used)) void* use68514 = (void*)&foo68514; +__attribute__((used)) void* use68515 = (void*)&foo68515; +__attribute__((used)) void* use68516 = (void*)&foo68516; +__attribute__((used)) void* use68517 = (void*)&foo68517; +__attribute__((used)) void* use68518 = (void*)&foo68518; +__attribute__((used)) void* use68519 = (void*)&foo68519; +__attribute__((used)) void* use68520 = (void*)&foo68520; +__attribute__((used)) void* use68521 = (void*)&foo68521; +__attribute__((used)) void* use68522 = (void*)&foo68522; +__attribute__((used)) void* use68523 = (void*)&foo68523; +__attribute__((used)) void* use68524 = (void*)&foo68524; +__attribute__((used)) void* use68525 = (void*)&foo68525; +__attribute__((used)) void* use68526 = (void*)&foo68526; +__attribute__((used)) void* use68527 = (void*)&foo68527; +__attribute__((used)) void* use68528 = (void*)&foo68528; +__attribute__((used)) void* use68529 = (void*)&foo68529; +__attribute__((used)) void* use68530 = (void*)&foo68530; +__attribute__((used)) void* use68531 = (void*)&foo68531; +__attribute__((used)) void* use68532 = (void*)&foo68532; +__attribute__((used)) void* use68533 = (void*)&foo68533; +__attribute__((used)) void* use68534 = (void*)&foo68534; +__attribute__((used)) void* use68535 = (void*)&foo68535; +__attribute__((used)) void* use68536 = (void*)&foo68536; +__attribute__((used)) void* use68537 = (void*)&foo68537; +__attribute__((used)) void* use68538 = (void*)&foo68538; +__attribute__((used)) void* use68539 = (void*)&foo68539; +__attribute__((used)) void* use68540 = (void*)&foo68540; +__attribute__((used)) void* use68541 = (void*)&foo68541; +__attribute__((used)) void* use68542 = (void*)&foo68542; +__attribute__((used)) void* use68543 = (void*)&foo68543; +__attribute__((used)) void* use68544 = (void*)&foo68544; +__attribute__((used)) void* use68545 = (void*)&foo68545; +__attribute__((used)) void* use68546 = (void*)&foo68546; +__attribute__((used)) void* use68547 = (void*)&foo68547; +__attribute__((used)) void* use68548 = (void*)&foo68548; +__attribute__((used)) void* use68549 = (void*)&foo68549; +__attribute__((used)) void* use68550 = (void*)&foo68550; +__attribute__((used)) void* use68551 = (void*)&foo68551; +__attribute__((used)) void* use68552 = (void*)&foo68552; +__attribute__((used)) void* use68553 = (void*)&foo68553; +__attribute__((used)) void* use68554 = (void*)&foo68554; +__attribute__((used)) void* use68555 = (void*)&foo68555; +__attribute__((used)) void* use68556 = (void*)&foo68556; +__attribute__((used)) void* use68557 = (void*)&foo68557; +__attribute__((used)) void* use68558 = (void*)&foo68558; +__attribute__((used)) void* use68559 = (void*)&foo68559; +__attribute__((used)) void* use68560 = (void*)&foo68560; +__attribute__((used)) void* use68561 = (void*)&foo68561; +__attribute__((used)) void* use68562 = (void*)&foo68562; +__attribute__((used)) void* use68563 = (void*)&foo68563; +__attribute__((used)) void* use68564 = (void*)&foo68564; +__attribute__((used)) void* use68565 = (void*)&foo68565; +__attribute__((used)) void* use68566 = (void*)&foo68566; +__attribute__((used)) void* use68567 = (void*)&foo68567; +__attribute__((used)) void* use68568 = (void*)&foo68568; +__attribute__((used)) void* use68569 = (void*)&foo68569; +__attribute__((used)) void* use68570 = (void*)&foo68570; +__attribute__((used)) void* use68571 = (void*)&foo68571; +__attribute__((used)) void* use68572 = (void*)&foo68572; +__attribute__((used)) void* use68573 = (void*)&foo68573; +__attribute__((used)) void* use68574 = (void*)&foo68574; +__attribute__((used)) void* use68575 = (void*)&foo68575; +__attribute__((used)) void* use68576 = (void*)&foo68576; +__attribute__((used)) void* use68577 = (void*)&foo68577; +__attribute__((used)) void* use68578 = (void*)&foo68578; +__attribute__((used)) void* use68579 = (void*)&foo68579; +__attribute__((used)) void* use68580 = (void*)&foo68580; +__attribute__((used)) void* use68581 = (void*)&foo68581; +__attribute__((used)) void* use68582 = (void*)&foo68582; +__attribute__((used)) void* use68583 = (void*)&foo68583; +__attribute__((used)) void* use68584 = (void*)&foo68584; +__attribute__((used)) void* use68585 = (void*)&foo68585; +__attribute__((used)) void* use68586 = (void*)&foo68586; +__attribute__((used)) void* use68587 = (void*)&foo68587; +__attribute__((used)) void* use68588 = (void*)&foo68588; +__attribute__((used)) void* use68589 = (void*)&foo68589; +__attribute__((used)) void* use68590 = (void*)&foo68590; +__attribute__((used)) void* use68591 = (void*)&foo68591; +__attribute__((used)) void* use68592 = (void*)&foo68592; +__attribute__((used)) void* use68593 = (void*)&foo68593; +__attribute__((used)) void* use68594 = (void*)&foo68594; +__attribute__((used)) void* use68595 = (void*)&foo68595; +__attribute__((used)) void* use68596 = (void*)&foo68596; +__attribute__((used)) void* use68597 = (void*)&foo68597; +__attribute__((used)) void* use68598 = (void*)&foo68598; +__attribute__((used)) void* use68599 = (void*)&foo68599; +__attribute__((used)) void* use68600 = (void*)&foo68600; +__attribute__((used)) void* use68601 = (void*)&foo68601; +__attribute__((used)) void* use68602 = (void*)&foo68602; +__attribute__((used)) void* use68603 = (void*)&foo68603; +__attribute__((used)) void* use68604 = (void*)&foo68604; +__attribute__((used)) void* use68605 = (void*)&foo68605; +__attribute__((used)) void* use68606 = (void*)&foo68606; +__attribute__((used)) void* use68607 = (void*)&foo68607; +__attribute__((used)) void* use68608 = (void*)&foo68608; +__attribute__((used)) void* use68609 = (void*)&foo68609; +__attribute__((used)) void* use68610 = (void*)&foo68610; +__attribute__((used)) void* use68611 = (void*)&foo68611; +__attribute__((used)) void* use68612 = (void*)&foo68612; +__attribute__((used)) void* use68613 = (void*)&foo68613; +__attribute__((used)) void* use68614 = (void*)&foo68614; +__attribute__((used)) void* use68615 = (void*)&foo68615; +__attribute__((used)) void* use68616 = (void*)&foo68616; +__attribute__((used)) void* use68617 = (void*)&foo68617; +__attribute__((used)) void* use68618 = (void*)&foo68618; +__attribute__((used)) void* use68619 = (void*)&foo68619; +__attribute__((used)) void* use68620 = (void*)&foo68620; +__attribute__((used)) void* use68621 = (void*)&foo68621; +__attribute__((used)) void* use68622 = (void*)&foo68622; +__attribute__((used)) void* use68623 = (void*)&foo68623; +__attribute__((used)) void* use68624 = (void*)&foo68624; +__attribute__((used)) void* use68625 = (void*)&foo68625; +__attribute__((used)) void* use68626 = (void*)&foo68626; +__attribute__((used)) void* use68627 = (void*)&foo68627; +__attribute__((used)) void* use68628 = (void*)&foo68628; +__attribute__((used)) void* use68629 = (void*)&foo68629; +__attribute__((used)) void* use68630 = (void*)&foo68630; +__attribute__((used)) void* use68631 = (void*)&foo68631; +__attribute__((used)) void* use68632 = (void*)&foo68632; +__attribute__((used)) void* use68633 = (void*)&foo68633; +__attribute__((used)) void* use68634 = (void*)&foo68634; +__attribute__((used)) void* use68635 = (void*)&foo68635; +__attribute__((used)) void* use68636 = (void*)&foo68636; +__attribute__((used)) void* use68637 = (void*)&foo68637; +__attribute__((used)) void* use68638 = (void*)&foo68638; +__attribute__((used)) void* use68639 = (void*)&foo68639; +__attribute__((used)) void* use68640 = (void*)&foo68640; +__attribute__((used)) void* use68641 = (void*)&foo68641; +__attribute__((used)) void* use68642 = (void*)&foo68642; +__attribute__((used)) void* use68643 = (void*)&foo68643; +__attribute__((used)) void* use68644 = (void*)&foo68644; +__attribute__((used)) void* use68645 = (void*)&foo68645; +__attribute__((used)) void* use68646 = (void*)&foo68646; +__attribute__((used)) void* use68647 = (void*)&foo68647; +__attribute__((used)) void* use68648 = (void*)&foo68648; +__attribute__((used)) void* use68649 = (void*)&foo68649; +__attribute__((used)) void* use68650 = (void*)&foo68650; +__attribute__((used)) void* use68651 = (void*)&foo68651; +__attribute__((used)) void* use68652 = (void*)&foo68652; +__attribute__((used)) void* use68653 = (void*)&foo68653; +__attribute__((used)) void* use68654 = (void*)&foo68654; +__attribute__((used)) void* use68655 = (void*)&foo68655; +__attribute__((used)) void* use68656 = (void*)&foo68656; +__attribute__((used)) void* use68657 = (void*)&foo68657; +__attribute__((used)) void* use68658 = (void*)&foo68658; +__attribute__((used)) void* use68659 = (void*)&foo68659; +__attribute__((used)) void* use68660 = (void*)&foo68660; +__attribute__((used)) void* use68661 = (void*)&foo68661; +__attribute__((used)) void* use68662 = (void*)&foo68662; +__attribute__((used)) void* use68663 = (void*)&foo68663; +__attribute__((used)) void* use68664 = (void*)&foo68664; +__attribute__((used)) void* use68665 = (void*)&foo68665; +__attribute__((used)) void* use68666 = (void*)&foo68666; +__attribute__((used)) void* use68667 = (void*)&foo68667; +__attribute__((used)) void* use68668 = (void*)&foo68668; +__attribute__((used)) void* use68669 = (void*)&foo68669; +__attribute__((used)) void* use68670 = (void*)&foo68670; +__attribute__((used)) void* use68671 = (void*)&foo68671; +__attribute__((used)) void* use68672 = (void*)&foo68672; +__attribute__((used)) void* use68673 = (void*)&foo68673; +__attribute__((used)) void* use68674 = (void*)&foo68674; +__attribute__((used)) void* use68675 = (void*)&foo68675; +__attribute__((used)) void* use68676 = (void*)&foo68676; +__attribute__((used)) void* use68677 = (void*)&foo68677; +__attribute__((used)) void* use68678 = (void*)&foo68678; +__attribute__((used)) void* use68679 = (void*)&foo68679; +__attribute__((used)) void* use68680 = (void*)&foo68680; +__attribute__((used)) void* use68681 = (void*)&foo68681; +__attribute__((used)) void* use68682 = (void*)&foo68682; +__attribute__((used)) void* use68683 = (void*)&foo68683; +__attribute__((used)) void* use68684 = (void*)&foo68684; +__attribute__((used)) void* use68685 = (void*)&foo68685; +__attribute__((used)) void* use68686 = (void*)&foo68686; +__attribute__((used)) void* use68687 = (void*)&foo68687; +__attribute__((used)) void* use68688 = (void*)&foo68688; +__attribute__((used)) void* use68689 = (void*)&foo68689; +__attribute__((used)) void* use68690 = (void*)&foo68690; +__attribute__((used)) void* use68691 = (void*)&foo68691; +__attribute__((used)) void* use68692 = (void*)&foo68692; +__attribute__((used)) void* use68693 = (void*)&foo68693; +__attribute__((used)) void* use68694 = (void*)&foo68694; +__attribute__((used)) void* use68695 = (void*)&foo68695; +__attribute__((used)) void* use68696 = (void*)&foo68696; +__attribute__((used)) void* use68697 = (void*)&foo68697; +__attribute__((used)) void* use68698 = (void*)&foo68698; +__attribute__((used)) void* use68699 = (void*)&foo68699; +__attribute__((used)) void* use68700 = (void*)&foo68700; +__attribute__((used)) void* use68701 = (void*)&foo68701; +__attribute__((used)) void* use68702 = (void*)&foo68702; +__attribute__((used)) void* use68703 = (void*)&foo68703; +__attribute__((used)) void* use68704 = (void*)&foo68704; +__attribute__((used)) void* use68705 = (void*)&foo68705; +__attribute__((used)) void* use68706 = (void*)&foo68706; +__attribute__((used)) void* use68707 = (void*)&foo68707; +__attribute__((used)) void* use68708 = (void*)&foo68708; +__attribute__((used)) void* use68709 = (void*)&foo68709; +__attribute__((used)) void* use68710 = (void*)&foo68710; +__attribute__((used)) void* use68711 = (void*)&foo68711; +__attribute__((used)) void* use68712 = (void*)&foo68712; +__attribute__((used)) void* use68713 = (void*)&foo68713; +__attribute__((used)) void* use68714 = (void*)&foo68714; +__attribute__((used)) void* use68715 = (void*)&foo68715; +__attribute__((used)) void* use68716 = (void*)&foo68716; +__attribute__((used)) void* use68717 = (void*)&foo68717; +__attribute__((used)) void* use68718 = (void*)&foo68718; +__attribute__((used)) void* use68719 = (void*)&foo68719; +__attribute__((used)) void* use68720 = (void*)&foo68720; +__attribute__((used)) void* use68721 = (void*)&foo68721; +__attribute__((used)) void* use68722 = (void*)&foo68722; +__attribute__((used)) void* use68723 = (void*)&foo68723; +__attribute__((used)) void* use68724 = (void*)&foo68724; +__attribute__((used)) void* use68725 = (void*)&foo68725; +__attribute__((used)) void* use68726 = (void*)&foo68726; +__attribute__((used)) void* use68727 = (void*)&foo68727; +__attribute__((used)) void* use68728 = (void*)&foo68728; +__attribute__((used)) void* use68729 = (void*)&foo68729; +__attribute__((used)) void* use68730 = (void*)&foo68730; +__attribute__((used)) void* use68731 = (void*)&foo68731; +__attribute__((used)) void* use68732 = (void*)&foo68732; +__attribute__((used)) void* use68733 = (void*)&foo68733; +__attribute__((used)) void* use68734 = (void*)&foo68734; +__attribute__((used)) void* use68735 = (void*)&foo68735; +__attribute__((used)) void* use68736 = (void*)&foo68736; +__attribute__((used)) void* use68737 = (void*)&foo68737; +__attribute__((used)) void* use68738 = (void*)&foo68738; +__attribute__((used)) void* use68739 = (void*)&foo68739; +__attribute__((used)) void* use68740 = (void*)&foo68740; +__attribute__((used)) void* use68741 = (void*)&foo68741; +__attribute__((used)) void* use68742 = (void*)&foo68742; +__attribute__((used)) void* use68743 = (void*)&foo68743; +__attribute__((used)) void* use68744 = (void*)&foo68744; +__attribute__((used)) void* use68745 = (void*)&foo68745; +__attribute__((used)) void* use68746 = (void*)&foo68746; +__attribute__((used)) void* use68747 = (void*)&foo68747; +__attribute__((used)) void* use68748 = (void*)&foo68748; +__attribute__((used)) void* use68749 = (void*)&foo68749; +__attribute__((used)) void* use68750 = (void*)&foo68750; +__attribute__((used)) void* use68751 = (void*)&foo68751; +__attribute__((used)) void* use68752 = (void*)&foo68752; +__attribute__((used)) void* use68753 = (void*)&foo68753; +__attribute__((used)) void* use68754 = (void*)&foo68754; +__attribute__((used)) void* use68755 = (void*)&foo68755; +__attribute__((used)) void* use68756 = (void*)&foo68756; +__attribute__((used)) void* use68757 = (void*)&foo68757; +__attribute__((used)) void* use68758 = (void*)&foo68758; +__attribute__((used)) void* use68759 = (void*)&foo68759; +__attribute__((used)) void* use68760 = (void*)&foo68760; +__attribute__((used)) void* use68761 = (void*)&foo68761; +__attribute__((used)) void* use68762 = (void*)&foo68762; +__attribute__((used)) void* use68763 = (void*)&foo68763; +__attribute__((used)) void* use68764 = (void*)&foo68764; +__attribute__((used)) void* use68765 = (void*)&foo68765; +__attribute__((used)) void* use68766 = (void*)&foo68766; +__attribute__((used)) void* use68767 = (void*)&foo68767; +__attribute__((used)) void* use68768 = (void*)&foo68768; +__attribute__((used)) void* use68769 = (void*)&foo68769; +__attribute__((used)) void* use68770 = (void*)&foo68770; +__attribute__((used)) void* use68771 = (void*)&foo68771; +__attribute__((used)) void* use68772 = (void*)&foo68772; +__attribute__((used)) void* use68773 = (void*)&foo68773; +__attribute__((used)) void* use68774 = (void*)&foo68774; +__attribute__((used)) void* use68775 = (void*)&foo68775; +__attribute__((used)) void* use68776 = (void*)&foo68776; +__attribute__((used)) void* use68777 = (void*)&foo68777; +__attribute__((used)) void* use68778 = (void*)&foo68778; +__attribute__((used)) void* use68779 = (void*)&foo68779; +__attribute__((used)) void* use68780 = (void*)&foo68780; +__attribute__((used)) void* use68781 = (void*)&foo68781; +__attribute__((used)) void* use68782 = (void*)&foo68782; +__attribute__((used)) void* use68783 = (void*)&foo68783; +__attribute__((used)) void* use68784 = (void*)&foo68784; +__attribute__((used)) void* use68785 = (void*)&foo68785; +__attribute__((used)) void* use68786 = (void*)&foo68786; +__attribute__((used)) void* use68787 = (void*)&foo68787; +__attribute__((used)) void* use68788 = (void*)&foo68788; +__attribute__((used)) void* use68789 = (void*)&foo68789; +__attribute__((used)) void* use68790 = (void*)&foo68790; +__attribute__((used)) void* use68791 = (void*)&foo68791; +__attribute__((used)) void* use68792 = (void*)&foo68792; +__attribute__((used)) void* use68793 = (void*)&foo68793; +__attribute__((used)) void* use68794 = (void*)&foo68794; +__attribute__((used)) void* use68795 = (void*)&foo68795; +__attribute__((used)) void* use68796 = (void*)&foo68796; +__attribute__((used)) void* use68797 = (void*)&foo68797; +__attribute__((used)) void* use68798 = (void*)&foo68798; +__attribute__((used)) void* use68799 = (void*)&foo68799; +__attribute__((used)) void* use68800 = (void*)&foo68800; +__attribute__((used)) void* use68801 = (void*)&foo68801; +__attribute__((used)) void* use68802 = (void*)&foo68802; +__attribute__((used)) void* use68803 = (void*)&foo68803; +__attribute__((used)) void* use68804 = (void*)&foo68804; +__attribute__((used)) void* use68805 = (void*)&foo68805; +__attribute__((used)) void* use68806 = (void*)&foo68806; +__attribute__((used)) void* use68807 = (void*)&foo68807; +__attribute__((used)) void* use68808 = (void*)&foo68808; +__attribute__((used)) void* use68809 = (void*)&foo68809; +__attribute__((used)) void* use68810 = (void*)&foo68810; +__attribute__((used)) void* use68811 = (void*)&foo68811; +__attribute__((used)) void* use68812 = (void*)&foo68812; +__attribute__((used)) void* use68813 = (void*)&foo68813; +__attribute__((used)) void* use68814 = (void*)&foo68814; +__attribute__((used)) void* use68815 = (void*)&foo68815; +__attribute__((used)) void* use68816 = (void*)&foo68816; +__attribute__((used)) void* use68817 = (void*)&foo68817; +__attribute__((used)) void* use68818 = (void*)&foo68818; +__attribute__((used)) void* use68819 = (void*)&foo68819; +__attribute__((used)) void* use68820 = (void*)&foo68820; +__attribute__((used)) void* use68821 = (void*)&foo68821; +__attribute__((used)) void* use68822 = (void*)&foo68822; +__attribute__((used)) void* use68823 = (void*)&foo68823; +__attribute__((used)) void* use68824 = (void*)&foo68824; +__attribute__((used)) void* use68825 = (void*)&foo68825; +__attribute__((used)) void* use68826 = (void*)&foo68826; +__attribute__((used)) void* use68827 = (void*)&foo68827; +__attribute__((used)) void* use68828 = (void*)&foo68828; +__attribute__((used)) void* use68829 = (void*)&foo68829; +__attribute__((used)) void* use68830 = (void*)&foo68830; +__attribute__((used)) void* use68831 = (void*)&foo68831; +__attribute__((used)) void* use68832 = (void*)&foo68832; +__attribute__((used)) void* use68833 = (void*)&foo68833; +__attribute__((used)) void* use68834 = (void*)&foo68834; +__attribute__((used)) void* use68835 = (void*)&foo68835; +__attribute__((used)) void* use68836 = (void*)&foo68836; +__attribute__((used)) void* use68837 = (void*)&foo68837; +__attribute__((used)) void* use68838 = (void*)&foo68838; +__attribute__((used)) void* use68839 = (void*)&foo68839; +__attribute__((used)) void* use68840 = (void*)&foo68840; +__attribute__((used)) void* use68841 = (void*)&foo68841; +__attribute__((used)) void* use68842 = (void*)&foo68842; +__attribute__((used)) void* use68843 = (void*)&foo68843; +__attribute__((used)) void* use68844 = (void*)&foo68844; +__attribute__((used)) void* use68845 = (void*)&foo68845; +__attribute__((used)) void* use68846 = (void*)&foo68846; +__attribute__((used)) void* use68847 = (void*)&foo68847; +__attribute__((used)) void* use68848 = (void*)&foo68848; +__attribute__((used)) void* use68849 = (void*)&foo68849; +__attribute__((used)) void* use68850 = (void*)&foo68850; +__attribute__((used)) void* use68851 = (void*)&foo68851; +__attribute__((used)) void* use68852 = (void*)&foo68852; +__attribute__((used)) void* use68853 = (void*)&foo68853; +__attribute__((used)) void* use68854 = (void*)&foo68854; +__attribute__((used)) void* use68855 = (void*)&foo68855; +__attribute__((used)) void* use68856 = (void*)&foo68856; +__attribute__((used)) void* use68857 = (void*)&foo68857; +__attribute__((used)) void* use68858 = (void*)&foo68858; +__attribute__((used)) void* use68859 = (void*)&foo68859; +__attribute__((used)) void* use68860 = (void*)&foo68860; +__attribute__((used)) void* use68861 = (void*)&foo68861; +__attribute__((used)) void* use68862 = (void*)&foo68862; +__attribute__((used)) void* use68863 = (void*)&foo68863; +__attribute__((used)) void* use68864 = (void*)&foo68864; +__attribute__((used)) void* use68865 = (void*)&foo68865; +__attribute__((used)) void* use68866 = (void*)&foo68866; +__attribute__((used)) void* use68867 = (void*)&foo68867; +__attribute__((used)) void* use68868 = (void*)&foo68868; +__attribute__((used)) void* use68869 = (void*)&foo68869; +__attribute__((used)) void* use68870 = (void*)&foo68870; +__attribute__((used)) void* use68871 = (void*)&foo68871; +__attribute__((used)) void* use68872 = (void*)&foo68872; +__attribute__((used)) void* use68873 = (void*)&foo68873; +__attribute__((used)) void* use68874 = (void*)&foo68874; +__attribute__((used)) void* use68875 = (void*)&foo68875; +__attribute__((used)) void* use68876 = (void*)&foo68876; +__attribute__((used)) void* use68877 = (void*)&foo68877; +__attribute__((used)) void* use68878 = (void*)&foo68878; +__attribute__((used)) void* use68879 = (void*)&foo68879; +__attribute__((used)) void* use68880 = (void*)&foo68880; +__attribute__((used)) void* use68881 = (void*)&foo68881; +__attribute__((used)) void* use68882 = (void*)&foo68882; +__attribute__((used)) void* use68883 = (void*)&foo68883; +__attribute__((used)) void* use68884 = (void*)&foo68884; +__attribute__((used)) void* use68885 = (void*)&foo68885; +__attribute__((used)) void* use68886 = (void*)&foo68886; +__attribute__((used)) void* use68887 = (void*)&foo68887; +__attribute__((used)) void* use68888 = (void*)&foo68888; +__attribute__((used)) void* use68889 = (void*)&foo68889; +__attribute__((used)) void* use68890 = (void*)&foo68890; +__attribute__((used)) void* use68891 = (void*)&foo68891; +__attribute__((used)) void* use68892 = (void*)&foo68892; +__attribute__((used)) void* use68893 = (void*)&foo68893; +__attribute__((used)) void* use68894 = (void*)&foo68894; +__attribute__((used)) void* use68895 = (void*)&foo68895; +__attribute__((used)) void* use68896 = (void*)&foo68896; +__attribute__((used)) void* use68897 = (void*)&foo68897; +__attribute__((used)) void* use68898 = (void*)&foo68898; +__attribute__((used)) void* use68899 = (void*)&foo68899; +__attribute__((used)) void* use68900 = (void*)&foo68900; +__attribute__((used)) void* use68901 = (void*)&foo68901; +__attribute__((used)) void* use68902 = (void*)&foo68902; +__attribute__((used)) void* use68903 = (void*)&foo68903; +__attribute__((used)) void* use68904 = (void*)&foo68904; +__attribute__((used)) void* use68905 = (void*)&foo68905; +__attribute__((used)) void* use68906 = (void*)&foo68906; +__attribute__((used)) void* use68907 = (void*)&foo68907; +__attribute__((used)) void* use68908 = (void*)&foo68908; +__attribute__((used)) void* use68909 = (void*)&foo68909; +__attribute__((used)) void* use68910 = (void*)&foo68910; +__attribute__((used)) void* use68911 = (void*)&foo68911; +__attribute__((used)) void* use68912 = (void*)&foo68912; +__attribute__((used)) void* use68913 = (void*)&foo68913; +__attribute__((used)) void* use68914 = (void*)&foo68914; +__attribute__((used)) void* use68915 = (void*)&foo68915; +__attribute__((used)) void* use68916 = (void*)&foo68916; +__attribute__((used)) void* use68917 = (void*)&foo68917; +__attribute__((used)) void* use68918 = (void*)&foo68918; +__attribute__((used)) void* use68919 = (void*)&foo68919; +__attribute__((used)) void* use68920 = (void*)&foo68920; +__attribute__((used)) void* use68921 = (void*)&foo68921; +__attribute__((used)) void* use68922 = (void*)&foo68922; +__attribute__((used)) void* use68923 = (void*)&foo68923; +__attribute__((used)) void* use68924 = (void*)&foo68924; +__attribute__((used)) void* use68925 = (void*)&foo68925; +__attribute__((used)) void* use68926 = (void*)&foo68926; +__attribute__((used)) void* use68927 = (void*)&foo68927; +__attribute__((used)) void* use68928 = (void*)&foo68928; +__attribute__((used)) void* use68929 = (void*)&foo68929; +__attribute__((used)) void* use68930 = (void*)&foo68930; +__attribute__((used)) void* use68931 = (void*)&foo68931; +__attribute__((used)) void* use68932 = (void*)&foo68932; +__attribute__((used)) void* use68933 = (void*)&foo68933; +__attribute__((used)) void* use68934 = (void*)&foo68934; +__attribute__((used)) void* use68935 = (void*)&foo68935; +__attribute__((used)) void* use68936 = (void*)&foo68936; +__attribute__((used)) void* use68937 = (void*)&foo68937; +__attribute__((used)) void* use68938 = (void*)&foo68938; +__attribute__((used)) void* use68939 = (void*)&foo68939; +__attribute__((used)) void* use68940 = (void*)&foo68940; +__attribute__((used)) void* use68941 = (void*)&foo68941; +__attribute__((used)) void* use68942 = (void*)&foo68942; +__attribute__((used)) void* use68943 = (void*)&foo68943; +__attribute__((used)) void* use68944 = (void*)&foo68944; +__attribute__((used)) void* use68945 = (void*)&foo68945; +__attribute__((used)) void* use68946 = (void*)&foo68946; +__attribute__((used)) void* use68947 = (void*)&foo68947; +__attribute__((used)) void* use68948 = (void*)&foo68948; +__attribute__((used)) void* use68949 = (void*)&foo68949; +__attribute__((used)) void* use68950 = (void*)&foo68950; +__attribute__((used)) void* use68951 = (void*)&foo68951; +__attribute__((used)) void* use68952 = (void*)&foo68952; +__attribute__((used)) void* use68953 = (void*)&foo68953; +__attribute__((used)) void* use68954 = (void*)&foo68954; +__attribute__((used)) void* use68955 = (void*)&foo68955; +__attribute__((used)) void* use68956 = (void*)&foo68956; +__attribute__((used)) void* use68957 = (void*)&foo68957; +__attribute__((used)) void* use68958 = (void*)&foo68958; +__attribute__((used)) void* use68959 = (void*)&foo68959; +__attribute__((used)) void* use68960 = (void*)&foo68960; +__attribute__((used)) void* use68961 = (void*)&foo68961; +__attribute__((used)) void* use68962 = (void*)&foo68962; +__attribute__((used)) void* use68963 = (void*)&foo68963; +__attribute__((used)) void* use68964 = (void*)&foo68964; +__attribute__((used)) void* use68965 = (void*)&foo68965; +__attribute__((used)) void* use68966 = (void*)&foo68966; +__attribute__((used)) void* use68967 = (void*)&foo68967; +__attribute__((used)) void* use68968 = (void*)&foo68968; +__attribute__((used)) void* use68969 = (void*)&foo68969; +__attribute__((used)) void* use68970 = (void*)&foo68970; +__attribute__((used)) void* use68971 = (void*)&foo68971; +__attribute__((used)) void* use68972 = (void*)&foo68972; +__attribute__((used)) void* use68973 = (void*)&foo68973; +__attribute__((used)) void* use68974 = (void*)&foo68974; +__attribute__((used)) void* use68975 = (void*)&foo68975; +__attribute__((used)) void* use68976 = (void*)&foo68976; +__attribute__((used)) void* use68977 = (void*)&foo68977; +__attribute__((used)) void* use68978 = (void*)&foo68978; +__attribute__((used)) void* use68979 = (void*)&foo68979; +__attribute__((used)) void* use68980 = (void*)&foo68980; +__attribute__((used)) void* use68981 = (void*)&foo68981; +__attribute__((used)) void* use68982 = (void*)&foo68982; +__attribute__((used)) void* use68983 = (void*)&foo68983; +__attribute__((used)) void* use68984 = (void*)&foo68984; +__attribute__((used)) void* use68985 = (void*)&foo68985; +__attribute__((used)) void* use68986 = (void*)&foo68986; +__attribute__((used)) void* use68987 = (void*)&foo68987; +__attribute__((used)) void* use68988 = (void*)&foo68988; +__attribute__((used)) void* use68989 = (void*)&foo68989; +__attribute__((used)) void* use68990 = (void*)&foo68990; +__attribute__((used)) void* use68991 = (void*)&foo68991; +__attribute__((used)) void* use68992 = (void*)&foo68992; +__attribute__((used)) void* use68993 = (void*)&foo68993; +__attribute__((used)) void* use68994 = (void*)&foo68994; +__attribute__((used)) void* use68995 = (void*)&foo68995; +__attribute__((used)) void* use68996 = (void*)&foo68996; +__attribute__((used)) void* use68997 = (void*)&foo68997; +__attribute__((used)) void* use68998 = (void*)&foo68998; +__attribute__((used)) void* use68999 = (void*)&foo68999; +__attribute__((used)) void* use69000 = (void*)&foo69000; +__attribute__((used)) void* use69001 = (void*)&foo69001; +__attribute__((used)) void* use69002 = (void*)&foo69002; +__attribute__((used)) void* use69003 = (void*)&foo69003; +__attribute__((used)) void* use69004 = (void*)&foo69004; +__attribute__((used)) void* use69005 = (void*)&foo69005; +__attribute__((used)) void* use69006 = (void*)&foo69006; +__attribute__((used)) void* use69007 = (void*)&foo69007; +__attribute__((used)) void* use69008 = (void*)&foo69008; +__attribute__((used)) void* use69009 = (void*)&foo69009; +__attribute__((used)) void* use69010 = (void*)&foo69010; +__attribute__((used)) void* use69011 = (void*)&foo69011; +__attribute__((used)) void* use69012 = (void*)&foo69012; +__attribute__((used)) void* use69013 = (void*)&foo69013; +__attribute__((used)) void* use69014 = (void*)&foo69014; +__attribute__((used)) void* use69015 = (void*)&foo69015; +__attribute__((used)) void* use69016 = (void*)&foo69016; +__attribute__((used)) void* use69017 = (void*)&foo69017; +__attribute__((used)) void* use69018 = (void*)&foo69018; +__attribute__((used)) void* use69019 = (void*)&foo69019; +__attribute__((used)) void* use69020 = (void*)&foo69020; +__attribute__((used)) void* use69021 = (void*)&foo69021; +__attribute__((used)) void* use69022 = (void*)&foo69022; +__attribute__((used)) void* use69023 = (void*)&foo69023; +__attribute__((used)) void* use69024 = (void*)&foo69024; +__attribute__((used)) void* use69025 = (void*)&foo69025; +__attribute__((used)) void* use69026 = (void*)&foo69026; +__attribute__((used)) void* use69027 = (void*)&foo69027; +__attribute__((used)) void* use69028 = (void*)&foo69028; +__attribute__((used)) void* use69029 = (void*)&foo69029; +__attribute__((used)) void* use69030 = (void*)&foo69030; +__attribute__((used)) void* use69031 = (void*)&foo69031; +__attribute__((used)) void* use69032 = (void*)&foo69032; +__attribute__((used)) void* use69033 = (void*)&foo69033; +__attribute__((used)) void* use69034 = (void*)&foo69034; +__attribute__((used)) void* use69035 = (void*)&foo69035; +__attribute__((used)) void* use69036 = (void*)&foo69036; +__attribute__((used)) void* use69037 = (void*)&foo69037; +__attribute__((used)) void* use69038 = (void*)&foo69038; +__attribute__((used)) void* use69039 = (void*)&foo69039; +__attribute__((used)) void* use69040 = (void*)&foo69040; +__attribute__((used)) void* use69041 = (void*)&foo69041; +__attribute__((used)) void* use69042 = (void*)&foo69042; +__attribute__((used)) void* use69043 = (void*)&foo69043; +__attribute__((used)) void* use69044 = (void*)&foo69044; +__attribute__((used)) void* use69045 = (void*)&foo69045; +__attribute__((used)) void* use69046 = (void*)&foo69046; +__attribute__((used)) void* use69047 = (void*)&foo69047; +__attribute__((used)) void* use69048 = (void*)&foo69048; +__attribute__((used)) void* use69049 = (void*)&foo69049; +__attribute__((used)) void* use69050 = (void*)&foo69050; +__attribute__((used)) void* use69051 = (void*)&foo69051; +__attribute__((used)) void* use69052 = (void*)&foo69052; +__attribute__((used)) void* use69053 = (void*)&foo69053; +__attribute__((used)) void* use69054 = (void*)&foo69054; +__attribute__((used)) void* use69055 = (void*)&foo69055; +__attribute__((used)) void* use69056 = (void*)&foo69056; +__attribute__((used)) void* use69057 = (void*)&foo69057; +__attribute__((used)) void* use69058 = (void*)&foo69058; +__attribute__((used)) void* use69059 = (void*)&foo69059; +__attribute__((used)) void* use69060 = (void*)&foo69060; +__attribute__((used)) void* use69061 = (void*)&foo69061; +__attribute__((used)) void* use69062 = (void*)&foo69062; +__attribute__((used)) void* use69063 = (void*)&foo69063; +__attribute__((used)) void* use69064 = (void*)&foo69064; +__attribute__((used)) void* use69065 = (void*)&foo69065; +__attribute__((used)) void* use69066 = (void*)&foo69066; +__attribute__((used)) void* use69067 = (void*)&foo69067; +__attribute__((used)) void* use69068 = (void*)&foo69068; +__attribute__((used)) void* use69069 = (void*)&foo69069; +__attribute__((used)) void* use69070 = (void*)&foo69070; +__attribute__((used)) void* use69071 = (void*)&foo69071; +__attribute__((used)) void* use69072 = (void*)&foo69072; +__attribute__((used)) void* use69073 = (void*)&foo69073; +__attribute__((used)) void* use69074 = (void*)&foo69074; +__attribute__((used)) void* use69075 = (void*)&foo69075; +__attribute__((used)) void* use69076 = (void*)&foo69076; +__attribute__((used)) void* use69077 = (void*)&foo69077; +__attribute__((used)) void* use69078 = (void*)&foo69078; +__attribute__((used)) void* use69079 = (void*)&foo69079; +__attribute__((used)) void* use69080 = (void*)&foo69080; +__attribute__((used)) void* use69081 = (void*)&foo69081; +__attribute__((used)) void* use69082 = (void*)&foo69082; +__attribute__((used)) void* use69083 = (void*)&foo69083; +__attribute__((used)) void* use69084 = (void*)&foo69084; +__attribute__((used)) void* use69085 = (void*)&foo69085; +__attribute__((used)) void* use69086 = (void*)&foo69086; +__attribute__((used)) void* use69087 = (void*)&foo69087; +__attribute__((used)) void* use69088 = (void*)&foo69088; +__attribute__((used)) void* use69089 = (void*)&foo69089; +__attribute__((used)) void* use69090 = (void*)&foo69090; +__attribute__((used)) void* use69091 = (void*)&foo69091; +__attribute__((used)) void* use69092 = (void*)&foo69092; +__attribute__((used)) void* use69093 = (void*)&foo69093; +__attribute__((used)) void* use69094 = (void*)&foo69094; +__attribute__((used)) void* use69095 = (void*)&foo69095; +__attribute__((used)) void* use69096 = (void*)&foo69096; +__attribute__((used)) void* use69097 = (void*)&foo69097; +__attribute__((used)) void* use69098 = (void*)&foo69098; +__attribute__((used)) void* use69099 = (void*)&foo69099; +__attribute__((used)) void* use69100 = (void*)&foo69100; +__attribute__((used)) void* use69101 = (void*)&foo69101; +__attribute__((used)) void* use69102 = (void*)&foo69102; +__attribute__((used)) void* use69103 = (void*)&foo69103; +__attribute__((used)) void* use69104 = (void*)&foo69104; +__attribute__((used)) void* use69105 = (void*)&foo69105; +__attribute__((used)) void* use69106 = (void*)&foo69106; +__attribute__((used)) void* use69107 = (void*)&foo69107; +__attribute__((used)) void* use69108 = (void*)&foo69108; +__attribute__((used)) void* use69109 = (void*)&foo69109; +__attribute__((used)) void* use69110 = (void*)&foo69110; +__attribute__((used)) void* use69111 = (void*)&foo69111; +__attribute__((used)) void* use69112 = (void*)&foo69112; +__attribute__((used)) void* use69113 = (void*)&foo69113; +__attribute__((used)) void* use69114 = (void*)&foo69114; +__attribute__((used)) void* use69115 = (void*)&foo69115; +__attribute__((used)) void* use69116 = (void*)&foo69116; +__attribute__((used)) void* use69117 = (void*)&foo69117; +__attribute__((used)) void* use69118 = (void*)&foo69118; +__attribute__((used)) void* use69119 = (void*)&foo69119; +__attribute__((used)) void* use69120 = (void*)&foo69120; +__attribute__((used)) void* use69121 = (void*)&foo69121; +__attribute__((used)) void* use69122 = (void*)&foo69122; +__attribute__((used)) void* use69123 = (void*)&foo69123; +__attribute__((used)) void* use69124 = (void*)&foo69124; +__attribute__((used)) void* use69125 = (void*)&foo69125; +__attribute__((used)) void* use69126 = (void*)&foo69126; +__attribute__((used)) void* use69127 = (void*)&foo69127; +__attribute__((used)) void* use69128 = (void*)&foo69128; +__attribute__((used)) void* use69129 = (void*)&foo69129; +__attribute__((used)) void* use69130 = (void*)&foo69130; +__attribute__((used)) void* use69131 = (void*)&foo69131; +__attribute__((used)) void* use69132 = (void*)&foo69132; +__attribute__((used)) void* use69133 = (void*)&foo69133; +__attribute__((used)) void* use69134 = (void*)&foo69134; +__attribute__((used)) void* use69135 = (void*)&foo69135; +__attribute__((used)) void* use69136 = (void*)&foo69136; +__attribute__((used)) void* use69137 = (void*)&foo69137; +__attribute__((used)) void* use69138 = (void*)&foo69138; +__attribute__((used)) void* use69139 = (void*)&foo69139; +__attribute__((used)) void* use69140 = (void*)&foo69140; +__attribute__((used)) void* use69141 = (void*)&foo69141; +__attribute__((used)) void* use69142 = (void*)&foo69142; +__attribute__((used)) void* use69143 = (void*)&foo69143; +__attribute__((used)) void* use69144 = (void*)&foo69144; +__attribute__((used)) void* use69145 = (void*)&foo69145; +__attribute__((used)) void* use69146 = (void*)&foo69146; +__attribute__((used)) void* use69147 = (void*)&foo69147; +__attribute__((used)) void* use69148 = (void*)&foo69148; +__attribute__((used)) void* use69149 = (void*)&foo69149; +__attribute__((used)) void* use69150 = (void*)&foo69150; +__attribute__((used)) void* use69151 = (void*)&foo69151; +__attribute__((used)) void* use69152 = (void*)&foo69152; +__attribute__((used)) void* use69153 = (void*)&foo69153; +__attribute__((used)) void* use69154 = (void*)&foo69154; +__attribute__((used)) void* use69155 = (void*)&foo69155; +__attribute__((used)) void* use69156 = (void*)&foo69156; +__attribute__((used)) void* use69157 = (void*)&foo69157; +__attribute__((used)) void* use69158 = (void*)&foo69158; +__attribute__((used)) void* use69159 = (void*)&foo69159; +__attribute__((used)) void* use69160 = (void*)&foo69160; +__attribute__((used)) void* use69161 = (void*)&foo69161; +__attribute__((used)) void* use69162 = (void*)&foo69162; +__attribute__((used)) void* use69163 = (void*)&foo69163; +__attribute__((used)) void* use69164 = (void*)&foo69164; +__attribute__((used)) void* use69165 = (void*)&foo69165; +__attribute__((used)) void* use69166 = (void*)&foo69166; +__attribute__((used)) void* use69167 = (void*)&foo69167; +__attribute__((used)) void* use69168 = (void*)&foo69168; +__attribute__((used)) void* use69169 = (void*)&foo69169; +__attribute__((used)) void* use69170 = (void*)&foo69170; +__attribute__((used)) void* use69171 = (void*)&foo69171; +__attribute__((used)) void* use69172 = (void*)&foo69172; +__attribute__((used)) void* use69173 = (void*)&foo69173; +__attribute__((used)) void* use69174 = (void*)&foo69174; +__attribute__((used)) void* use69175 = (void*)&foo69175; +__attribute__((used)) void* use69176 = (void*)&foo69176; +__attribute__((used)) void* use69177 = (void*)&foo69177; +__attribute__((used)) void* use69178 = (void*)&foo69178; +__attribute__((used)) void* use69179 = (void*)&foo69179; +__attribute__((used)) void* use69180 = (void*)&foo69180; +__attribute__((used)) void* use69181 = (void*)&foo69181; +__attribute__((used)) void* use69182 = (void*)&foo69182; +__attribute__((used)) void* use69183 = (void*)&foo69183; +__attribute__((used)) void* use69184 = (void*)&foo69184; +__attribute__((used)) void* use69185 = (void*)&foo69185; +__attribute__((used)) void* use69186 = (void*)&foo69186; +__attribute__((used)) void* use69187 = (void*)&foo69187; +__attribute__((used)) void* use69188 = (void*)&foo69188; +__attribute__((used)) void* use69189 = (void*)&foo69189; +__attribute__((used)) void* use69190 = (void*)&foo69190; +__attribute__((used)) void* use69191 = (void*)&foo69191; +__attribute__((used)) void* use69192 = (void*)&foo69192; +__attribute__((used)) void* use69193 = (void*)&foo69193; +__attribute__((used)) void* use69194 = (void*)&foo69194; +__attribute__((used)) void* use69195 = (void*)&foo69195; +__attribute__((used)) void* use69196 = (void*)&foo69196; +__attribute__((used)) void* use69197 = (void*)&foo69197; +__attribute__((used)) void* use69198 = (void*)&foo69198; +__attribute__((used)) void* use69199 = (void*)&foo69199; +__attribute__((used)) void* use69200 = (void*)&foo69200; +__attribute__((used)) void* use69201 = (void*)&foo69201; +__attribute__((used)) void* use69202 = (void*)&foo69202; +__attribute__((used)) void* use69203 = (void*)&foo69203; +__attribute__((used)) void* use69204 = (void*)&foo69204; +__attribute__((used)) void* use69205 = (void*)&foo69205; +__attribute__((used)) void* use69206 = (void*)&foo69206; +__attribute__((used)) void* use69207 = (void*)&foo69207; +__attribute__((used)) void* use69208 = (void*)&foo69208; +__attribute__((used)) void* use69209 = (void*)&foo69209; +__attribute__((used)) void* use69210 = (void*)&foo69210; +__attribute__((used)) void* use69211 = (void*)&foo69211; +__attribute__((used)) void* use69212 = (void*)&foo69212; +__attribute__((used)) void* use69213 = (void*)&foo69213; +__attribute__((used)) void* use69214 = (void*)&foo69214; +__attribute__((used)) void* use69215 = (void*)&foo69215; +__attribute__((used)) void* use69216 = (void*)&foo69216; +__attribute__((used)) void* use69217 = (void*)&foo69217; +__attribute__((used)) void* use69218 = (void*)&foo69218; +__attribute__((used)) void* use69219 = (void*)&foo69219; +__attribute__((used)) void* use69220 = (void*)&foo69220; +__attribute__((used)) void* use69221 = (void*)&foo69221; +__attribute__((used)) void* use69222 = (void*)&foo69222; +__attribute__((used)) void* use69223 = (void*)&foo69223; +__attribute__((used)) void* use69224 = (void*)&foo69224; +__attribute__((used)) void* use69225 = (void*)&foo69225; +__attribute__((used)) void* use69226 = (void*)&foo69226; +__attribute__((used)) void* use69227 = (void*)&foo69227; +__attribute__((used)) void* use69228 = (void*)&foo69228; +__attribute__((used)) void* use69229 = (void*)&foo69229; +__attribute__((used)) void* use69230 = (void*)&foo69230; +__attribute__((used)) void* use69231 = (void*)&foo69231; +__attribute__((used)) void* use69232 = (void*)&foo69232; +__attribute__((used)) void* use69233 = (void*)&foo69233; +__attribute__((used)) void* use69234 = (void*)&foo69234; +__attribute__((used)) void* use69235 = (void*)&foo69235; +__attribute__((used)) void* use69236 = (void*)&foo69236; +__attribute__((used)) void* use69237 = (void*)&foo69237; +__attribute__((used)) void* use69238 = (void*)&foo69238; +__attribute__((used)) void* use69239 = (void*)&foo69239; +__attribute__((used)) void* use69240 = (void*)&foo69240; +__attribute__((used)) void* use69241 = (void*)&foo69241; +__attribute__((used)) void* use69242 = (void*)&foo69242; +__attribute__((used)) void* use69243 = (void*)&foo69243; +__attribute__((used)) void* use69244 = (void*)&foo69244; +__attribute__((used)) void* use69245 = (void*)&foo69245; +__attribute__((used)) void* use69246 = (void*)&foo69246; +__attribute__((used)) void* use69247 = (void*)&foo69247; +__attribute__((used)) void* use69248 = (void*)&foo69248; +__attribute__((used)) void* use69249 = (void*)&foo69249; +__attribute__((used)) void* use69250 = (void*)&foo69250; +__attribute__((used)) void* use69251 = (void*)&foo69251; +__attribute__((used)) void* use69252 = (void*)&foo69252; +__attribute__((used)) void* use69253 = (void*)&foo69253; +__attribute__((used)) void* use69254 = (void*)&foo69254; +__attribute__((used)) void* use69255 = (void*)&foo69255; +__attribute__((used)) void* use69256 = (void*)&foo69256; +__attribute__((used)) void* use69257 = (void*)&foo69257; +__attribute__((used)) void* use69258 = (void*)&foo69258; +__attribute__((used)) void* use69259 = (void*)&foo69259; +__attribute__((used)) void* use69260 = (void*)&foo69260; +__attribute__((used)) void* use69261 = (void*)&foo69261; +__attribute__((used)) void* use69262 = (void*)&foo69262; +__attribute__((used)) void* use69263 = (void*)&foo69263; +__attribute__((used)) void* use69264 = (void*)&foo69264; +__attribute__((used)) void* use69265 = (void*)&foo69265; +__attribute__((used)) void* use69266 = (void*)&foo69266; +__attribute__((used)) void* use69267 = (void*)&foo69267; +__attribute__((used)) void* use69268 = (void*)&foo69268; +__attribute__((used)) void* use69269 = (void*)&foo69269; +__attribute__((used)) void* use69270 = (void*)&foo69270; +__attribute__((used)) void* use69271 = (void*)&foo69271; +__attribute__((used)) void* use69272 = (void*)&foo69272; +__attribute__((used)) void* use69273 = (void*)&foo69273; +__attribute__((used)) void* use69274 = (void*)&foo69274; +__attribute__((used)) void* use69275 = (void*)&foo69275; +__attribute__((used)) void* use69276 = (void*)&foo69276; +__attribute__((used)) void* use69277 = (void*)&foo69277; +__attribute__((used)) void* use69278 = (void*)&foo69278; +__attribute__((used)) void* use69279 = (void*)&foo69279; +__attribute__((used)) void* use69280 = (void*)&foo69280; +__attribute__((used)) void* use69281 = (void*)&foo69281; +__attribute__((used)) void* use69282 = (void*)&foo69282; +__attribute__((used)) void* use69283 = (void*)&foo69283; +__attribute__((used)) void* use69284 = (void*)&foo69284; +__attribute__((used)) void* use69285 = (void*)&foo69285; +__attribute__((used)) void* use69286 = (void*)&foo69286; +__attribute__((used)) void* use69287 = (void*)&foo69287; +__attribute__((used)) void* use69288 = (void*)&foo69288; +__attribute__((used)) void* use69289 = (void*)&foo69289; +__attribute__((used)) void* use69290 = (void*)&foo69290; +__attribute__((used)) void* use69291 = (void*)&foo69291; +__attribute__((used)) void* use69292 = (void*)&foo69292; +__attribute__((used)) void* use69293 = (void*)&foo69293; +__attribute__((used)) void* use69294 = (void*)&foo69294; +__attribute__((used)) void* use69295 = (void*)&foo69295; +__attribute__((used)) void* use69296 = (void*)&foo69296; +__attribute__((used)) void* use69297 = (void*)&foo69297; +__attribute__((used)) void* use69298 = (void*)&foo69298; +__attribute__((used)) void* use69299 = (void*)&foo69299; +__attribute__((used)) void* use69300 = (void*)&foo69300; +__attribute__((used)) void* use69301 = (void*)&foo69301; +__attribute__((used)) void* use69302 = (void*)&foo69302; +__attribute__((used)) void* use69303 = (void*)&foo69303; +__attribute__((used)) void* use69304 = (void*)&foo69304; +__attribute__((used)) void* use69305 = (void*)&foo69305; +__attribute__((used)) void* use69306 = (void*)&foo69306; +__attribute__((used)) void* use69307 = (void*)&foo69307; +__attribute__((used)) void* use69308 = (void*)&foo69308; +__attribute__((used)) void* use69309 = (void*)&foo69309; +__attribute__((used)) void* use69310 = (void*)&foo69310; +__attribute__((used)) void* use69311 = (void*)&foo69311; +__attribute__((used)) void* use69312 = (void*)&foo69312; +__attribute__((used)) void* use69313 = (void*)&foo69313; +__attribute__((used)) void* use69314 = (void*)&foo69314; +__attribute__((used)) void* use69315 = (void*)&foo69315; +__attribute__((used)) void* use69316 = (void*)&foo69316; +__attribute__((used)) void* use69317 = (void*)&foo69317; +__attribute__((used)) void* use69318 = (void*)&foo69318; +__attribute__((used)) void* use69319 = (void*)&foo69319; +__attribute__((used)) void* use69320 = (void*)&foo69320; +__attribute__((used)) void* use69321 = (void*)&foo69321; +__attribute__((used)) void* use69322 = (void*)&foo69322; +__attribute__((used)) void* use69323 = (void*)&foo69323; +__attribute__((used)) void* use69324 = (void*)&foo69324; +__attribute__((used)) void* use69325 = (void*)&foo69325; +__attribute__((used)) void* use69326 = (void*)&foo69326; +__attribute__((used)) void* use69327 = (void*)&foo69327; +__attribute__((used)) void* use69328 = (void*)&foo69328; +__attribute__((used)) void* use69329 = (void*)&foo69329; +__attribute__((used)) void* use69330 = (void*)&foo69330; +__attribute__((used)) void* use69331 = (void*)&foo69331; +__attribute__((used)) void* use69332 = (void*)&foo69332; +__attribute__((used)) void* use69333 = (void*)&foo69333; +__attribute__((used)) void* use69334 = (void*)&foo69334; +__attribute__((used)) void* use69335 = (void*)&foo69335; +__attribute__((used)) void* use69336 = (void*)&foo69336; +__attribute__((used)) void* use69337 = (void*)&foo69337; +__attribute__((used)) void* use69338 = (void*)&foo69338; +__attribute__((used)) void* use69339 = (void*)&foo69339; +__attribute__((used)) void* use69340 = (void*)&foo69340; +__attribute__((used)) void* use69341 = (void*)&foo69341; +__attribute__((used)) void* use69342 = (void*)&foo69342; +__attribute__((used)) void* use69343 = (void*)&foo69343; +__attribute__((used)) void* use69344 = (void*)&foo69344; +__attribute__((used)) void* use69345 = (void*)&foo69345; +__attribute__((used)) void* use69346 = (void*)&foo69346; +__attribute__((used)) void* use69347 = (void*)&foo69347; +__attribute__((used)) void* use69348 = (void*)&foo69348; +__attribute__((used)) void* use69349 = (void*)&foo69349; +__attribute__((used)) void* use69350 = (void*)&foo69350; +__attribute__((used)) void* use69351 = (void*)&foo69351; +__attribute__((used)) void* use69352 = (void*)&foo69352; +__attribute__((used)) void* use69353 = (void*)&foo69353; +__attribute__((used)) void* use69354 = (void*)&foo69354; +__attribute__((used)) void* use69355 = (void*)&foo69355; +__attribute__((used)) void* use69356 = (void*)&foo69356; +__attribute__((used)) void* use69357 = (void*)&foo69357; +__attribute__((used)) void* use69358 = (void*)&foo69358; +__attribute__((used)) void* use69359 = (void*)&foo69359; +__attribute__((used)) void* use69360 = (void*)&foo69360; +__attribute__((used)) void* use69361 = (void*)&foo69361; +__attribute__((used)) void* use69362 = (void*)&foo69362; +__attribute__((used)) void* use69363 = (void*)&foo69363; +__attribute__((used)) void* use69364 = (void*)&foo69364; +__attribute__((used)) void* use69365 = (void*)&foo69365; +__attribute__((used)) void* use69366 = (void*)&foo69366; +__attribute__((used)) void* use69367 = (void*)&foo69367; +__attribute__((used)) void* use69368 = (void*)&foo69368; +__attribute__((used)) void* use69369 = (void*)&foo69369; +__attribute__((used)) void* use69370 = (void*)&foo69370; +__attribute__((used)) void* use69371 = (void*)&foo69371; +__attribute__((used)) void* use69372 = (void*)&foo69372; +__attribute__((used)) void* use69373 = (void*)&foo69373; +__attribute__((used)) void* use69374 = (void*)&foo69374; +__attribute__((used)) void* use69375 = (void*)&foo69375; +__attribute__((used)) void* use69376 = (void*)&foo69376; +__attribute__((used)) void* use69377 = (void*)&foo69377; +__attribute__((used)) void* use69378 = (void*)&foo69378; +__attribute__((used)) void* use69379 = (void*)&foo69379; +__attribute__((used)) void* use69380 = (void*)&foo69380; +__attribute__((used)) void* use69381 = (void*)&foo69381; +__attribute__((used)) void* use69382 = (void*)&foo69382; +__attribute__((used)) void* use69383 = (void*)&foo69383; +__attribute__((used)) void* use69384 = (void*)&foo69384; +__attribute__((used)) void* use69385 = (void*)&foo69385; +__attribute__((used)) void* use69386 = (void*)&foo69386; +__attribute__((used)) void* use69387 = (void*)&foo69387; +__attribute__((used)) void* use69388 = (void*)&foo69388; +__attribute__((used)) void* use69389 = (void*)&foo69389; +__attribute__((used)) void* use69390 = (void*)&foo69390; +__attribute__((used)) void* use69391 = (void*)&foo69391; +__attribute__((used)) void* use69392 = (void*)&foo69392; +__attribute__((used)) void* use69393 = (void*)&foo69393; +__attribute__((used)) void* use69394 = (void*)&foo69394; +__attribute__((used)) void* use69395 = (void*)&foo69395; +__attribute__((used)) void* use69396 = (void*)&foo69396; +__attribute__((used)) void* use69397 = (void*)&foo69397; +__attribute__((used)) void* use69398 = (void*)&foo69398; +__attribute__((used)) void* use69399 = (void*)&foo69399; +__attribute__((used)) void* use69400 = (void*)&foo69400; +__attribute__((used)) void* use69401 = (void*)&foo69401; +__attribute__((used)) void* use69402 = (void*)&foo69402; +__attribute__((used)) void* use69403 = (void*)&foo69403; +__attribute__((used)) void* use69404 = (void*)&foo69404; +__attribute__((used)) void* use69405 = (void*)&foo69405; +__attribute__((used)) void* use69406 = (void*)&foo69406; +__attribute__((used)) void* use69407 = (void*)&foo69407; +__attribute__((used)) void* use69408 = (void*)&foo69408; +__attribute__((used)) void* use69409 = (void*)&foo69409; +__attribute__((used)) void* use69410 = (void*)&foo69410; +__attribute__((used)) void* use69411 = (void*)&foo69411; +__attribute__((used)) void* use69412 = (void*)&foo69412; +__attribute__((used)) void* use69413 = (void*)&foo69413; +__attribute__((used)) void* use69414 = (void*)&foo69414; +__attribute__((used)) void* use69415 = (void*)&foo69415; +__attribute__((used)) void* use69416 = (void*)&foo69416; +__attribute__((used)) void* use69417 = (void*)&foo69417; +__attribute__((used)) void* use69418 = (void*)&foo69418; +__attribute__((used)) void* use69419 = (void*)&foo69419; +__attribute__((used)) void* use69420 = (void*)&foo69420; +__attribute__((used)) void* use69421 = (void*)&foo69421; +__attribute__((used)) void* use69422 = (void*)&foo69422; +__attribute__((used)) void* use69423 = (void*)&foo69423; +__attribute__((used)) void* use69424 = (void*)&foo69424; +__attribute__((used)) void* use69425 = (void*)&foo69425; +__attribute__((used)) void* use69426 = (void*)&foo69426; +__attribute__((used)) void* use69427 = (void*)&foo69427; +__attribute__((used)) void* use69428 = (void*)&foo69428; +__attribute__((used)) void* use69429 = (void*)&foo69429; +__attribute__((used)) void* use69430 = (void*)&foo69430; +__attribute__((used)) void* use69431 = (void*)&foo69431; +__attribute__((used)) void* use69432 = (void*)&foo69432; +__attribute__((used)) void* use69433 = (void*)&foo69433; +__attribute__((used)) void* use69434 = (void*)&foo69434; +__attribute__((used)) void* use69435 = (void*)&foo69435; +__attribute__((used)) void* use69436 = (void*)&foo69436; +__attribute__((used)) void* use69437 = (void*)&foo69437; +__attribute__((used)) void* use69438 = (void*)&foo69438; +__attribute__((used)) void* use69439 = (void*)&foo69439; +__attribute__((used)) void* use69440 = (void*)&foo69440; +__attribute__((used)) void* use69441 = (void*)&foo69441; +__attribute__((used)) void* use69442 = (void*)&foo69442; +__attribute__((used)) void* use69443 = (void*)&foo69443; +__attribute__((used)) void* use69444 = (void*)&foo69444; +__attribute__((used)) void* use69445 = (void*)&foo69445; +__attribute__((used)) void* use69446 = (void*)&foo69446; +__attribute__((used)) void* use69447 = (void*)&foo69447; +__attribute__((used)) void* use69448 = (void*)&foo69448; +__attribute__((used)) void* use69449 = (void*)&foo69449; +__attribute__((used)) void* use69450 = (void*)&foo69450; +__attribute__((used)) void* use69451 = (void*)&foo69451; +__attribute__((used)) void* use69452 = (void*)&foo69452; +__attribute__((used)) void* use69453 = (void*)&foo69453; +__attribute__((used)) void* use69454 = (void*)&foo69454; +__attribute__((used)) void* use69455 = (void*)&foo69455; +__attribute__((used)) void* use69456 = (void*)&foo69456; +__attribute__((used)) void* use69457 = (void*)&foo69457; +__attribute__((used)) void* use69458 = (void*)&foo69458; +__attribute__((used)) void* use69459 = (void*)&foo69459; +__attribute__((used)) void* use69460 = (void*)&foo69460; +__attribute__((used)) void* use69461 = (void*)&foo69461; +__attribute__((used)) void* use69462 = (void*)&foo69462; +__attribute__((used)) void* use69463 = (void*)&foo69463; +__attribute__((used)) void* use69464 = (void*)&foo69464; +__attribute__((used)) void* use69465 = (void*)&foo69465; +__attribute__((used)) void* use69466 = (void*)&foo69466; +__attribute__((used)) void* use69467 = (void*)&foo69467; +__attribute__((used)) void* use69468 = (void*)&foo69468; +__attribute__((used)) void* use69469 = (void*)&foo69469; +__attribute__((used)) void* use69470 = (void*)&foo69470; +__attribute__((used)) void* use69471 = (void*)&foo69471; +__attribute__((used)) void* use69472 = (void*)&foo69472; +__attribute__((used)) void* use69473 = (void*)&foo69473; +__attribute__((used)) void* use69474 = (void*)&foo69474; +__attribute__((used)) void* use69475 = (void*)&foo69475; +__attribute__((used)) void* use69476 = (void*)&foo69476; +__attribute__((used)) void* use69477 = (void*)&foo69477; +__attribute__((used)) void* use69478 = (void*)&foo69478; +__attribute__((used)) void* use69479 = (void*)&foo69479; +__attribute__((used)) void* use69480 = (void*)&foo69480; +__attribute__((used)) void* use69481 = (void*)&foo69481; +__attribute__((used)) void* use69482 = (void*)&foo69482; +__attribute__((used)) void* use69483 = (void*)&foo69483; +__attribute__((used)) void* use69484 = (void*)&foo69484; +__attribute__((used)) void* use69485 = (void*)&foo69485; +__attribute__((used)) void* use69486 = (void*)&foo69486; +__attribute__((used)) void* use69487 = (void*)&foo69487; +__attribute__((used)) void* use69488 = (void*)&foo69488; +__attribute__((used)) void* use69489 = (void*)&foo69489; +__attribute__((used)) void* use69490 = (void*)&foo69490; +__attribute__((used)) void* use69491 = (void*)&foo69491; +__attribute__((used)) void* use69492 = (void*)&foo69492; +__attribute__((used)) void* use69493 = (void*)&foo69493; +__attribute__((used)) void* use69494 = (void*)&foo69494; +__attribute__((used)) void* use69495 = (void*)&foo69495; +__attribute__((used)) void* use69496 = (void*)&foo69496; +__attribute__((used)) void* use69497 = (void*)&foo69497; +__attribute__((used)) void* use69498 = (void*)&foo69498; +__attribute__((used)) void* use69499 = (void*)&foo69499; +__attribute__((used)) void* use69500 = (void*)&foo69500; +__attribute__((used)) void* use69501 = (void*)&foo69501; +__attribute__((used)) void* use69502 = (void*)&foo69502; +__attribute__((used)) void* use69503 = (void*)&foo69503; +__attribute__((used)) void* use69504 = (void*)&foo69504; +__attribute__((used)) void* use69505 = (void*)&foo69505; +__attribute__((used)) void* use69506 = (void*)&foo69506; +__attribute__((used)) void* use69507 = (void*)&foo69507; +__attribute__((used)) void* use69508 = (void*)&foo69508; +__attribute__((used)) void* use69509 = (void*)&foo69509; +__attribute__((used)) void* use69510 = (void*)&foo69510; +__attribute__((used)) void* use69511 = (void*)&foo69511; +__attribute__((used)) void* use69512 = (void*)&foo69512; +__attribute__((used)) void* use69513 = (void*)&foo69513; +__attribute__((used)) void* use69514 = (void*)&foo69514; +__attribute__((used)) void* use69515 = (void*)&foo69515; +__attribute__((used)) void* use69516 = (void*)&foo69516; +__attribute__((used)) void* use69517 = (void*)&foo69517; +__attribute__((used)) void* use69518 = (void*)&foo69518; +__attribute__((used)) void* use69519 = (void*)&foo69519; +__attribute__((used)) void* use69520 = (void*)&foo69520; +__attribute__((used)) void* use69521 = (void*)&foo69521; +__attribute__((used)) void* use69522 = (void*)&foo69522; +__attribute__((used)) void* use69523 = (void*)&foo69523; +__attribute__((used)) void* use69524 = (void*)&foo69524; +__attribute__((used)) void* use69525 = (void*)&foo69525; +__attribute__((used)) void* use69526 = (void*)&foo69526; +__attribute__((used)) void* use69527 = (void*)&foo69527; +__attribute__((used)) void* use69528 = (void*)&foo69528; +__attribute__((used)) void* use69529 = (void*)&foo69529; +__attribute__((used)) void* use69530 = (void*)&foo69530; +__attribute__((used)) void* use69531 = (void*)&foo69531; +__attribute__((used)) void* use69532 = (void*)&foo69532; +__attribute__((used)) void* use69533 = (void*)&foo69533; +__attribute__((used)) void* use69534 = (void*)&foo69534; +__attribute__((used)) void* use69535 = (void*)&foo69535; +__attribute__((used)) void* use69536 = (void*)&foo69536; +__attribute__((used)) void* use69537 = (void*)&foo69537; +__attribute__((used)) void* use69538 = (void*)&foo69538; +__attribute__((used)) void* use69539 = (void*)&foo69539; +__attribute__((used)) void* use69540 = (void*)&foo69540; +__attribute__((used)) void* use69541 = (void*)&foo69541; +__attribute__((used)) void* use69542 = (void*)&foo69542; +__attribute__((used)) void* use69543 = (void*)&foo69543; +__attribute__((used)) void* use69544 = (void*)&foo69544; +__attribute__((used)) void* use69545 = (void*)&foo69545; +__attribute__((used)) void* use69546 = (void*)&foo69546; +__attribute__((used)) void* use69547 = (void*)&foo69547; +__attribute__((used)) void* use69548 = (void*)&foo69548; +__attribute__((used)) void* use69549 = (void*)&foo69549; +__attribute__((used)) void* use69550 = (void*)&foo69550; +__attribute__((used)) void* use69551 = (void*)&foo69551; +__attribute__((used)) void* use69552 = (void*)&foo69552; +__attribute__((used)) void* use69553 = (void*)&foo69553; +__attribute__((used)) void* use69554 = (void*)&foo69554; +__attribute__((used)) void* use69555 = (void*)&foo69555; +__attribute__((used)) void* use69556 = (void*)&foo69556; +__attribute__((used)) void* use69557 = (void*)&foo69557; +__attribute__((used)) void* use69558 = (void*)&foo69558; +__attribute__((used)) void* use69559 = (void*)&foo69559; +__attribute__((used)) void* use69560 = (void*)&foo69560; +__attribute__((used)) void* use69561 = (void*)&foo69561; +__attribute__((used)) void* use69562 = (void*)&foo69562; +__attribute__((used)) void* use69563 = (void*)&foo69563; +__attribute__((used)) void* use69564 = (void*)&foo69564; +__attribute__((used)) void* use69565 = (void*)&foo69565; +__attribute__((used)) void* use69566 = (void*)&foo69566; +__attribute__((used)) void* use69567 = (void*)&foo69567; +__attribute__((used)) void* use69568 = (void*)&foo69568; +__attribute__((used)) void* use69569 = (void*)&foo69569; +__attribute__((used)) void* use69570 = (void*)&foo69570; +__attribute__((used)) void* use69571 = (void*)&foo69571; +__attribute__((used)) void* use69572 = (void*)&foo69572; +__attribute__((used)) void* use69573 = (void*)&foo69573; +__attribute__((used)) void* use69574 = (void*)&foo69574; +__attribute__((used)) void* use69575 = (void*)&foo69575; +__attribute__((used)) void* use69576 = (void*)&foo69576; +__attribute__((used)) void* use69577 = (void*)&foo69577; +__attribute__((used)) void* use69578 = (void*)&foo69578; +__attribute__((used)) void* use69579 = (void*)&foo69579; +__attribute__((used)) void* use69580 = (void*)&foo69580; +__attribute__((used)) void* use69581 = (void*)&foo69581; +__attribute__((used)) void* use69582 = (void*)&foo69582; +__attribute__((used)) void* use69583 = (void*)&foo69583; +__attribute__((used)) void* use69584 = (void*)&foo69584; +__attribute__((used)) void* use69585 = (void*)&foo69585; +__attribute__((used)) void* use69586 = (void*)&foo69586; +__attribute__((used)) void* use69587 = (void*)&foo69587; +__attribute__((used)) void* use69588 = (void*)&foo69588; +__attribute__((used)) void* use69589 = (void*)&foo69589; +__attribute__((used)) void* use69590 = (void*)&foo69590; +__attribute__((used)) void* use69591 = (void*)&foo69591; +__attribute__((used)) void* use69592 = (void*)&foo69592; +__attribute__((used)) void* use69593 = (void*)&foo69593; +__attribute__((used)) void* use69594 = (void*)&foo69594; +__attribute__((used)) void* use69595 = (void*)&foo69595; +__attribute__((used)) void* use69596 = (void*)&foo69596; +__attribute__((used)) void* use69597 = (void*)&foo69597; +__attribute__((used)) void* use69598 = (void*)&foo69598; +__attribute__((used)) void* use69599 = (void*)&foo69599; +__attribute__((used)) void* use69600 = (void*)&foo69600; +__attribute__((used)) void* use69601 = (void*)&foo69601; +__attribute__((used)) void* use69602 = (void*)&foo69602; +__attribute__((used)) void* use69603 = (void*)&foo69603; +__attribute__((used)) void* use69604 = (void*)&foo69604; +__attribute__((used)) void* use69605 = (void*)&foo69605; +__attribute__((used)) void* use69606 = (void*)&foo69606; +__attribute__((used)) void* use69607 = (void*)&foo69607; +__attribute__((used)) void* use69608 = (void*)&foo69608; +__attribute__((used)) void* use69609 = (void*)&foo69609; +__attribute__((used)) void* use69610 = (void*)&foo69610; +__attribute__((used)) void* use69611 = (void*)&foo69611; +__attribute__((used)) void* use69612 = (void*)&foo69612; +__attribute__((used)) void* use69613 = (void*)&foo69613; +__attribute__((used)) void* use69614 = (void*)&foo69614; +__attribute__((used)) void* use69615 = (void*)&foo69615; +__attribute__((used)) void* use69616 = (void*)&foo69616; +__attribute__((used)) void* use69617 = (void*)&foo69617; +__attribute__((used)) void* use69618 = (void*)&foo69618; +__attribute__((used)) void* use69619 = (void*)&foo69619; +__attribute__((used)) void* use69620 = (void*)&foo69620; +__attribute__((used)) void* use69621 = (void*)&foo69621; +__attribute__((used)) void* use69622 = (void*)&foo69622; +__attribute__((used)) void* use69623 = (void*)&foo69623; +__attribute__((used)) void* use69624 = (void*)&foo69624; +__attribute__((used)) void* use69625 = (void*)&foo69625; +__attribute__((used)) void* use69626 = (void*)&foo69626; +__attribute__((used)) void* use69627 = (void*)&foo69627; +__attribute__((used)) void* use69628 = (void*)&foo69628; +__attribute__((used)) void* use69629 = (void*)&foo69629; +__attribute__((used)) void* use69630 = (void*)&foo69630; +__attribute__((used)) void* use69631 = (void*)&foo69631; +__attribute__((used)) void* use69632 = (void*)&foo69632; +__attribute__((used)) void* use69633 = (void*)&foo69633; +__attribute__((used)) void* use69634 = (void*)&foo69634; +__attribute__((used)) void* use69635 = (void*)&foo69635; +__attribute__((used)) void* use69636 = (void*)&foo69636; +__attribute__((used)) void* use69637 = (void*)&foo69637; +__attribute__((used)) void* use69638 = (void*)&foo69638; +__attribute__((used)) void* use69639 = (void*)&foo69639; +__attribute__((used)) void* use69640 = (void*)&foo69640; +__attribute__((used)) void* use69641 = (void*)&foo69641; +__attribute__((used)) void* use69642 = (void*)&foo69642; +__attribute__((used)) void* use69643 = (void*)&foo69643; +__attribute__((used)) void* use69644 = (void*)&foo69644; +__attribute__((used)) void* use69645 = (void*)&foo69645; +__attribute__((used)) void* use69646 = (void*)&foo69646; +__attribute__((used)) void* use69647 = (void*)&foo69647; +__attribute__((used)) void* use69648 = (void*)&foo69648; +__attribute__((used)) void* use69649 = (void*)&foo69649; +__attribute__((used)) void* use69650 = (void*)&foo69650; +__attribute__((used)) void* use69651 = (void*)&foo69651; +__attribute__((used)) void* use69652 = (void*)&foo69652; +__attribute__((used)) void* use69653 = (void*)&foo69653; +__attribute__((used)) void* use69654 = (void*)&foo69654; +__attribute__((used)) void* use69655 = (void*)&foo69655; +__attribute__((used)) void* use69656 = (void*)&foo69656; +__attribute__((used)) void* use69657 = (void*)&foo69657; +__attribute__((used)) void* use69658 = (void*)&foo69658; +__attribute__((used)) void* use69659 = (void*)&foo69659; +__attribute__((used)) void* use69660 = (void*)&foo69660; +__attribute__((used)) void* use69661 = (void*)&foo69661; +__attribute__((used)) void* use69662 = (void*)&foo69662; +__attribute__((used)) void* use69663 = (void*)&foo69663; +__attribute__((used)) void* use69664 = (void*)&foo69664; +__attribute__((used)) void* use69665 = (void*)&foo69665; +__attribute__((used)) void* use69666 = (void*)&foo69666; +__attribute__((used)) void* use69667 = (void*)&foo69667; +__attribute__((used)) void* use69668 = (void*)&foo69668; +__attribute__((used)) void* use69669 = (void*)&foo69669; +__attribute__((used)) void* use69670 = (void*)&foo69670; +__attribute__((used)) void* use69671 = (void*)&foo69671; +__attribute__((used)) void* use69672 = (void*)&foo69672; +__attribute__((used)) void* use69673 = (void*)&foo69673; +__attribute__((used)) void* use69674 = (void*)&foo69674; +__attribute__((used)) void* use69675 = (void*)&foo69675; +__attribute__((used)) void* use69676 = (void*)&foo69676; +__attribute__((used)) void* use69677 = (void*)&foo69677; +__attribute__((used)) void* use69678 = (void*)&foo69678; +__attribute__((used)) void* use69679 = (void*)&foo69679; +__attribute__((used)) void* use69680 = (void*)&foo69680; +__attribute__((used)) void* use69681 = (void*)&foo69681; +__attribute__((used)) void* use69682 = (void*)&foo69682; +__attribute__((used)) void* use69683 = (void*)&foo69683; +__attribute__((used)) void* use69684 = (void*)&foo69684; +__attribute__((used)) void* use69685 = (void*)&foo69685; +__attribute__((used)) void* use69686 = (void*)&foo69686; +__attribute__((used)) void* use69687 = (void*)&foo69687; +__attribute__((used)) void* use69688 = (void*)&foo69688; +__attribute__((used)) void* use69689 = (void*)&foo69689; +__attribute__((used)) void* use69690 = (void*)&foo69690; +__attribute__((used)) void* use69691 = (void*)&foo69691; +__attribute__((used)) void* use69692 = (void*)&foo69692; +__attribute__((used)) void* use69693 = (void*)&foo69693; +__attribute__((used)) void* use69694 = (void*)&foo69694; +__attribute__((used)) void* use69695 = (void*)&foo69695; +__attribute__((used)) void* use69696 = (void*)&foo69696; +__attribute__((used)) void* use69697 = (void*)&foo69697; +__attribute__((used)) void* use69698 = (void*)&foo69698; +__attribute__((used)) void* use69699 = (void*)&foo69699; +__attribute__((used)) void* use69700 = (void*)&foo69700; +__attribute__((used)) void* use69701 = (void*)&foo69701; +__attribute__((used)) void* use69702 = (void*)&foo69702; +__attribute__((used)) void* use69703 = (void*)&foo69703; +__attribute__((used)) void* use69704 = (void*)&foo69704; +__attribute__((used)) void* use69705 = (void*)&foo69705; +__attribute__((used)) void* use69706 = (void*)&foo69706; +__attribute__((used)) void* use69707 = (void*)&foo69707; +__attribute__((used)) void* use69708 = (void*)&foo69708; +__attribute__((used)) void* use69709 = (void*)&foo69709; +__attribute__((used)) void* use69710 = (void*)&foo69710; +__attribute__((used)) void* use69711 = (void*)&foo69711; +__attribute__((used)) void* use69712 = (void*)&foo69712; +__attribute__((used)) void* use69713 = (void*)&foo69713; +__attribute__((used)) void* use69714 = (void*)&foo69714; +__attribute__((used)) void* use69715 = (void*)&foo69715; +__attribute__((used)) void* use69716 = (void*)&foo69716; +__attribute__((used)) void* use69717 = (void*)&foo69717; +__attribute__((used)) void* use69718 = (void*)&foo69718; +__attribute__((used)) void* use69719 = (void*)&foo69719; +__attribute__((used)) void* use69720 = (void*)&foo69720; +__attribute__((used)) void* use69721 = (void*)&foo69721; +__attribute__((used)) void* use69722 = (void*)&foo69722; +__attribute__((used)) void* use69723 = (void*)&foo69723; +__attribute__((used)) void* use69724 = (void*)&foo69724; +__attribute__((used)) void* use69725 = (void*)&foo69725; +__attribute__((used)) void* use69726 = (void*)&foo69726; +__attribute__((used)) void* use69727 = (void*)&foo69727; +__attribute__((used)) void* use69728 = (void*)&foo69728; +__attribute__((used)) void* use69729 = (void*)&foo69729; +__attribute__((used)) void* use69730 = (void*)&foo69730; +__attribute__((used)) void* use69731 = (void*)&foo69731; +__attribute__((used)) void* use69732 = (void*)&foo69732; +__attribute__((used)) void* use69733 = (void*)&foo69733; +__attribute__((used)) void* use69734 = (void*)&foo69734; +__attribute__((used)) void* use69735 = (void*)&foo69735; +__attribute__((used)) void* use69736 = (void*)&foo69736; +__attribute__((used)) void* use69737 = (void*)&foo69737; +__attribute__((used)) void* use69738 = (void*)&foo69738; +__attribute__((used)) void* use69739 = (void*)&foo69739; +__attribute__((used)) void* use69740 = (void*)&foo69740; +__attribute__((used)) void* use69741 = (void*)&foo69741; +__attribute__((used)) void* use69742 = (void*)&foo69742; +__attribute__((used)) void* use69743 = (void*)&foo69743; +__attribute__((used)) void* use69744 = (void*)&foo69744; +__attribute__((used)) void* use69745 = (void*)&foo69745; +__attribute__((used)) void* use69746 = (void*)&foo69746; +__attribute__((used)) void* use69747 = (void*)&foo69747; +__attribute__((used)) void* use69748 = (void*)&foo69748; +__attribute__((used)) void* use69749 = (void*)&foo69749; +__attribute__((used)) void* use69750 = (void*)&foo69750; +__attribute__((used)) void* use69751 = (void*)&foo69751; +__attribute__((used)) void* use69752 = (void*)&foo69752; +__attribute__((used)) void* use69753 = (void*)&foo69753; +__attribute__((used)) void* use69754 = (void*)&foo69754; +__attribute__((used)) void* use69755 = (void*)&foo69755; +__attribute__((used)) void* use69756 = (void*)&foo69756; +__attribute__((used)) void* use69757 = (void*)&foo69757; +__attribute__((used)) void* use69758 = (void*)&foo69758; +__attribute__((used)) void* use69759 = (void*)&foo69759; +__attribute__((used)) void* use69760 = (void*)&foo69760; +__attribute__((used)) void* use69761 = (void*)&foo69761; +__attribute__((used)) void* use69762 = (void*)&foo69762; +__attribute__((used)) void* use69763 = (void*)&foo69763; +__attribute__((used)) void* use69764 = (void*)&foo69764; +__attribute__((used)) void* use69765 = (void*)&foo69765; +__attribute__((used)) void* use69766 = (void*)&foo69766; +__attribute__((used)) void* use69767 = (void*)&foo69767; +__attribute__((used)) void* use69768 = (void*)&foo69768; +__attribute__((used)) void* use69769 = (void*)&foo69769; +__attribute__((used)) void* use69770 = (void*)&foo69770; +__attribute__((used)) void* use69771 = (void*)&foo69771; +__attribute__((used)) void* use69772 = (void*)&foo69772; +__attribute__((used)) void* use69773 = (void*)&foo69773; +__attribute__((used)) void* use69774 = (void*)&foo69774; +__attribute__((used)) void* use69775 = (void*)&foo69775; +__attribute__((used)) void* use69776 = (void*)&foo69776; +__attribute__((used)) void* use69777 = (void*)&foo69777; +__attribute__((used)) void* use69778 = (void*)&foo69778; +__attribute__((used)) void* use69779 = (void*)&foo69779; +__attribute__((used)) void* use69780 = (void*)&foo69780; +__attribute__((used)) void* use69781 = (void*)&foo69781; +__attribute__((used)) void* use69782 = (void*)&foo69782; +__attribute__((used)) void* use69783 = (void*)&foo69783; +__attribute__((used)) void* use69784 = (void*)&foo69784; +__attribute__((used)) void* use69785 = (void*)&foo69785; +__attribute__((used)) void* use69786 = (void*)&foo69786; +__attribute__((used)) void* use69787 = (void*)&foo69787; +__attribute__((used)) void* use69788 = (void*)&foo69788; +__attribute__((used)) void* use69789 = (void*)&foo69789; +__attribute__((used)) void* use69790 = (void*)&foo69790; +__attribute__((used)) void* use69791 = (void*)&foo69791; +__attribute__((used)) void* use69792 = (void*)&foo69792; +__attribute__((used)) void* use69793 = (void*)&foo69793; +__attribute__((used)) void* use69794 = (void*)&foo69794; +__attribute__((used)) void* use69795 = (void*)&foo69795; +__attribute__((used)) void* use69796 = (void*)&foo69796; +__attribute__((used)) void* use69797 = (void*)&foo69797; +__attribute__((used)) void* use69798 = (void*)&foo69798; +__attribute__((used)) void* use69799 = (void*)&foo69799; +__attribute__((used)) void* use69800 = (void*)&foo69800; +__attribute__((used)) void* use69801 = (void*)&foo69801; +__attribute__((used)) void* use69802 = (void*)&foo69802; +__attribute__((used)) void* use69803 = (void*)&foo69803; +__attribute__((used)) void* use69804 = (void*)&foo69804; +__attribute__((used)) void* use69805 = (void*)&foo69805; +__attribute__((used)) void* use69806 = (void*)&foo69806; +__attribute__((used)) void* use69807 = (void*)&foo69807; +__attribute__((used)) void* use69808 = (void*)&foo69808; +__attribute__((used)) void* use69809 = (void*)&foo69809; +__attribute__((used)) void* use69810 = (void*)&foo69810; +__attribute__((used)) void* use69811 = (void*)&foo69811; +__attribute__((used)) void* use69812 = (void*)&foo69812; +__attribute__((used)) void* use69813 = (void*)&foo69813; +__attribute__((used)) void* use69814 = (void*)&foo69814; +__attribute__((used)) void* use69815 = (void*)&foo69815; +__attribute__((used)) void* use69816 = (void*)&foo69816; +__attribute__((used)) void* use69817 = (void*)&foo69817; +__attribute__((used)) void* use69818 = (void*)&foo69818; +__attribute__((used)) void* use69819 = (void*)&foo69819; +__attribute__((used)) void* use69820 = (void*)&foo69820; +__attribute__((used)) void* use69821 = (void*)&foo69821; +__attribute__((used)) void* use69822 = (void*)&foo69822; +__attribute__((used)) void* use69823 = (void*)&foo69823; +__attribute__((used)) void* use69824 = (void*)&foo69824; +__attribute__((used)) void* use69825 = (void*)&foo69825; +__attribute__((used)) void* use69826 = (void*)&foo69826; +__attribute__((used)) void* use69827 = (void*)&foo69827; +__attribute__((used)) void* use69828 = (void*)&foo69828; +__attribute__((used)) void* use69829 = (void*)&foo69829; +__attribute__((used)) void* use69830 = (void*)&foo69830; +__attribute__((used)) void* use69831 = (void*)&foo69831; +__attribute__((used)) void* use69832 = (void*)&foo69832; +__attribute__((used)) void* use69833 = (void*)&foo69833; +__attribute__((used)) void* use69834 = (void*)&foo69834; +__attribute__((used)) void* use69835 = (void*)&foo69835; +__attribute__((used)) void* use69836 = (void*)&foo69836; +__attribute__((used)) void* use69837 = (void*)&foo69837; +__attribute__((used)) void* use69838 = (void*)&foo69838; +__attribute__((used)) void* use69839 = (void*)&foo69839; +__attribute__((used)) void* use69840 = (void*)&foo69840; +__attribute__((used)) void* use69841 = (void*)&foo69841; +__attribute__((used)) void* use69842 = (void*)&foo69842; +__attribute__((used)) void* use69843 = (void*)&foo69843; +__attribute__((used)) void* use69844 = (void*)&foo69844; +__attribute__((used)) void* use69845 = (void*)&foo69845; +__attribute__((used)) void* use69846 = (void*)&foo69846; +__attribute__((used)) void* use69847 = (void*)&foo69847; +__attribute__((used)) void* use69848 = (void*)&foo69848; +__attribute__((used)) void* use69849 = (void*)&foo69849; +__attribute__((used)) void* use69850 = (void*)&foo69850; +__attribute__((used)) void* use69851 = (void*)&foo69851; +__attribute__((used)) void* use69852 = (void*)&foo69852; +__attribute__((used)) void* use69853 = (void*)&foo69853; +__attribute__((used)) void* use69854 = (void*)&foo69854; +__attribute__((used)) void* use69855 = (void*)&foo69855; +__attribute__((used)) void* use69856 = (void*)&foo69856; +__attribute__((used)) void* use69857 = (void*)&foo69857; +__attribute__((used)) void* use69858 = (void*)&foo69858; +__attribute__((used)) void* use69859 = (void*)&foo69859; +__attribute__((used)) void* use69860 = (void*)&foo69860; +__attribute__((used)) void* use69861 = (void*)&foo69861; +__attribute__((used)) void* use69862 = (void*)&foo69862; +__attribute__((used)) void* use69863 = (void*)&foo69863; +__attribute__((used)) void* use69864 = (void*)&foo69864; +__attribute__((used)) void* use69865 = (void*)&foo69865; +__attribute__((used)) void* use69866 = (void*)&foo69866; +__attribute__((used)) void* use69867 = (void*)&foo69867; +__attribute__((used)) void* use69868 = (void*)&foo69868; +__attribute__((used)) void* use69869 = (void*)&foo69869; +__attribute__((used)) void* use69870 = (void*)&foo69870; +__attribute__((used)) void* use69871 = (void*)&foo69871; +__attribute__((used)) void* use69872 = (void*)&foo69872; +__attribute__((used)) void* use69873 = (void*)&foo69873; +__attribute__((used)) void* use69874 = (void*)&foo69874; +__attribute__((used)) void* use69875 = (void*)&foo69875; +__attribute__((used)) void* use69876 = (void*)&foo69876; +__attribute__((used)) void* use69877 = (void*)&foo69877; +__attribute__((used)) void* use69878 = (void*)&foo69878; +__attribute__((used)) void* use69879 = (void*)&foo69879; +__attribute__((used)) void* use69880 = (void*)&foo69880; +__attribute__((used)) void* use69881 = (void*)&foo69881; +__attribute__((used)) void* use69882 = (void*)&foo69882; +__attribute__((used)) void* use69883 = (void*)&foo69883; +__attribute__((used)) void* use69884 = (void*)&foo69884; +__attribute__((used)) void* use69885 = (void*)&foo69885; +__attribute__((used)) void* use69886 = (void*)&foo69886; +__attribute__((used)) void* use69887 = (void*)&foo69887; +__attribute__((used)) void* use69888 = (void*)&foo69888; +__attribute__((used)) void* use69889 = (void*)&foo69889; +__attribute__((used)) void* use69890 = (void*)&foo69890; +__attribute__((used)) void* use69891 = (void*)&foo69891; +__attribute__((used)) void* use69892 = (void*)&foo69892; +__attribute__((used)) void* use69893 = (void*)&foo69893; +__attribute__((used)) void* use69894 = (void*)&foo69894; +__attribute__((used)) void* use69895 = (void*)&foo69895; +__attribute__((used)) void* use69896 = (void*)&foo69896; +__attribute__((used)) void* use69897 = (void*)&foo69897; +__attribute__((used)) void* use69898 = (void*)&foo69898; +__attribute__((used)) void* use69899 = (void*)&foo69899; +__attribute__((used)) void* use69900 = (void*)&foo69900; +__attribute__((used)) void* use69901 = (void*)&foo69901; +__attribute__((used)) void* use69902 = (void*)&foo69902; +__attribute__((used)) void* use69903 = (void*)&foo69903; +__attribute__((used)) void* use69904 = (void*)&foo69904; +__attribute__((used)) void* use69905 = (void*)&foo69905; +__attribute__((used)) void* use69906 = (void*)&foo69906; +__attribute__((used)) void* use69907 = (void*)&foo69907; +__attribute__((used)) void* use69908 = (void*)&foo69908; +__attribute__((used)) void* use69909 = (void*)&foo69909; +__attribute__((used)) void* use69910 = (void*)&foo69910; +__attribute__((used)) void* use69911 = (void*)&foo69911; +__attribute__((used)) void* use69912 = (void*)&foo69912; +__attribute__((used)) void* use69913 = (void*)&foo69913; +__attribute__((used)) void* use69914 = (void*)&foo69914; +__attribute__((used)) void* use69915 = (void*)&foo69915; +__attribute__((used)) void* use69916 = (void*)&foo69916; +__attribute__((used)) void* use69917 = (void*)&foo69917; +__attribute__((used)) void* use69918 = (void*)&foo69918; +__attribute__((used)) void* use69919 = (void*)&foo69919; +__attribute__((used)) void* use69920 = (void*)&foo69920; +__attribute__((used)) void* use69921 = (void*)&foo69921; +__attribute__((used)) void* use69922 = (void*)&foo69922; +__attribute__((used)) void* use69923 = (void*)&foo69923; +__attribute__((used)) void* use69924 = (void*)&foo69924; +__attribute__((used)) void* use69925 = (void*)&foo69925; +__attribute__((used)) void* use69926 = (void*)&foo69926; +__attribute__((used)) void* use69927 = (void*)&foo69927; +__attribute__((used)) void* use69928 = (void*)&foo69928; +__attribute__((used)) void* use69929 = (void*)&foo69929; +__attribute__((used)) void* use69930 = (void*)&foo69930; +__attribute__((used)) void* use69931 = (void*)&foo69931; +__attribute__((used)) void* use69932 = (void*)&foo69932; +__attribute__((used)) void* use69933 = (void*)&foo69933; +__attribute__((used)) void* use69934 = (void*)&foo69934; +__attribute__((used)) void* use69935 = (void*)&foo69935; +__attribute__((used)) void* use69936 = (void*)&foo69936; +__attribute__((used)) void* use69937 = (void*)&foo69937; +__attribute__((used)) void* use69938 = (void*)&foo69938; +__attribute__((used)) void* use69939 = (void*)&foo69939; +__attribute__((used)) void* use69940 = (void*)&foo69940; +__attribute__((used)) void* use69941 = (void*)&foo69941; +__attribute__((used)) void* use69942 = (void*)&foo69942; +__attribute__((used)) void* use69943 = (void*)&foo69943; +__attribute__((used)) void* use69944 = (void*)&foo69944; +__attribute__((used)) void* use69945 = (void*)&foo69945; +__attribute__((used)) void* use69946 = (void*)&foo69946; +__attribute__((used)) void* use69947 = (void*)&foo69947; +__attribute__((used)) void* use69948 = (void*)&foo69948; +__attribute__((used)) void* use69949 = (void*)&foo69949; +__attribute__((used)) void* use69950 = (void*)&foo69950; +__attribute__((used)) void* use69951 = (void*)&foo69951; +__attribute__((used)) void* use69952 = (void*)&foo69952; +__attribute__((used)) void* use69953 = (void*)&foo69953; +__attribute__((used)) void* use69954 = (void*)&foo69954; +__attribute__((used)) void* use69955 = (void*)&foo69955; +__attribute__((used)) void* use69956 = (void*)&foo69956; +__attribute__((used)) void* use69957 = (void*)&foo69957; +__attribute__((used)) void* use69958 = (void*)&foo69958; +__attribute__((used)) void* use69959 = (void*)&foo69959; +__attribute__((used)) void* use69960 = (void*)&foo69960; +__attribute__((used)) void* use69961 = (void*)&foo69961; +__attribute__((used)) void* use69962 = (void*)&foo69962; +__attribute__((used)) void* use69963 = (void*)&foo69963; +__attribute__((used)) void* use69964 = (void*)&foo69964; +__attribute__((used)) void* use69965 = (void*)&foo69965; +__attribute__((used)) void* use69966 = (void*)&foo69966; +__attribute__((used)) void* use69967 = (void*)&foo69967; +__attribute__((used)) void* use69968 = (void*)&foo69968; +__attribute__((used)) void* use69969 = (void*)&foo69969; +__attribute__((used)) void* use69970 = (void*)&foo69970; +__attribute__((used)) void* use69971 = (void*)&foo69971; +__attribute__((used)) void* use69972 = (void*)&foo69972; +__attribute__((used)) void* use69973 = (void*)&foo69973; +__attribute__((used)) void* use69974 = (void*)&foo69974; +__attribute__((used)) void* use69975 = (void*)&foo69975; +__attribute__((used)) void* use69976 = (void*)&foo69976; +__attribute__((used)) void* use69977 = (void*)&foo69977; +__attribute__((used)) void* use69978 = (void*)&foo69978; +__attribute__((used)) void* use69979 = (void*)&foo69979; +__attribute__((used)) void* use69980 = (void*)&foo69980; +__attribute__((used)) void* use69981 = (void*)&foo69981; +__attribute__((used)) void* use69982 = (void*)&foo69982; +__attribute__((used)) void* use69983 = (void*)&foo69983; +__attribute__((used)) void* use69984 = (void*)&foo69984; +__attribute__((used)) void* use69985 = (void*)&foo69985; +__attribute__((used)) void* use69986 = (void*)&foo69986; +__attribute__((used)) void* use69987 = (void*)&foo69987; +__attribute__((used)) void* use69988 = (void*)&foo69988; +__attribute__((used)) void* use69989 = (void*)&foo69989; +__attribute__((used)) void* use69990 = (void*)&foo69990; +__attribute__((used)) void* use69991 = (void*)&foo69991; +__attribute__((used)) void* use69992 = (void*)&foo69992; +__attribute__((used)) void* use69993 = (void*)&foo69993; +__attribute__((used)) void* use69994 = (void*)&foo69994; +__attribute__((used)) void* use69995 = (void*)&foo69995; +__attribute__((used)) void* use69996 = (void*)&foo69996; +__attribute__((used)) void* use69997 = (void*)&foo69997; +__attribute__((used)) void* use69998 = (void*)&foo69998; +__attribute__((used)) void* use69999 = (void*)&foo69999; +__attribute__((used)) void* use70000 = (void*)&foo70000; diff --git a/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c b/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c new file mode 100644 index 0000000..efc78e1 --- /dev/null +++ b/testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c @@ -0,0 +1,87 @@ +// BUILD(macos|x86_64): $CC main.c -mmacosx-version-min=10.5 -o $BUILD_DIR/crt-vars10.5-libSystem.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./crt-vars10.5-libSystem.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" + +// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record +// the address of crt global variables. +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; + + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +static const struct ProgramVars* sVars; + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + +int main(int argc, const char* argv[]) +{ + if ( _NSGetArgv() != &NXArgv ) { + FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + } + + if ( _NSGetArgc() != &NXArgc ) { + FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + } + + if ( _NSGetEnviron() != &environ ) { + FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + } + + if ( _NSGetProgname() != &__progname ) { + FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + } + + if ( sVars->environPtr != &environ ) { + FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + } + + if ( sVars->__prognamePtr != &__progname ) { + FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + } + + if ( sVars->mh != &_mh_execute_header ) { + FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + } + + PASS("Success"); +} + diff --git a/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c b/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c new file mode 100644 index 0000000..3ed21f7 --- /dev/null +++ b/testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c @@ -0,0 +1,87 @@ +// BUILD(macos|x86_64): $CC main.c -mmacosx-version-min=10.6 -o $BUILD_DIR/crt-vars10.6-libSystem.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./crt-vars10.6-libSystem.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" + +// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record +// the address of crt global variables. +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; + + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +static const struct ProgramVars* sVars; + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + +int main(int argc, const char* argv[]) +{ + if ( _NSGetArgv() != &NXArgv ) { + FAIL("crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + } + + if ( _NSGetArgc() != &NXArgc ) { + FAIL("crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + } + + if ( _NSGetEnviron() != &environ ) { + FAIL("crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + } + + if ( _NSGetProgname() != &__progname ) { + FAIL("crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + FAIL("crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + FAIL("crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + FAIL("crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + } + + if ( sVars->environPtr != &environ ) { + FAIL("crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + } + + if ( sVars->__prognamePtr != &__progname ) { + FAIL("crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + } + + if ( sVars->mh != &_mh_execute_header ) { + FAIL("crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + } + + PASS("Success"); +} + diff --git a/testing/test-cases/crt-vars-libSystem.dtest/main.c b/testing/test-cases/crt-vars-libSystem.dtest/main.c index 6eb9381..9cf4775 100644 --- a/testing/test-cases/crt-vars-libSystem.dtest/main.c +++ b/testing/test-cases/crt-vars-libSystem.dtest/main.c @@ -9,6 +9,8 @@ #include #include +#include "test_support.h" + // This struct is passed as fifth parameter to libSystem.dylib's initializer so it record // the address of crt global variables. struct ProgramVars @@ -36,65 +38,46 @@ myInit(int argc, const char* argv[], const char* envp[], const char* apple[], co sVars = vars; } - -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] crt-vars-libSystem\n"); - bool success = true; - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( _NSGetArgv() != &NXArgv ) { - printf("[FAIL] crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); - success = false; + FAIL("_NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); } if ( _NSGetArgc() != &NXArgc ) { - printf("[FAIL] crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); - success = false; + FAIL("_NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); } if ( _NSGetEnviron() != &environ ) { - printf("[FAIL] crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); - success = false; + FAIL("_NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); } if ( _NSGetProgname() != &__progname ) { - printf("[FAIL] crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); - success = false; + FAIL("_NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); } if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { - printf("[FAIL] crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); - success = false; + FAIL("_NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); } if ( sVars->NXArgvPtr != &NXArgv ) { - printf("[FAIL] crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); - success = false; + FAIL("sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); } if ( sVars->NXArgcPtr != &NXArgc ) { - printf("[FAIL] crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); - success = false; + FAIL("sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); } if ( sVars->environPtr != &environ ) { - printf("[FAIL] crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); - success = false; + FAIL("sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); } if ( sVars->__prognamePtr != &__progname ) { - printf("[FAIL] crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); - success = false; + FAIL("sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); } if ( sVars->mh != &_mh_execute_header ) { - printf("[FAIL] crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); - success = false; + FAIL("sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); } - - if ( success ) - printf("[PASS] crt-vars-libSystem\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/cwd-relative-load.dtest/main.c b/testing/test-cases/cwd-relative-load.dtest/main.c index 8f2042f..b111390 100644 --- a/testing/test-cases/cwd-relative-load.dtest/main.c +++ b/testing/test-cases/cwd-relative-load.dtest/main.c @@ -11,18 +11,16 @@ #include +#include "test_support.h" + extern int foo; -int main() -{ - printf("[BEGIN] cwd-relative-load\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( foo == 42 ) - printf("[PASS] cwd-relative-load\n"); + PASS("cwd-relative-load"); else - printf("[FAIL] cwd-relative-load, wrong value\n"); - - return 0; + FAIL("cwd-relative-load, wrong value"); } diff --git a/testing/test-cases/dladdr-basic.dtest/main-no-syms.c b/testing/test-cases/dladdr-basic.dtest/main-no-syms.c index 672595d..baef3f6 100644 --- a/testing/test-cases/dladdr-basic.dtest/main-no-syms.c +++ b/testing/test-cases/dladdr-basic.dtest/main-no-syms.c @@ -1,6 +1,6 @@ // BUILD: $CC main-no-syms.c -o $BUILD_DIR/dladdr-stripped.exe -// BUILD: strip $BUILD_DIR/dladdr-stripped.exe +// BUILD: $STRIP $BUILD_DIR/dladdr-stripped.exe // RUN: ./dladdr-stripped.exe @@ -11,28 +11,22 @@ #include #include - +#include "test_support.h" /// /// verify dladdr() returns NULL for a symbol name in a fully stripped /// main executable (and not _mh_execute_header+nnn). /// -int main() -{ - printf("[BEGIN] dladdr-stripped\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { Dl_info info; if ( dladdr(&main, &info) == 0 ) { - printf("[FAIL] dladdr(&main, xx) failed\n"); - return 0; + FAIL("dladdr(&main, xx) failed"); } if ( info.dli_sname != NULL ){ - printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname); - return 0; + FAIL("%s\" instead of NULL", info.dli_sname); } - printf("[PASS] dladdr-stripped\n"); - return 0; + PASS("Succes"); } diff --git a/testing/test-cases/dladdr-basic.dtest/main.c b/testing/test-cases/dladdr-basic.dtest/main.c index 5f2e67c..4ee8ada 100644 --- a/testing/test-cases/dladdr-basic.dtest/main.c +++ b/testing/test-cases/dladdr-basic.dtest/main.c @@ -9,6 +9,8 @@ #include #include +#include "test_support.h" + extern char** environ; #if __has_feature(ptrauth_calls) @@ -45,20 +47,16 @@ static void verifybar() { Dl_info info; if ( dladdr(&bar, &info) == 0 ) { - printf("[FAIL] dladdr(&bar, xx) failed\n"); - exit(0); + FAIL("dladdr(&bar, xx) failed"); } if ( strcmp(info.dli_sname, "bar") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&bar) ) { - printf("[FAIL] dladdr()->dli_saddr is not &bar\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &bar"); } if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &bar"); } } @@ -67,20 +65,17 @@ static void verifyfoo() { Dl_info info; if ( dladdr(&foo, &info) == 0 ) { - printf("[FAIL] dladdr(&foo, xx) failed\n"); - exit(0); + FAIL("dladdr(&foo, xx) failed"); } if ( strcmp(info.dli_sname, "foo") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname); exit(0); } if ( info.dli_saddr != stripPointer(&foo) ) { - printf("[FAIL] dladdr()->dli_saddr is not &foo\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &foo"); } if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &foo"); } } @@ -89,20 +84,16 @@ static void verifyhide() { Dl_info info; if ( dladdr(&hide, &info) == 0 ) { - printf("[FAIL] dladdr(&hide, xx) failed\n"); - exit(0); + FAIL("dladdr(&hide, xx) failed"); } if ( strcmp(info.dli_sname, "hide") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&hide) ) { - printf("[FAIL] dladdr()->dli_saddr is not &hide\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &hide"); } if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &hide"); } } @@ -111,20 +102,17 @@ static void verifymalloc() { Dl_info info; if ( dladdr(&malloc, &info) == 0 ) { - printf("[FAIL] dladdr(&malloc, xx) failed\n"); - exit(0); + FAIL("dladdr(&malloc, xx) failed"); } if ( strcmp(info.dli_sname, "malloc") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname); exit(0); } if ( info.dli_saddr != stripPointer(&malloc) ) { - printf("[FAIL] dladdr()->dli_saddr is not &malloc\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &malloc"); } if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &malloc"); } } @@ -133,20 +121,16 @@ static void verifyenviron() { Dl_info info; if ( dladdr(&environ, &info) == 0 ) { - printf("[FAIL] dladdr(&environ, xx) failed\n"); - exit(0); + FAIL("dladdr(&environ, xx) failed"); } if ( strcmp(info.dli_sname, "environ") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"environ\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"environ\"", info.dli_sname); } if ( info.dli_saddr != &environ ) { - printf("[FAIL] dladdr()->dli_saddr is not &environ\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &environ"); } if ( info.dli_fbase != dyld_image_header_containing_address(&environ) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &environ\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &environ"); } } @@ -156,20 +140,16 @@ static void verifymydata() { Dl_info info; if ( dladdr(&mydata, &info) == 0 ) { - printf("[FAIL] dladdr(&mydata, xx) failed\n"); - exit(0); + FAIL("dladdr(&mydata, xx) failed"); } if ( strcmp(info.dli_sname, "mydata") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"mydata\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"mydata\"", info.dli_sname); } if ( info.dli_saddr != &mydata ) { - printf("[FAIL] dladdr()->dli_saddr is not &mydata\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &mydata"); } if ( info.dli_fbase != dyld_image_header_containing_address(&mydata) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &mydata\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &mydata"); } } @@ -179,18 +159,14 @@ static void verifyNULL() { Dl_info info; if ( dladdr(&malloc, NULL) != 0 ) { - printf("[FAIL] dladdr(&malloc, NULL) did not fail\n"); - exit(0); + FAIL("dladdr(&malloc, NULL) did not fail"); } if ( dladdr(NULL, NULL) != 0 ) { - printf("[FAIL] dladdr(NULL, NULL) did not fail\n"); - exit(0); + FAIL("dladdr(NULL, NULL) did not fail"); } } -int main() -{ - printf("[BEGIN] dladdr-basic\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { verifybar(); verifyhide(); verifyfoo(); @@ -199,7 +175,5 @@ int main() verifymydata(); verifyNULL(); - printf("[PASS] dladdr-basic\n"); - return 0; -} + PASS("Success");} diff --git a/testing/test-cases/dladdr-dylib.dtest/foo.c b/testing/test-cases/dladdr-dylib.dtest/foo.c index c62f8c6..1135218 100644 --- a/testing/test-cases/dladdr-dylib.dtest/foo.c +++ b/testing/test-cases/dladdr-dylib.dtest/foo.c @@ -9,6 +9,8 @@ #include #endif +#include "test_support.h" + extern void* __dso_handle; @@ -42,20 +44,16 @@ static void verifybar() { Dl_info info; if ( dladdr(&dylib_bar, &info) == 0 ) { - printf("[FAIL] dladdr(&dylib_bar, xx) failed\n"); - exit(0); + FAIL("dladdr(&dylib_bar, xx) failed"); } if ( strcmp(info.dli_sname, "dylib_bar") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&dylib_bar) ) { - printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &dylib_bar"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_bar\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &dylib_bar"); } } @@ -64,20 +62,16 @@ static void verifyfoo() { Dl_info info; if ( dladdr(&dylib_foo, &info) == 0 ) { - printf("[FAIL] dladdr(&dylib_foo, xx) failed\n"); - exit(0); + FAIL("dladdr(&dylib_foo, xx) failed"); } if ( strcmp(info.dli_sname, "dylib_foo") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&dylib_foo) ) { - printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &dylib_foo"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_foo\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &dylib_foo"); } } @@ -86,20 +80,16 @@ static void verifyhide() { Dl_info info; if ( dladdr(&dylib_hide, &info) == 0 ) { - printf("[FAIL] dladdr(&dylib_hide, xx) failed\n"); - exit(0); + FAIL("dladdr(&dylib_hide, xx) failed"); } if ( strcmp(info.dli_sname, "dylib_hide") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&dylib_hide) ) { - printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &dylib_hide"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_hide\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &dylib_hide"); } } @@ -108,20 +98,16 @@ static void verifyDSOHandle() { Dl_info info; if ( dladdr(&__dso_handle, &info) == 0 ) { - printf("[FAIL] dladdr(&__dso_handle, xx) failed\n"); - exit(0); + FAIL("dladdr(&__dso_handle, xx) failed"); } if ( strcmp(info.dli_sname, "__dso_handle") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"__dso_handle\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"__dso_handle\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&__dso_handle) ) { - printf("[FAIL] dladdr()->dli_saddr is not &__dso_handle\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &__dso_handle"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &__dso_handle\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &__dso_handle"); } } diff --git a/testing/test-cases/dladdr-dylib.dtest/main.c b/testing/test-cases/dladdr-dylib.dtest/main.c index a4401ab..6a56135 100644 --- a/testing/test-cases/dladdr-dylib.dtest/main.c +++ b/testing/test-cases/dladdr-dylib.dtest/main.c @@ -13,6 +13,8 @@ #include #endif +#include "test_support.h" + extern void* __dso_handle; extern void verifyDylib(); @@ -48,20 +50,16 @@ static void verifybar() { Dl_info info; if ( dladdr(&bar, &info) == 0 ) { - printf("[FAIL] dladdr(&bar, xx) failed\n"); - exit(0); + FAIL("dladdr(&bar, xx) failed"); } if ( strcmp(info.dli_sname, "bar") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&bar) ) { - printf("[FAIL] dladdr()->dli_saddr is not &bar\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &bar"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &bar"); } } @@ -70,20 +68,16 @@ static void verifyfoo() { Dl_info info; if ( dladdr(&foo, &info) == 0 ) { - printf("[FAIL] dladdr(&foo, xx) failed\n"); - exit(0); + FAIL("dladdr(&foo, xx) failed"); } if ( strcmp(info.dli_sname, "foo") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&foo) ) { - printf("[FAIL] dladdr()->dli_saddr is not &foo\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &foo"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &foo"); } } @@ -92,20 +86,16 @@ static void verifyhide() { Dl_info info; if ( dladdr(&hide, &info) == 0 ) { - printf("[FAIL] dladdr(&hide, xx) failed\n"); - exit(0); + FAIL("dladdr(&hide, xx) failed"); } if ( strcmp(info.dli_sname, "hide") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&hide) ) { - printf("[FAIL] dladdr()->dli_saddr is not &hide\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &hide"); } if ( info.dli_fbase != &__dso_handle ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &hide"); } } @@ -114,27 +104,21 @@ static void verifymalloc() { Dl_info info; if ( dladdr(&malloc, &info) == 0 ) { - printf("[FAIL] dladdr(&malloc, xx) failed\n"); - exit(0); + FAIL("dladdr(&malloc, xx) failed"); } if ( strcmp(info.dli_sname, "malloc") != 0 ) { - printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname); - exit(0); + FAIL("dladdr()->dli_sname is \"%s\" instead of \"malloc\"", info.dli_sname); } if ( info.dli_saddr != stripPointer(&malloc) ) { - printf("[FAIL] dladdr()->dli_saddr is not &malloc\n"); - exit(0); + FAIL("dladdr()->dli_saddr is not &malloc"); } if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) { - printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n"); - exit(0); + FAIL("dladdr()->dli_fbase is not image that contains &malloc"); } } -int main() -{ - printf("[BEGIN] dladdr-dylib\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { verifybar(); verifyhide(); verifyfoo(); @@ -142,7 +126,6 @@ int main() verifyDylib(); - printf("[PASS] dladdr-dylib\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlclose-static-terminator.dtest/main.c b/testing/test-cases/dlclose-static-terminator.dtest/main.c index dd19bbf..da4a01a 100644 --- a/testing/test-cases/dlclose-static-terminator.dtest/main.c +++ b/testing/test-cases/dlclose-static-terminator.dtest/main.c @@ -9,6 +9,7 @@ #include #include +#include "test_support.h" // verify dlclose() runs static terminator @@ -21,22 +22,17 @@ static void termNotifyFunc() termDidRun = true; } -int main() -{ - printf("[BEGIN] dlclose-static-terminator\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // load dylib void* handle = dlopen(RUN_DIR "/libterm.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlclose-static-terminator: libterm.dylib could not be loaded, %s\n", dlerror()); - return 0; + FAIL("libterm.dylib could not be loaded, %s", dlerror()); } // stuff pointer to my notifier NotifyProc* pointerAddress = (NotifyProc*)dlsym(handle, "gNotifer"); if ( pointerAddress == NULL ) { - printf("[FAIL] dlclose-static-terminator: gNotifer not found in libterm.dylib\n"); - return 0; + FAIL("gNotifer not found in libterm.dylib"); } *pointerAddress = &termNotifyFunc; @@ -44,10 +40,8 @@ int main() dlclose(handle); if ( termDidRun ) - printf("[PASS] dlclose-static-terminator\n"); + PASS("Success"); else - printf("[FAIL] dlclose-static-terminator: terminator not run\n"); - - return 0; + FAIL("terminator not run"); } diff --git a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c index 431c63c..53d222f 100644 --- a/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c @@ -1,5 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2 // BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3 // BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10 $BUILD_DIR/door1/libbar.dylib @@ -15,70 +14,52 @@ #include #include +#include "test_support.h" + // Program dlopen()s libfoo.dylib which was linked against libbar.dylib // Neither have valid paths and must be found via DYLD_LIBRARY_PATH // This test direct and indirect loading. -int main(int argc, const char* argv[]) -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { const char* env = getenv("DYLD_LIBRARY_PATH"); if ( env == NULL ) { - printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n"); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n"); - return 0; + FAIL("env not set"); } const char* valueStr = argv[1]; if ( valueStr == NULL ) { - printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n"); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n"); - return 0; + FAIL("arg1 value not set"); } char* end; long value = strtol(valueStr, &end, 0); - printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env); - void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); - return 0; - } - + void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(\"/bogus/libfoo.dylib\"): %s", dlerror()); + } + typedef int (*FooProc)(); - FooProc sym = (FooProc)dlsym(handle, "foo"); - if ( sym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); - return 0; - } + FooProc sym = (FooProc)dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } int result = (*sym)(); if ( result != value ) { - printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); - return 0; - } + FAIL("result=%d, expected %ld (str=%s)", result, value, valueStr); + } int r = dlclose(handle); if ( r != 0 ) { - printf("dlclose() returned %d\n", r); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); - return 0; - } + FAIL("dlclose() returned %d", r); + } - void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY); - if ( handle2 == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); - return 0; - } + void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlerror(\"/junk/libfoo.dylib\"): %s", dlerror()); + } - - - printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c index 8c91cb5..10f71a4 100644 --- a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c @@ -11,6 +11,7 @@ #include #include +#include "test_support.h" /// /// This tests the interaction of RTLD_LOCAL and weak-def coalescing. @@ -32,30 +33,23 @@ typedef int (*IntProc)(void); int __attribute__((weak)) coalA = 0; -int main() -{ - printf("[BEGIN] dlopen-RTLD_LOCAL-coalesce\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// 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; + FAIL("dlopen(\"libfoo1.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror()); } + void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_LOCAL); 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; + FAIL("dlopen(\"libfoo2.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror()); } + void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_GLOBAL); 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; + FAIL("dlopen(\"libfoo3.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror()); } - /// /// Get accessor functions /// @@ -70,8 +64,7 @@ int main() 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; + FAIL("dlsym() failed"); } /// @@ -85,38 +78,30 @@ int main() 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); + LOG("coalA in main: %d", coalA); + LOG("coalA in libfoo1: %d", foo1A); + LOG("coalA in libfoo2: %d", foo2A); + LOG("coalA in libfoo3: %d", foo3A); + LOG("coalB in libfoo1: %d", foo1B); + LOG("coalB in libfoo2: %d", foo2B); + LOG("coalB in libfoo3: %d", foo3B); + LOG("coalC in libfoo2: %d", foo2C); + LOG("coalC in libfoo3: %d", 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; + FAIL("coalA was not coalesced properly"); } if ( (foo1B != 1) || (foo2B != 1) || (foo3B != 1) ) { - printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalB was not coalesced properly\n"); - return 0; + FAIL("coalB was not coalesced properly"); } if ( (foo2C != 2) || (foo3C != 3) ) { - printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalC was not coalesced properly\n"); - return 0; + FAIL("coalC was not coalesced properly"); } - - - printf("[PASS] dlopen-RTLD_LOCAL-coalesce\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c index 79c113a..b358dc9 100644 --- a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c @@ -10,65 +10,52 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dlopen-RTLD_LOCAL-hides\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// 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; + FAIL("dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed"); } - /// /// 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; + FAIL("dlopen(\"libfoo.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL"); } - /// /// 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; + FAIL("dlopen(\"libbar.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlopen(\"libbar.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s", dlerror()); } - printf("[PASS] dlopen-RTLD_LOCAL-hides\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c index 3fff117..abb8a3a 100644 --- a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c @@ -10,35 +10,29 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dlopen-RTLD_NODELETE\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// 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; + FAIL("dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror()); } 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; + FAIL("dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded"); } // 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; + FAIL("value at fooSym changed"); } /// @@ -46,33 +40,26 @@ int main() /// 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; + FAIL("dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlsym(handle, \"bar\") failed but it should have worked: %s", dlerror()); } 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; + FAIL("dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s", dlerror()); } 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; + FAIL("dladdr(barSym, xx) succeeded as if libbar.dylib was not unloaded"); } // 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; + FAIL("value at barSym changed"); } - - printf("[PASS] dlopen-RTLD_NODELETE\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c index 81bc32e..c5752e5 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c @@ -4,19 +4,21 @@ #include #include +#include "test_support.h" + bool doneInitB = false; bool inInitB = false; __attribute__((constructor)) -void initB() +void initB(int argc, const char* argv[], const char* envp[], const char* apple[]) { inInitB = true; // "upward" link to libInitA.dylib void* handle = dlopen(RUN_DIR "/libInitA.dylib", RTLD_NOLOAD); if ( handle == NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: dlopen(libInitA.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); + FAIL("dlopen(\"libInitA.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror()); return; } inInitB = false; diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c index 41201ea..036d102 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c @@ -11,23 +11,19 @@ #include #include +#include "test_support.h" extern bool initsInWrongOrder; extern bool allInitsDone(); -int main() -{ - printf("[BEGIN] dlopen-RTLD_NOLOAD-in-initializer\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// This tests that using RTLD_NOLOAD in an initializer does not trigger out of order initializers /// if ( initsInWrongOrder ) - printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: wrong init order\n"); + FAIL("wrong init order"); else if ( !allInitsDone() ) - printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: all initializers not run\n"); + FAIL("all initializers not run"); else - printf("[PASS] dlopen-RTLD_NOLOAD-in-initializer\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c index 8fda486..7eef719 100644 --- a/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c @@ -1,7 +1,7 @@ // BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib // BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dlopen-RTLD_NOLOAD-basic.exe -// BUILD: cd $BUILD_DIR && ln -s libfoo.dylib libfoo-sym.dylib +// BUILD: $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-sym.dylib // RUN: ./dlopen-RTLD_NOLOAD-basic.exe @@ -10,23 +10,19 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dlopen-RTLD_NOLOAD-basic\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// This tests that RTLD_NOLOAD finds existing dylib statically linked /// void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOLOAD); if ( handle == NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); - return 0; + FAIL("dlopen(\"libfoo.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror()); } void* sym = dlsym(handle, "foo"); if ( sym == NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror()); - return 0; + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", dlerror()); } /// @@ -34,8 +30,7 @@ int main() /// void* handle2 = dlopen(RUN_DIR "/libfobbulate.dylib", RTLD_NOLOAD); if ( handle2 != NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfobbulate.dylib, RTLD_NOLOAD) succeeded but it should have failed\n"); - return 0; + FAIL("dlopen(\"libfobbulate.dylib\", RTLD_NOLOAD) succeeded but it should have failed"); } @@ -44,8 +39,7 @@ int main() /// void* handle3 = dlopen(RUN_DIR "/libfoo-sym.dylib", RTLD_NOLOAD); if ( handle3 == NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo-sym.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); - return 0; + FAIL("dlopen(\"libfoo-sym.dylib\", RTLD_NOLOAD) failed but it should have worked: %s", dlerror()); } @@ -54,11 +48,8 @@ int main() /// void* handle4 = dlopen("/usr/lib/libz.1.dylib", RTLD_NOLOAD); if ( handle4 != NULL ) { - printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libz.dylib, RTLD_NOLOAD) worked but it should have failed\n"); - return 0; + FAIL("dlopen(\"libz.dylib\", RTLD_NOLOAD) worked but it should have failed"); } - - printf("[PASS] dlopen-RTLD_NOLOAD-basic\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c b/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c index 29f9b31..385ea03 100644 --- a/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c +++ b/testing/test-cases/dlopen-RTLD_NOW.dtest/main.c @@ -1,9 +1,11 @@ // BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib -// BUILD: $CC bar.c -dynamiclib -o $TEMP_DIR/libbar-present.dylib -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1 -// BUILD: $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $TEMP_DIR/libbar-present.dylib +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar-present.dylib -install_name $RUN_DIR/libbar.dylib -DHAS_SYMBOL=1 +// BUILD: $CC foo.c -dynamiclib -Os -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar-present.dylib // BUILD: $CC main.c -o $BUILD_DIR/dlopen-RTLD_NOW.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar-present.dylib + // RUN: ./dlopen-RTLD_NOW.exe #include @@ -13,23 +15,21 @@ #include #include +#include "test_support.h" + #if __LP64__ extern struct mach_header_64 __dso_handle; #else extern struct mach_header __dso_handle; #endif -int main() -{ - printf("[BEGIN] dlopen-RTLD_NOW\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { /// /// This tests that RTLD_NOW on dlopen() will return NULL because call from libfoo to libbar could not be bound /// void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW); if ( handle != NULL ) { - printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_NOW) should have failed\n"); - return 0; + FAIL("dlopen(\"libfoo.dylib\", RTLD_NOW) should have failed"); } @@ -53,18 +53,14 @@ int main() handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); if ( supportsLazyBinding ) { if ( handle == NULL ) { - printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_LAZY) should have succeeded: %s\n", dlerror()); - return 0; + FAIL("dlopen(\"libfoo.dylib\", RTLD_LAZY) should have succeeded: %s", dlerror()); } } else { if ( handle != NULL ) { - printf("[FAIL] dlopen-RTLD_NOW: dlopen(libfoo.dylib, RTLD_LAZY) should have failed becuase a symbol was missing\n"); - return 0; + FAIL("dlopen(\"libfoo.dylib\", RTLD_LAZY) should have failed becuase a symbol was missing"); } } - - printf("[PASS] dlopen-RTLD_NOW\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-atpath-restricted.dtest/main.c b/testing/test-cases/dlopen-atpath-restricted.dtest/main.c index bd55fb4..cbd93fc 100644 --- a/testing/test-cases/dlopen-atpath-restricted.dtest/main.c +++ b/testing/test-cases/dlopen-atpath-restricted.dtest/main.c @@ -1,80 +1,61 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib +// BUILD(macos): $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib -// 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(macos): $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib +// BUILD(macos): $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.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(macos): $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib +// BUILD(macos): $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3 $BUILD_DIR/test3/libtest3.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(macos): $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib +// BUILD(macos): $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.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(macos): $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib +// BUILD(macos): $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.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(macos): $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null - - -// BUILD: $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./dlopen-restricted.exe #include #include +#include "test_support.h" -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dlopen-restricted\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // 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; + FAIL("test1.bundle dlerror(): %s", dlerror()); } // 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; + FAIL("test2.bundle\n dlerror(): %s", dlerror()); } // 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; + FAIL("test3.bundle dlerror(): %s", dlerror()); } // 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; + FAIL("test4.bundle dlopen() should not work"); } // 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; + FAIL("test5.bundle dlopen() should not work"); } - - - printf("[PASS] dlopen-restricted\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-bad-file.dtest/main.c b/testing/test-cases/dlopen-bad-file.dtest/main.c index 63108fd..2a11c5f 100644 --- a/testing/test-cases/dlopen-bad-file.dtest/main.c +++ b/testing/test-cases/dlopen-bad-file.dtest/main.c @@ -1,5 +1,5 @@ -// BUILD: cp bad.txt $BUILD_DIR/libnota.dylib +// BUILD: $CP bad.txt $BUILD_DIR/libnota.dylib // BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR" // RUN: ./dlopen-bad-file.exe @@ -8,40 +8,29 @@ #include #include +#include "test_support.h" - -int main() -{ - printf("[BEGIN] dlopen-bad-file\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // try to dlopen() a text file - void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST); - if ( handle != NULL ) { - printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib"); - return 0; - } + void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST); + if ( handle != NULL ) { + FAIL("Should have failed on non-mach-o file %s", RUN_DIR "/libnota.dylib"); + } const char* message = dlerror(); if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) { - printf("dlerror: %s\n", message); - printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n"); - return 0; - } + FAIL("dlerror() message '%s' did not contain 'mach-o'", message); + } // try to dlopen() a directory - handle = dlopen(RUN_DIR, RTLD_FIRST); - if ( handle != NULL ) { - printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR); - return 0; - } + handle = dlopen(RUN_DIR, RTLD_FIRST); + if ( handle != NULL ) { + FAIL("Should have failed on dir %s", RUN_DIR); + } message = dlerror(); if ( strstr(message, "not a file") == NULL ) { - printf("dlerror: %s\n", message); - printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n"); - return 0; - } + FAIL("dlerror() message '%s' did not contain 'not a file", message); + } - printf("[PASS] dlopen-bad-file\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-basic.dtest/main.c b/testing/test-cases/dlopen-basic.dtest/main.c index 26e3c5d..60c6711 100644 --- a/testing/test-cases/dlopen-basic.dtest/main.c +++ b/testing/test-cases/dlopen-basic.dtest/main.c @@ -8,41 +8,31 @@ #include #include +#include "test_support.h" static void tryImage(const char* path) { - printf("[BEGIN] dlopen-basic %s\n", path); - void* handle = dlopen(path, RTLD_LAZY); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-basic %s\n", path); - return; - } - - void* sym = dlsym(handle, "foo"); - if ( sym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-basic %s\n", path); - return; - } - - int result = dlclose(handle); - if ( result != 0 ) { - printf("dlclose() returned %d, dlerror()=%s\n", result, dlerror()); - printf("[FAIL] dlopen-basic %s\n", path); - return; - } + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\"), dlerror()=%s", path, dlerror()); + } - printf("[PASS] dlopen-basic %s\n", path); + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(\"foo\") for \"%s\" returned NULL, dlerror()=%s", path, dlerror()); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(\"%s\") returned %d, dlerror()=%s", path, result, dlerror()); + } } -int main() -{ - tryImage(RUN_DIR "/test.bundle"); - tryImage(RUN_DIR "/test.dylib"); - - return 0; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + tryImage(RUN_DIR "/test.bundle"); + tryImage(RUN_DIR "/test.dylib"); + PASS("Success"); } diff --git a/testing/test-cases/dlopen-dyld.dtest/main.c b/testing/test-cases/dlopen-dyld.dtest/main.c new file mode 100644 index 0000000..2d011d8 --- /dev/null +++ b/testing/test-cases/dlopen-dyld.dtest/main.c @@ -0,0 +1,21 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-dyld.exe + +// RUN: ./dlopen-dyld.exe + + +#include +#include +#include + +#include "test_support.h" + + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + void* handle = dlopen("/usr/lib/dyld", RTLD_LAZY); + if ( handle == NULL ) + PASS("Success"); + else + FAIL("dlopen-dyld: dlopen() should have failed"); +} diff --git a/testing/test-cases/dlopen-empty-data.dtest/main.c b/testing/test-cases/dlopen-empty-data.dtest/main.c index 9bf8051..430a2f7 100644 --- a/testing/test-cases/dlopen-empty-data.dtest/main.c +++ b/testing/test-cases/dlopen-empty-data.dtest/main.c @@ -9,19 +9,16 @@ #include #include +#include "test_support.h" + // libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment -int main() -{ - printf("[BEGIN] dlopen-empty-data\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror()); - return 0; + FAIL("libfoo-dynamic.dylib could not be loaded: %s", dlerror()); } - printf("[PASS] dlopen-empty-data\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-fail-cleanly.dtest/main.c b/testing/test-cases/dlopen-fail-cleanly.dtest/main.c index ceee099..61eb1a3 100644 --- a/testing/test-cases/dlopen-fail-cleanly.dtest/main.c +++ b/testing/test-cases/dlopen-fail-cleanly.dtest/main.c @@ -1,10 +1,13 @@ -// BUILD: $CC c.c -dynamiclib -o $TEMP_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1 +// BUILD: $CC c.c -dynamiclib -o $BUILD_DIR/libcextra.dylib -install_name $RUN_DIR/libc.dylib -DEXTRA_SYMBOL=1 // BUILD: $CC c.c -dynamiclib -o $BUILD_DIR/libc.dylib -install_name $RUN_DIR/libc.dylib -// BUILD: $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $TEMP_DIR/libcextra.dylib -framework Foundation +// BUILD: $CC b.m -dynamiclib -o $BUILD_DIR/libb.dylib -install_name $RUN_DIR/libb.dylib $BUILD_DIR/libcextra.dylib -framework Foundation // BUILD: $CC a.c -dynamiclib -o $BUILD_DIR/liba.dylib -install_name $RUN_DIR/liba.dylib $BUILD_DIR/libb.dylib // BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-fail-cleanly.exe +// BUILD: $SKIP_INSTALL $BUILD_DIR/libcextra.dylib + + // RUN: ./dlopen-fail-cleanly.exe #include @@ -12,16 +15,13 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dlopen-fail-cleanly\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // dlopen dylib chain that should fail void* handle = dlopen(RUN_DIR "/liba.dylib", RTLD_NOW); if ( handle != NULL ) { - printf("[FAIL] dlopen-fail-cleanly dlopen(liba.dylib) expected to fail but did not\n"); - return 0; + FAIL("dlopen(liba.dylib) expected to fail but did not"); } // iterate loaded images and make sure no residue from failed dlopen @@ -29,14 +29,12 @@ int main() int count = _dyld_image_count(); for (int i=0; i < count; ++i) { const char* path = _dyld_get_image_name(i); - //printf("path[%2d]=%s\n", i, path); + LOG("path[%2d]=%s", i, path); if ( strstr(path, RUN_DIR "/lib") != NULL ) { - printf("[FAIL] dlopen-fail-cleanly: found unexpected loaded image: %s\n", path); - return 0; + FAIL("Found unexpected loaded image: %s", path); } } - printf("[PASS] dlopen-fail-cleanly\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-flat.dtest/main.c b/testing/test-cases/dlopen-flat.dtest/main.c index 84f5835..67a00f5 100644 --- a/testing/test-cases/dlopen-flat.dtest/main.c +++ b/testing/test-cases/dlopen-flat.dtest/main.c @@ -8,85 +8,69 @@ #include #include +#include "test_support.h" + int gInitialisersCalled = 0; -int main() { - printf("[BEGIN] dlopen-flat\n"); - int result; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + int result; - // Foo exports foo() - void* fooHandle = 0; - { - fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); - if (!fooHandle) { - printf("dlopen failed with error: %s\n", dlerror()); - return 1; - } - if (gInitialisersCalled != 1) { - printf("gInitialisersCalled != 1\n"); - printf("[FAIL] dlopen-flat\n"); - return 1; - } - } - // Now unload foo which should do something. - result = dlclose(fooHandle); - if (result != 0) { - printf("dlclose() returned %c\n", result); - printf("[FAIL] dlopen-flat\n"); - return 1; - } + // Foo exports foo() + void* fooHandle = 0; + { + fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + if (!fooHandle) { + FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\") failed with error: %s", dlerror()); + } + if (gInitialisersCalled != 1) { + FAIL("gInitialisersCalled != 1"); + } + } + // Now unload foo which should do something. + result = dlclose(fooHandle); + if (result != 0) { + FAIL("dlclose() returned %c", result); + } - // Open foo again which should do something. - { - fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); - if (!fooHandle) { - printf("dlopen failed with error: %s\n", dlerror()); - return 1; - } - if (gInitialisersCalled != 2) { - printf("gInitialisersCalled != 2\n"); - printf("[FAIL] dlopen-flat\n"); - return 1; - } - } + // Open foo again which should do something. + { + fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + if (!fooHandle) { + FAIL("dlopen failed with error: %s", dlerror()); + } + if (gInitialisersCalled != 2) { + FAIL("gInitialisersCalled != 2"); + } + } - // Bar is going to resolve foo() - void* barHandle = 0; - { - barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY); - if (!barHandle) { - printf("dlopen failed with error: %s\n", dlerror()); - return 1; - } - if (gInitialisersCalled != 3) { - printf("gInitialisersCalled != 3\n"); - printf("[FAIL] dlopen-flat\n"); - return 1; - } - } - // Now unload foo which shouldn't do anything. - result = dlclose(fooHandle); - if (result != 0) { - printf("dlclose() returned %c\n", result); - printf("[FAIL] dlopen-flat\n"); - return 1; - } + // Bar is going to resolve foo() + void* barHandle = 0; + { + barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY); + if (!barHandle) { + FAIL("dlopen(\"" RUN_DIR "/libbar.dylib\" failed with error: %s", dlerror()); + } + if (gInitialisersCalled != 3) { + FAIL("gInitialisersCalled != 3"); + } + } + // Now unload foo which shouldn't do anything. + result = dlclose(fooHandle); + if (result != 0) { + FAIL("dlclose(\"" RUN_DIR "/libfoo.dylib\") returned %c", result); + } - // Open foo again which shouldn't do anything. - { - fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); - if (!fooHandle) { - printf("dlopen failed with error: %s\n", dlerror()); - return 1; - } - if (gInitialisersCalled != 3) { - printf("gInitialisersCalled != 3\n"); - printf("[FAIL] dlopen-flat\n"); - return 1; - } - } + // Open foo again which shouldn't do anything. + { + fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + if (!fooHandle) { + FAIL("dlopen(\"" RUN_DIR "/libfoo.dylib\" failed with error: %s", dlerror()); + } + if (gInitialisersCalled != 3) { + FAIL("gInitialisersCalled != 3"); + } + } - printf("[PASS] dlopen-flat\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-framework-fallback.dtest/main.c b/testing/test-cases/dlopen-framework-fallback.dtest/main.c index 02c467e..0f47a99 100644 --- a/testing/test-cases/dlopen-framework-fallback.dtest/main.c +++ b/testing/test-cases/dlopen-framework-fallback.dtest/main.c @@ -6,30 +6,23 @@ #include #include +#include "test_support.h" - -int main() -{ - printf("[BEGIN] dlopen-framework-fallback\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // Verify dyld will fallback and look for framework in /System/Library/Frameworks/ - void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-framework-fallback\n"); - return 0; - } + void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - // validate handle works to find symbols - void* sym = dlsym(handle, "CFRetain"); - if ( sym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-framework-fallback\n"); - return 0; - } + // validate handle works to find symbols + void* sym = dlsym(handle, "CFRetain"); + if ( sym == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - printf("[PASS] dlopen-framework-fallback\n"); + PASS("Success"); - return 0; + return 0; } diff --git a/testing/test-cases/dlopen-haswell.dtest/main.c b/testing/test-cases/dlopen-haswell.dtest/main.c index a76b9c5..6954db0 100644 --- a/testing/test-cases/dlopen-haswell.dtest/main.c +++ b/testing/test-cases/dlopen-haswell.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC a.c -dynamiclib -arch x86_64h -o $BUILD_DIR/libHaswellCheck.dylib -install_name $RUN_DIR/libHaswellCheck.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/dlopen-haswell.exe -DRUN_DIR="$RUN_DIR" -// 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" +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./dlopen-haswell.exe @@ -13,6 +13,8 @@ #include #include +#include "test_support.h" + typedef bool (*BoolFunc)(void); @@ -30,22 +32,15 @@ bool isHaswell_dynamic() } -int main(int arg, const char* argv[]) -{ - printf("[BEGIN] dlopen-haswell\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { 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; + FAIL("dlopen(\"" RUN_DIR "/libHaswellCheck.dylib\") error: %s", dlerror()); } BoolFunc libFunc = (BoolFunc)dlsym(handle, "isHaswell"); if ( libFunc == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-haswell dlsym\n"); - return 0; + FAIL("dlsym(\"isHaswell\") error: %s", dlerror()); } // check if haswell slice of libHaswellCheck.dylib was loaded on haswell machines @@ -53,11 +48,9 @@ int main(int arg, const char* argv[]) bool runtimeIsHaswell = isHaswell_dynamic(); if ( dylibIsHaswellSlice != runtimeIsHaswell ) - printf("[FAIL] dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d\n", dylibIsHaswellSlice, runtimeIsHaswell); + FAIL("dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d", dylibIsHaswellSlice, runtimeIsHaswell); else - printf("[PASS] dlopen-haswell\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-iOSMac.dtest/cat.c b/testing/test-cases/dlopen-iOSMac.dtest/cat.c new file mode 100644 index 0000000..8b61ebc --- /dev/null +++ b/testing/test-cases/dlopen-iOSMac.dtest/cat.c @@ -0,0 +1 @@ +void func() {} diff --git a/testing/test-cases/dlopen-iOSMac.dtest/foo.c b/testing/test-cases/dlopen-iOSMac.dtest/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/testing/test-cases/dlopen-iOSMac.dtest/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/testing/test-cases/dlopen-iOSMac.dtest/main.c b/testing/test-cases/dlopen-iOSMac.dtest/main.c new file mode 100644 index 0000000..3ee86c2 --- /dev/null +++ b/testing/test-cases/dlopen-iOSMac.dtest/main.c @@ -0,0 +1,56 @@ +// BUILD(macos): $CC main.c -o $BUILD_DIR/dlopen-iOSMac.exe -DFOR_IOSMAC=1 -target x86_64-darwin-ios13.0-macabi +// BUILD(macos): $CC main.c -o $BUILD_DIR/dlopen-macOS.exe -DRUN_DIR="$RUN_DIR" +// BUILD(macos): $CC cat.c -dynamiclib -install_name $RUN_DIR/libcat.dylib -o $BUILD_DIR/libcat.dylib -target x86_64-darwin-ios13.0-macabi +// BUILD(macos): $CC foo.c -dynamiclib -install_name $RUN_DIR/libtestnotincache.dylib -o $BUILD_DIR/libtestnotincache.dylib $BUILD_DIR/libcat.dylib +// BUILD(macos): $CC foo.c -dynamiclib -install_name $RUN_DIR/libtestincache.dylib -o $BUILD_DIR/libtestincache.dylib -framework UIKit -F/System/iOSSupport/System/Library/Frameworks + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./dlopen-iOSMac.exe +// RUN: ./dlopen-macOS.exe + + +#include +#include +#include + +#include "test_support.h" + +int main(int arg, const char* argv[]) +{ + +#if FOR_IOSMAC + void* handle = dlopen("/System/Library/Frameworks/UIKit.framework/UIKit", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-iOS-on-Mac, UIKit failed to dlopen(): %s", dlerror()); + return 0; + } +#else + void* handle = dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-macOS, AppKit failed to dlopen(): %s", dlerror()); + return 0; + } + // verify that can't load a macOS dylib that links with a catalyst dylib + handle = dlopen(RUN_DIR "/libtestnotincache.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-macOS, libtestnotincache.dylib should not be loaded"); + return 0; + } + // verify that can't load a macOS dylib that links with a catalyst dylib which is in the dyld cache + handle = dlopen(RUN_DIR "/libtestincache.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-macOS, libtestincache.dylib should not be loaded"); + return 0; + } +#endif + +#if FOR_IOSMAC + PASS("dlopen-iOS-on-Mac"); +#else + PASS("dlopen-macOS"); +#endif + + return 0; +} + diff --git a/testing/test-cases/dlopen-in-init.dtest/foo.c b/testing/test-cases/dlopen-in-init.dtest/foo.c index 5f91679..6b89368 100644 --- a/testing/test-cases/dlopen-in-init.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init.dtest/foo.c @@ -1,16 +1,7 @@ - - -#include #include -#include -#include -#include -#include -#include #include -#include -#include +#include "test_support.h" static void* work1(void* arg) { @@ -21,13 +12,11 @@ static void* work1(void* arg) __attribute__((constructor)) -void myinit() -{ +void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { pthread_t workerThread; if ( pthread_create(&workerThread, NULL, work1, NULL) != 0 ) { - printf("[FAIL] dlopen-in-init, pthread_create\n"); - return; + FAIL("pthread_create"); } void* dummy; diff --git a/testing/test-cases/dlopen-in-init.dtest/main.c b/testing/test-cases/dlopen-in-init.dtest/main.c index d96489e..987925f 100644 --- a/testing/test-cases/dlopen-in-init.dtest/main.c +++ b/testing/test-cases/dlopen-in-init.dtest/main.c @@ -9,6 +9,7 @@ #include #include +#include "test_support.h" __attribute__((constructor)) void myinit() @@ -17,13 +18,8 @@ void myinit() } -int main() -{ - printf("[BEGIN] dlopen-in-init\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // 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; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-in-init2.dtest/bar.c b/testing/test-cases/dlopen-in-init2.dtest/bar.c index 336e471..6f930dc 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/bar.c +++ b/testing/test-cases/dlopen-in-init2.dtest/bar.c @@ -1,3 +1,4 @@ +#include "test_support.h" static int inited = 0; @@ -11,6 +12,8 @@ int barIsInited() { return inited; } -int bar() { - return inited ? 0 : 1; -} \ No newline at end of file +void bar() { + if (inited == 0) { + FAIL("libbar.dylib not initialized"); + } +} diff --git a/testing/test-cases/dlopen-in-init2.dtest/foo.c b/testing/test-cases/dlopen-in-init2.dtest/foo.c index bdd1e73..199fb4f 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init2.dtest/foo.c @@ -3,7 +3,8 @@ #include #include -extern int bar(); +#include "test_support.h" + extern int bazInited(); static void* barHandle = NULL; @@ -12,40 +13,33 @@ static int fooInited = 0; static int barInited = 0; __attribute__((constructor)) -static void myinit() -{ +static void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { fooInited = 1; barHandle = dlopen(RUN_DIR "/libbar.dylib", 0); if ( barHandle == NULL ) { - printf("[FAIL] dlopen-in-init2, dlopen libbar.dylib: %s\n", dlerror()); - return; + FAIL("dlopen libbar.dylib: %s", dlerror()); } barSymbol = dlsym(RTLD_DEFAULT, "barIsInited"); if ( barSymbol == NULL ) { - printf("[FAIL] dlopen-in-init2, dlsym libbar.dylib\n"); - return; + FAIL("dlsym libbar.dylib"); } barInited = ((int(*)())barSymbol)(); } -int foo() { +void foo() { if ( fooInited == 0 ) { - printf("[FAIL] dlopen-in-init2, didn't init foo\n"); - return 1; + FAIL("Didn't init foo"); } if ( barHandle == NULL ) { - return 1; + FAIL("barHandle not inited"); } if ( barSymbol == NULL ) { - return 1; + FAIL("barSymbol not inited"); } if ( barInited == 0 ) { - printf("[FAIL] dlopen-in-init2, didn't init bar\n"); - return 1; + FAIL("Didn't init bar"); } if ( bazInited() == 0 ) { - printf("[FAIL] dlopen-in-init2, didn't init baz\n"); - return 1; + FAIL("Didn't init baz"); } - return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/dlopen-in-init2.dtest/main.c b/testing/test-cases/dlopen-in-init2.dtest/main.c index 454802d..a9d1231 100644 --- a/testing/test-cases/dlopen-in-init2.dtest/main.c +++ b/testing/test-cases/dlopen-in-init2.dtest/main.c @@ -17,17 +17,14 @@ #include #include +#include "test_support.h" -extern int foo(); -extern int bar(); +extern void foo(); +extern void bar(); -int main() { - printf("[BEGIN] dlopen-in-init2\n"); - if ( foo() != 0 ) - return 0; - if ( bar() != 0 ) - return 0; - printf("[PASS] dlopen-in-init2\n"); - return 0; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + foo(); + bar(); + PASS("Success"); } diff --git a/testing/test-cases/dlopen-in-init3.dtest/bar.c b/testing/test-cases/dlopen-in-init3.dtest/bar.c index 8629f2c..4f15068 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/bar.c +++ b/testing/test-cases/dlopen-in-init3.dtest/bar.c @@ -3,42 +3,39 @@ #include #include +#include "test_support.h" + static void* bazHandle = NULL; static void* bazSymbol = NULL; static int barInited = 0; static int bazInited = 0; __attribute__((constructor)) -static void myinit() -{ - barInited = 1; - bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0); - if ( bazHandle == NULL ) { - printf("[FAIL] dlopen-in-init3, dlopen libbaz.dylib: %s\n", dlerror()); - return; - } - bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited"); +static void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { + barInited = 1; + bazHandle = dlopen(RUN_DIR "/libbaz.dylib", 0); + if ( bazHandle == NULL ) { + FAIL("dlopen libbaz.dylib: %s", dlerror()); + } + bazSymbol = dlsym(RTLD_DEFAULT, "bazIsInited"); if ( bazSymbol == NULL ) { - printf("[FAIL] dlopen-in-init3, dlsym libbaz.dylib\n"); - return; + FAIL("dlsym libbaz.dylib"); } bazInited = ((int(*)())bazSymbol)(); } int bar() { if ( barInited == 0 ) { - printf("[FAIL] dlopen-in-init3, didn't init bar\n"); - return 1; + FAIL("Didn't init bar"); } if ( bazHandle == NULL ) { - return 1; + FAIL("bazHandle not inited"); } if ( bazSymbol == NULL ) { - return 1; + FAIL("bazSymbol not inited"); } if ( bazInited == 0 ) { - printf("[FAIL] dlopen-in-init3, didn't init bar\n"); - return 1; + FAIL("didn't init bar"); } return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/dlopen-in-init3.dtest/foo.c b/testing/test-cases/dlopen-in-init3.dtest/foo.c index 86850a8..3fc5a5a 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/foo.c +++ b/testing/test-cases/dlopen-in-init3.dtest/foo.c @@ -1,5 +1,4 @@ - -#include +#include "test_support.h" extern int bar(); extern int bazIsInited(); @@ -9,8 +8,7 @@ int foo() { return 1; } if ( bazIsInited() == 0 ) { - printf("[FAIL] dlopen-in-init3, didn't init baz\n"); - return 1; + FAIL("didn't init baz"); } return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/dlopen-in-init3.dtest/main.c b/testing/test-cases/dlopen-in-init3.dtest/main.c index 4f6b362..17ac740 100644 --- a/testing/test-cases/dlopen-in-init3.dtest/main.c +++ b/testing/test-cases/dlopen-in-init3.dtest/main.c @@ -8,7 +8,7 @@ // RUN: ./dlopen-in-init3.exe // This test uses dlopen to jump ahead in the initializer graph -// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbar. +// main doesn't directly link any of the libraries here, but dlopen's libfoo which links libbar and libbaz. // We should run initializers in the order libbar, libbaz, libfoo. // However, libbar has a static init with a dlopen of libbaz and so libbaz needs to be initialized by libbar instead of by libfoo @@ -16,21 +16,20 @@ #include #include -int main() { - printf("[BEGIN] dlopen-in-init3\n"); - void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0); - if ( fooHandle == NULL ) { - printf("[FAIL] dlopen-in-init3, dlopen libfoo.dylib: %s\n", dlerror()); - return 0; - } - void* fooSymbol = dlsym(RTLD_DEFAULT, "foo"); - if ( fooSymbol == NULL ) { - printf("[FAIL] dlopen-in-init3, dlsym libfoo.dylib\n"); - return 0; +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", 0); + if ( fooHandle == NULL ) { + FAIL("dlopen-in-init3, dlopen libfoo.dylib: %s", dlerror()); } - if ( ((int(*)())fooSymbol)() != 0 ) - return 0; - printf("[PASS] dlopen-in-init3\n"); - return 0; + void* fooSymbol = dlsym(RTLD_DEFAULT, "foo"); + if ( fooSymbol == NULL ) { + FAIL("dlsym libfoo.dylib"); + } + if ( ((int(*)())fooSymbol)() != 0 ) { + FAIL("fooSymbol() should return 0"); + } + PASS("Success"); } diff --git a/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c b/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c index 4547e90..7c1e9da 100644 --- a/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c +++ b/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c @@ -1,9 +1,9 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations +// BUILD(macos): $CC foo.c -o $BUILD_DIR/foo.bundle -bundle +// BUILD(macos): $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD(macos): $CC baz.c -dynamiclib -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib -// BUILD: $CC main.c -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations -// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle -// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -// BUILD: $CC baz.c -dynamiclib -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./dlopen-indirect-groupNum.exe $RUN_DIR/foo.bundle $RUN_DIR/libbar.dylib $RUN_DIR/libbaz.dylib @@ -18,82 +18,71 @@ #include #include +#include "test_support.h" static void checkBundle(const char* path, bool unlinkBeforeDestroy) { int fd = open(path, O_RDONLY, 0); if ( fd == -1 ) { - printf("[FAIL] open(%s) failed", path); - exit(0); + FAIL("open(%s) failed", path); } struct stat stat_buf; if ( fstat(fd, &stat_buf) == -1) { - printf("[FAIL] fstat() failed\n"); - exit(0); + FAIL("fstat() failed"); } void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( loadAddress == ((void*)(-1)) ) { - printf("[FAIL] mmap() failed\n"); - exit(0); + FAIL("mmap() failed"); } close(fd); NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { - printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n"); - exit(0); + FAIL("NSCreateObjectFileImageFromMemory failed"); } NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); if ( mod == NULL ) { - printf("[FAIL] NSLinkModule failed\n"); - exit(0); + FAIL("NSLinkModule failed"); } if ( !unlinkBeforeDestroy ) { // API lets you destroy ofi and NSModule lives on if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - exit(0); + FAIL("NSDestroyObjectFileImage failed"); } } NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); if ( sym == NULL ) { - printf("[FAIL] NSLookupSymbolInModule failed\n"); - exit(0); + FAIL("NSLookupSymbolInModule failed"); } void* func = NSAddressOfSymbol(sym); if ( func == NULL ) { - printf("[FAIL] NSAddressOfSymbol failed\n"); - exit(0); + FAIL("NSAddressOfSymbol failed"); } Dl_info info; if ( dladdr(func, &info) == 0 ) { - printf("[FAIL] dladdr(&p, xx) failed\n"); - exit(0); + FAIL("dladdr(&p, xx) failed"); } - //printf("_fooInBundle found in %s\n", info.dli_fname); + LOG("_fooInBundle found in %s", info.dli_fname); if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { - printf("[FAIL] NSUnLinkModule failed\n"); - exit(0); + FAIL("NSUnLinkModule failed"); } if ( dladdr(func, &info) != 0 ) { - printf("[FAIL] dladdr(&p, xx) found but should not have\n"); - exit(0); + FAIL("dladdr(&p, xx) found but should not have"); } if ( unlinkBeforeDestroy ) { if ( !NSDestroyObjectFileImage(ofi) ) { - printf("[FAIL] NSDestroyObjectFileImage failed\n"); - exit(0); + FAIL("NSDestroyObjectFileImage failed"); } } } @@ -104,31 +93,22 @@ static void tryImage(const char* path, const char* symbol) { void* handle = dlopen(path, RTLD_LAZY); if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-indirect-groupNum %s\n", path); - exit(0); + FAIL("dlopen(%s) error: %s", path, dlerror()); } void* sym = dlsym(handle, symbol); if ( sym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-indirect-groupNum %s\n", path); - exit(0); + FAIL("dlsym(%s) error: %s", symbol, dlerror()); } int result = dlclose(handle); if ( result != 0 ) { - printf("dlclose() returned %c\n", result); - printf("[FAIL] dlopen-indirect-groupNum %s\n", path); - exit(0); + FAIL("dlclose(%s) returned %c", path, result); } } -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dlopen-indirect-groupNum\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { checkBundle(argv[1], true); checkBundle(argv[1], false); @@ -142,6 +122,5 @@ int main(int argc, const char* argv[]) // And now open baz.dylib which depends on bar.dylib tryImage(argv[3], "bazInDylib"); - printf("[PASS] dlopen-indirect-groupNum\n"); - return 0; -} \ No newline at end of file + PASS("Success"); +} diff --git a/testing/test-cases/dlopen-intertwined.dtest/base.c b/testing/test-cases/dlopen-intertwined.dtest/base.c index e3143fb..3b37d08 100644 --- a/testing/test-cases/dlopen-intertwined.dtest/base.c +++ b/testing/test-cases/dlopen-intertwined.dtest/base.c @@ -2,6 +2,8 @@ #include #include +#include "test_support.h" + static const char* expectedStrings[] = { "a() from main", "initC", @@ -16,10 +18,9 @@ static const char** curState = expectedStrings; void setState(const char* from) { - printf("%s\n", from); +// LOG("%s", from); if ( strcmp(*curState, from) != 0 ) { - printf("[FAIL] dlopen-intertwined: expected %s\n", *curState); - exit(0); + FAIL("Expected %s", *curState); } ++curState; } diff --git a/testing/test-cases/dlopen-intertwined.dtest/main.c b/testing/test-cases/dlopen-intertwined.dtest/main.c index 11580d3..93668e1 100644 --- a/testing/test-cases/dlopen-intertwined.dtest/main.c +++ b/testing/test-cases/dlopen-intertwined.dtest/main.c @@ -16,6 +16,8 @@ #include #include +#include "test_support.h" + // main deps on A // main dlopens B which deps on C // main dlopens D which deps on C @@ -25,39 +27,31 @@ extern void a(const char*); extern void setState(const char* from); -int main() -{ - printf("[BEGIN] dlopen-intertwined\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { a("main"); void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); - exit(0); + FAIL("Error: %s", dlerror()); } handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); - exit(0); + FAIL("Error: %s", dlerror()); } handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); - exit(0); + FAIL("Error: %s", dlerror()); } handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); - exit(0); + FAIL("Error: %s", dlerror()); } setState("DONE"); - printf("[PASS] dlopen-intertwined\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-jna.dtest/foo.c b/testing/test-cases/dlopen-jna.dtest/foo.c new file mode 100644 index 0000000..df28bca --- /dev/null +++ b/testing/test-cases/dlopen-jna.dtest/foo.c @@ -0,0 +1,26 @@ + +#include +#include +#include + +#include "test_support.h" + +void foo() { + // foo can't see libFoundation.dylib as it's name isn't correct + void* handle = dlopen("libFoundation.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-jna, libfoo should not be able to dlopen()"); + } + + // foo can't see libSecurity.dylib as it's name isn't correct + void* handle2 = dlopen("libSecurity.dylib", RTLD_LAZY); + if ( handle2 != NULL ) { + FAIL("dlopen-jna, libfoo should not be able to dlopen()"); + } + + // foo can't see libCarbon.dylib as it's name isn't correct + void* handle3 = dlopen("libCarbon.dylib", RTLD_LAZY); + if ( handle3 != NULL ) { + FAIL("dlopen-jna, libfoo should not be able to dlopen()"); + } +} diff --git a/testing/test-cases/dlopen-jna.dtest/jna.c b/testing/test-cases/dlopen-jna.dtest/jna.c new file mode 100644 index 0000000..63143ed --- /dev/null +++ b/testing/test-cases/dlopen-jna.dtest/jna.c @@ -0,0 +1,26 @@ + +#include +#include +#include + +#include "test_support.h" + +void jna() { + // jna should see libFoundation.dylib as it's name is correct + void* handle = dlopen("libFoundation.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror()); + } + + // jna should see libSecurity.dylib as it's name is correct + void* handle2 = dlopen("libSecurity.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror()); + } + + // jna should see libCarbon.dylib as it's name is correct + void* handle3 = dlopen("libCarbon.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror()); + } +} diff --git a/testing/test-cases/dlopen-jna.dtest/main.c b/testing/test-cases/dlopen-jna.dtest/main.c new file mode 100644 index 0000000..d2748ae --- /dev/null +++ b/testing/test-cases/dlopen-jna.dtest/main.c @@ -0,0 +1,21 @@ +// BUILD(macos): $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD(macos): $CC jna.c -dynamiclib -install_name $RUN_DIR/jna.dylib -o $BUILD_DIR/jna.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/dlopen-jna.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/jna.dylib + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./dlopen-jna.exe + +#include "test_support.h" + +extern void foo(); +extern void jna(); + +int main(int arg, const char* argv[]) +{ + foo(); + jna(); + PASS("Success"); + return 0; +} + diff --git a/testing/test-cases/dlopen-long-error-message.dtest/main.c b/testing/test-cases/dlopen-long-error-message.dtest/main.c index 8b0a16a..d517337 100644 --- a/testing/test-cases/dlopen-long-error-message.dtest/main.c +++ b/testing/test-cases/dlopen-long-error-message.dtest/main.c @@ -8,23 +8,18 @@ #include #include +#include "test_support.h" - -int main() -{ - printf("[BEGIN] dlopen-long-error-message\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { for (int i=0; i < 10; ++i) { void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST); if ( handle != NULL ) { - printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n"); + FAIL("Should have failed on non-existent file"); return 0; } free(strdup("hello there")); } - printf("[PASS] dlopen-long-error-message\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c b/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c index 483725f..bca7d59 100644 --- a/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c +++ b/testing/test-cases/dlopen-prebuilt-dlopen-closure.dtest/main.c @@ -10,19 +10,14 @@ #include #include -int main() -{ - printf("[BEGIN] dlopen-prebuilt-dlopen-closure\n"); +#include "test_support.h" - void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-prebuilt-dlopen-closure\n"); - return 0; - } +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + void* handle = dlopen("/usr/lib/libobjc-trampolines.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - printf("[PASS] dlopen-prebuilt-dlopen-closure\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-race.dtest/foo.c b/testing/test-cases/dlopen-race.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-race.dtest/foo.c +++ b/testing/test-cases/dlopen-race.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-race.dtest/main.c b/testing/test-cases/dlopen-race.dtest/main.c index bad9ff7..1b1c3cb 100644 --- a/testing/test-cases/dlopen-race.dtest/main.c +++ b/testing/test-cases/dlopen-race.dtest/main.c @@ -9,25 +9,21 @@ #include #include +#include "test_support.h" - -int main() -{ - printf("[BEGIN] dlopen-read\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { __block bool allGood = true; dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { for (int i=0; i < 500; ++i) { void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-read: %s\n", dlerror()); - exit(0); + FAIL("dlopen-read: %s", dlerror()); } dlclose(handle); } }); - printf("[PASS] dlopen-read\n"); + PASS("Success"); return 0; } diff --git a/testing/test-cases/dlopen-realpath.dtest/main.c b/testing/test-cases/dlopen-realpath.dtest/main.c index c622e9f..30793d4 100644 --- a/testing/test-cases/dlopen-realpath.dtest/main.c +++ b/testing/test-cases/dlopen-realpath.dtest/main.c @@ -1,39 +1,34 @@ // 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 +// BUILD: $SYMLINK ./IOKit.framework/IOKit $BUILD_DIR/IOKit +// BUILD: $SYMLINK /System/Library/Frameworks/IOKit.framework $BUILD_DIR/IOKit.framework -// RUN: DYLD_FALLBACK_LIBRARY_PATH=/baz ./dlopen-realpath.exe +//FIXME: Use something besides IOKit so we do not need to copy it into the chroot +// R: DYLD_FALLBACK_LIBRARY_PATH=/baz ./dlopen-realpath.exe #include #include +#include "test_support.h" static void tryImage(const char* path) { - printf("[BEGIN] dlopen-realpath %s\n", path); - void* handle = dlopen(path, RTLD_LAZY); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-realpath %s\n", path); - return; - } + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(\"%s\"): %s", path, dlerror()); + } - int result = dlclose(handle); - if ( result != 0 ) { - printf("dlclose(%p): %s\n", handle, dlerror()); - printf("[FAIL] dlopen-realpath %s\n", path); - return; - } - - printf("[PASS] dlopen-realpath %s\n", path); + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(\"%s\"): %s", path, dlerror()); + } } -int main() -{ - tryImage("./IOKit.framework/IOKit"); - tryImage("./././IOKit/../IOKit.framework/IOKit"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + tryImage("./IOKit.framework/IOKit"); + tryImage("./././IOKit/../IOKit.framework/IOKit"); tryImage("./IOKit"); // Also try libSystem which has an alias in the OS to /usr/lib/libSystem.B.dylib @@ -44,6 +39,6 @@ int main() tryImage("//usr/lib/libSystem.dylib"); tryImage("/usr/./lib/libSystem.dylib"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-recurse.dtest/bar.c b/testing/test-cases/dlopen-recurse.dtest/bar.c index c86856e..077c499 100644 --- a/testing/test-cases/dlopen-recurse.dtest/bar.c +++ b/testing/test-cases/dlopen-recurse.dtest/bar.c @@ -1,5 +1,5 @@ int bar() { - return 0; + return 0; } diff --git a/testing/test-cases/dlopen-recurse.dtest/main.c b/testing/test-cases/dlopen-recurse.dtest/main.c index 53c244e..a48fc72 100644 --- a/testing/test-cases/dlopen-recurse.dtest/main.c +++ b/testing/test-cases/dlopen-recurse.dtest/main.c @@ -10,17 +10,13 @@ #include #include +#include "test_support.h" - -int main() -{ - printf("[BEGIN] dlopen-recurse\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // libfoo's initializer calls dlopen(). If that hangs, we have a locking bug void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); dlclose(handle); - printf("[PASS] dlopen-recurse\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c index c86856e..077c499 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c @@ -1,5 +1,5 @@ int bar() { - return 0; + return 0; } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c index ab6936e..0b8be87 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c @@ -1,5 +1,4 @@ -// 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 @@ -10,20 +9,14 @@ #include #include +#include "test_support.h" /// test that a call to dlopen() from a dylib uses the LC_RPATH from that dylib -extern bool test_dlopen(); +extern void 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; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + test_dlopen(); + PASS("Succcess"); } diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c index dba4287..c655aa3 100644 --- a/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c +++ b/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c @@ -3,17 +3,14 @@ #include #include -bool test_dlopen() +#include "test_support.h" + +void 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; + FAIL("dlopen(\"@rpath/libbar.dylib\") failed: %s", dlerror()); } - dlclose(handle); - - return true; } - diff --git a/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c b/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-implicit.dtest/main.c b/testing/test-cases/dlopen-rpath-implicit.dtest/main.c index 6d43d7f..19bde69 100644 --- a/testing/test-cases/dlopen-rpath-implicit.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-implicit.dtest/main.c @@ -1,5 +1,4 @@ -// 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 @@ -8,23 +7,17 @@ #include #include +#include "test_support.h" /// test that a leaf name passed to dlopen() searches the rpath -int main() -{ - printf("[BEGIN] dlopen-rpath-implicit\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* handle = dlopen("libimplicitrpath.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlopen-rpath-implicit: dlopen(libimplicitrpath.dylib) failed: %s\n", dlerror()); - return 0; + FAIL("dlopen(\"libimplicitrpath.dylib\") failed: %s", dlerror()); } dlclose(handle); - - printf("[PASS] dlopen-rpath-implicit\n"); - - return 0; + PASS("Succcess"); } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c index 08b8b6a..d9219bf 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c @@ -1,10 +1,7 @@ -#include -#include - +#include "test_support.h" __attribute__((constructor)) -void init() +void init(int argc, const char* argv[], const char* envp[], const char* apple[]) { - printf("[FAIL] dlopen-rpath-prev-override\n"); - exit(0); + FAIL("Bad dylib loaded"); } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c index 852b89b..127cfb9 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c @@ -1,5 +1,5 @@ int sub2() { - return 2; + return 2; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c index 98c93f6..d1955d6 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/good.c @@ -1,5 +1,5 @@ int sub1() { - return 1; + return 1; } diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c index 8aa206f..30b5dbb 100644 --- a/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c @@ -1,5 +1,4 @@ -// 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 @@ -16,18 +15,14 @@ #include #include -int main() -{ - printf("[BEGIN] dlopen-rpath-prev-override\n"); +#include "test_support.h" - 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; - } +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - printf("[PASS] dlopen-rpath-prev-override\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev.dtest/foo.c index c8e9924..13457f2 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/foo.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/foo.c @@ -1,5 +1,5 @@ int foo() { - return 10; + return 10; } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/main.c b/testing/test-cases/dlopen-rpath-prev.dtest/main.c index 67741d0..d164692 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/main.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/main.c @@ -1,5 +1,4 @@ -// 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 @@ -13,18 +12,14 @@ #include #include -int main() -{ - printf("[BEGIN] dlopen-rpath-prev\n"); +#include "test_support.h" - 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; - } +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - printf("[PASS] dlopen-rpath-prev\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c b/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c index 98c93f6..d1955d6 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c @@ -1,5 +1,5 @@ int sub1() { - return 1; + return 1; } diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c b/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c index 852b89b..127cfb9 100644 --- a/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c +++ b/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c @@ -1,5 +1,5 @@ int sub2() { - return 2; + return 2; } diff --git a/testing/test-cases/dlopen-signing.dtest/main.c b/testing/test-cases/dlopen-signing.dtest/main.c index eaaa69b..6a41bcd 100644 --- a/testing/test-cases/dlopen-signing.dtest/main.c +++ b/testing/test-cases/dlopen-signing.dtest/main.c @@ -10,39 +10,30 @@ #include #include -int main() { - printf("[BEGIN] dlopen-signing\n"); +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* handle = dlopen("signed.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-signing (signed loading signed)\n"); - return 0; + FAIL("dlerror(): %s", dlerror()); } else { int result = dlclose(handle); if ( result != 0 ) { - printf("dlclose() returned %c\n", result); - printf("[FAIL] dlopen-signing (signed unloading signed)\n"); - return 0; + FAIL("dlclose() returned %c", result); } } handle = dlopen("unsigned.dylib", RTLD_LAZY); if ( handle != NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-signing (signed loading unsigned)\n"); - return 0; + FAIL("dlerror(): %s", dlerror()); } else { int result = dlclose(handle); if ( result != 0 ) { - printf("dlclose() returned %c\n", result); - printf("[FAIL] dlopen-signing (signed unloading signed)\n"); - return 0; + FAIL("dlclose() returned %c", result); } } - printf("[PASS] dlopen-signing\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen-symlink.dtest/main.c b/testing/test-cases/dlopen-symlink.dtest/main.c index ce197a9..50ba18d 100644 --- a/testing/test-cases/dlopen-symlink.dtest/main.c +++ b/testing/test-cases/dlopen-symlink.dtest/main.c @@ -1,6 +1,6 @@ // BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -// BUILD: cd $BUILD_DIR && ln -s libfoo.dylib libfoo-symlink.dylib +// BUILD: $SYMLINK libfoo.dylib $BUILD_DIR/libfoo-symlink.dylib // BUILD: $CC main.c -o $BUILD_DIR/dlopen-symlink.exe -DRUN_DIR="$RUN_DIR" // RUN: ./dlopen-symlink.exe @@ -12,17 +12,13 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dlopen-symlink\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // call dlopen() with a path that is a symlink void* handle = dlopen(RUN_DIR "/libfoo-symlink.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlopen-symlink\n"); - return 0; + FAIL("dlerror(): %s", dlerror()); } // walk images to see if path was converted to real path @@ -30,27 +26,23 @@ int main() int count = _dyld_image_count(); for (int i=0; i < count; ++i) { const char* path = _dyld_get_image_name(i); - //printf("path[%2d]=%s\n", i, path); + LOG("path[%2d]=%s", i, path); if ( strstr(path, "libfoo") != NULL ) { if ( foundPath == NULL ) { foundPath = path; } else { - printf("[FAIL] dlopen-symlink: more than one libfoo found\n"); - return 0; + FAIL("More than one libfoo found"); } } } if ( foundPath == NULL ) { - printf("[FAIL] dlopen-symlink: no libfoo found\n"); - return 0; + FAIL("No libfoo found"); } if ( strstr(foundPath, "libfoo-symlink") != NULL ) { - printf("[FAIL] dlopen-symlink: path is symlink not real path: %s\n", foundPath); - return 0; + FAIL("Path is symlink not real path: %s", foundPath); } - printf("[PASS] dlopen-symlink\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlopen_from-basic.dtest/bar.c b/testing/test-cases/dlopen_from-basic.dtest/bar.c new file mode 100644 index 0000000..320c246 --- /dev/null +++ b/testing/test-cases/dlopen_from-basic.dtest/bar.c @@ -0,0 +1,4 @@ +void bar() +{ +} + diff --git a/testing/test-cases/dlopen_from-basic.dtest/main.c b/testing/test-cases/dlopen_from-basic.dtest/main.c new file mode 100644 index 0000000..40c0d23 --- /dev/null +++ b/testing/test-cases/dlopen_from-basic.dtest/main.c @@ -0,0 +1,45 @@ + +// BUILD: $CC bar.c -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib +// BUILD: $CC bar.c -dynamiclib -install_name @rpath/libbaz.dylib -o $BUILD_DIR/dir/libbaz.dylib +// BUILD: $CC test.c -dynamiclib -install_name $RUN_DIR/test/libtest.dylib -o $BUILD_DIR/test/libtest.dylib -rpath @loader_path/../dir +// BUILD: $CC main.c -o $BUILD_DIR/dlopen_from-basic.exe $BUILD_DIR/test/libtest.dylib + +// RUN: ./dlopen_from-basic.exe + +#include +#include +#include + +#include "test_support.h" + +/// test dlopen_from() works for both @rpath and @loader_path + +extern void test(); + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify the straight dlopen fails because libtest.dylib sets up the LC_RPATH + void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen(\"@rpath/libbar.dylib\") should not have succeeded"); + } + // verify dlopen_from() works becuase libtest.dylib sets up an LC_RPATH + handle = dlopen_from("@rpath/libbar.dylib", RTLD_LAZY, &test); + if ( handle == NULL ) { + FAIL("dlopen_from(\"@rpath/libbar.dylib\", &test) failed: %s", dlerror()); + } + + // verify the straight dlopen fails because main executables @loader_path is used + handle = dlopen("@loader_path/../dir/libbaz.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen(\"@loader_path/../dir/libbaz.dylib\") should not have succeeded"); + } + // verify dlopen_from() works with @loader_path resolving to where libtest.dylib is + handle = dlopen_from("@loader_path/../dir/libbaz.dylib", RTLD_LAZY, &test); + if ( handle == NULL ) { + FAIL("dlopen_from(\"@loader_path/../dir/libbaz.dylib\", &test) failed: %s", dlerror()); + } + + PASS("dlopen_from"); +} + diff --git a/testing/test-cases/dlopen_from-basic.dtest/test.c b/testing/test-cases/dlopen_from-basic.dtest/test.c new file mode 100644 index 0000000..93f31e9 --- /dev/null +++ b/testing/test-cases/dlopen_from-basic.dtest/test.c @@ -0,0 +1,11 @@ + +#include +#include +#include + +#include "test_support.h" + +void test() +{ +} + diff --git a/testing/test-cases/dlopen_preflight.dtest/main.c b/testing/test-cases/dlopen_preflight.dtest/main.c new file mode 100644 index 0000000..2c99ba6 --- /dev/null +++ b/testing/test-cases/dlopen_preflight.dtest/main.c @@ -0,0 +1,25 @@ + +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen_preflight.exe + +// RUN: ./dlopen_preflight.exe + +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + if ( !dlopen_preflight("/System/Library/Frameworks/Foundation.framework/Foundation") ) { + FAIL("Foundation.framework cannot be found"); + } + +#if TARGET_OS_OSX + if ( !dlopen_preflight("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") ) { + FAIL("Foundation.framework via symlink cannot be found"); + } +#endif + + PASS("SUCCESS"); +} + diff --git a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c index 445c15e..a6c750d 100644 --- a/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#include "test_support.h" // verify RTLD_DEFAULT search order @@ -33,54 +34,42 @@ static bool symbolInImage(const char* symName, const char* image) - -int main() -{ - printf("[BEGIN] dlsym-RTLD_DEFAULT\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify mainSymbol is found in main executable if ( !symbolInImage("mainSymbol", "dlsym-RTLD_DEFAULT") ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: mainSymbol\n"); - return 0; + FAIL("mainSymbol"); } // verify free is found in main executable, overrideing one in OS if ( !symbolInImage("free", "dlsym-RTLD_DEFAULT") ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: free\n"); - return 0; + FAIL("free"); } // verify foo is found in libfoo-static.dylib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: libfoo-dynamic.dylib could not be loaded\n"); - return 0; + FAIL("libfoo-dynamic.dylib could not be loaded"); } // verify foo is still found in statically linked lib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } // verify foo2 is found in libfoo-dynamic.dylib" if ( !symbolInImage("foo2", "libfoo-dynamic.dylib") ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: foo2 not in libfoo-dynamic.dylib\n"); - return 0; + FAIL("foo2 not in libfoo-dynamic.dylib"); } // renamed and re-exported symbols work if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) { - printf("[FAIL] dlsym-RTLD_DEFAULT: strcmp not found\n"); - return 0; + FAIL("strcmp not found"); } - printf("[PASS] dlsym-RTLD_DEFAULT\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c index 2300ad1..d0fd9e9 100644 --- a/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#include "test_support.h" // verify RTLD_MAIN_ONLY search order @@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image) -int main() -{ - printf("[BEGIN] dlsym-RTLD_MAIN_ONLY\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify mainSymbol is found if ( !symbolInImage("mainSymbol", "dlsym-RTLD_MAIN_ONLY") ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: mainSymbol should have been found\n"); - return 0; + FAIL("mainSymbol should have been found"); } // verify free is found in this program - not in OS if ( !symbolInImage("free", "dlsym-RTLD_MAIN_ONLY") ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: free\n"); - return 0; + FAIL("free"); } // verify foo is not found if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found\n"); - return 0; + FAIL("foo should not have been found"); } void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: libfoo-dynamic.dylib could not be loaded\n"); - return 0; + FAIL("libfoo-dynamic.dylib could not be loaded"); } // verify foo is still not found if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found after dlopen\n"); - return 0; + FAIL("foo should not have been found after dlopen"); } // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_MAIN_ONLY only searches main executable if ( dlsym(RTLD_MAIN_ONLY, "foo2") != NULL ) { - printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo2 found but should not have been\n"); - return 0; + FAIL("foo2 found but should not have been"); } - printf("[PASS] dlsym-RTLD_MAIN_ONLY\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c b/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c index 4a3130e..09dfda2 100644 --- a/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#include "test_support.h" // verify RTLD_NEXT search order @@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image) -int main() -{ - printf("[BEGIN] dlsym-RTLD_NEXT\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify mainSymbol is not found if ( dlsym(RTLD_NEXT, "mainSymbol") != NULL ) { - printf("[FAIL] dlsym-RTLD_NEXT: mainSymbol should not have been found\n"); - return 0; + FAIL("mainSymbol should not have been found"); } // verify free is found in OS (not local one) if ( !symbolInImage("free", "/usr/lib/") ) { - printf("[FAIL] dlsym-RTLD_NEXT: free\n"); - return 0; + FAIL("free"); } // verify foo is found in libfoo-static.dylib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlsym-RTLD_NEXT: libfoo-dynamic.dylib could not be loaded\n"); - return 0; + FAIL("libfoo-dynamic.dylib could not be loaded"); } // verify foo is still found in statically linked lib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_NEXT only searches thing this image would have seen if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) { - printf("[FAIL] dlsym-RTLD_NEXT: foo2 found but should not have been\n"); - return 0; + FAIL("foo2 found but should not have been"); } - printf("[PASS] dlsym-RTLD_NEXT\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c b/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c index 5af0e2f..7f83ffe 100644 --- a/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c +++ b/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#include "test_support.h" // verify RTLD_SELF search order @@ -34,47 +35,37 @@ static bool symbolInImage(const char* symName, const char* image) -int main() -{ - printf("[BEGIN] dlsym-RTLD_SELF\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // verify mainSymbol is found if ( dlsym(RTLD_SELF, "mainSymbol") == NULL ) { - printf("[FAIL] dlsym-RTLD_SELF: mainSymbol should have been found\n"); - return 0; + FAIL("mainSymbol should have been found"); } // verify free is found in this program - not in OS if ( !symbolInImage("free", "dlsym-RTLD_SELF") ) { - printf("[FAIL] dlsym-RTLD_SELF: free\n"); - return 0; + FAIL("free"); } // verify foo is found in libfoo-static.dylib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlsym-RTLD_SELF: libfoo-dynamic.dylib could not be loaded\n"); - return 0; + FAIL("libfoo-dynamic.dylib could not be loaded"); } // verify foo is still found in statically linked lib if ( !symbolInImage("foo", "libfoo-static.dylib") ) { - printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n"); - return 0; + FAIL("foo not in libfoo-static.dylib"); } // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_SELF only searches thing this image would have seen if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) { - printf("[FAIL] dlsym-RTLD_SELF: foo2 found but should not have been\n"); - return 0; + FAIL("foo2 found but should not have been"); } - printf("[PASS] dlsym-RTLD_SELF\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-handle.dtest/main.c b/testing/test-cases/dlsym-handle.dtest/main.c index 52e7750..1bd5499 100644 --- a/testing/test-cases/dlsym-handle.dtest/main.c +++ b/testing/test-cases/dlsym-handle.dtest/main.c @@ -11,89 +11,74 @@ #include #include +#include "test_support.h" // verify RTLD_DEFAULT search order int mainSymbol = 4; -int main() -{ - printf("[BEGIN] dlsym-handle\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); if ( fooHandle == NULL ) { - printf("[FAIL] dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror()); - return 0; + FAIL("libfoo.dylib could not be loaded, %s", dlerror()); } void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY); if ( barHandle == NULL ) { - printf("[FAIL] dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror()); - return 0; + FAIL("libbar.dylib could not be loaded, %s", dlerror()); } // verify fooHandle does not find mainSymbol if ( dlsym(fooHandle, "mainSymbol") != NULL ) { - printf("[FAIL] dlsym-handle: mainSymbol was found with fooHandle\n"); - return 0; + FAIL("mainSymbol was found with fooHandle"); } // verify fooHandle can find foo if ( dlsym(fooHandle, "foo") == NULL ) { - printf("[FAIL] dlsym-handle: foo not found with fooHandle\n"); - return 0; + FAIL("foo not found with fooHandle"); } // verify fooHandle can find base if ( dlsym(fooHandle, "base") == NULL ) { - printf("[FAIL] dlsym-handle: base not found with fooHandle\n"); - return 0; + FAIL("base not found with fooHandle"); } // verify fooHandle cannot find bar if ( dlsym(fooHandle, "bar") != NULL ) { - printf("[FAIL] dlsym-handle: bar found with fooHandle\n"); - return 0; + FAIL("bar found with fooHandle"); } // verify barHandle can find bar if ( dlsym(barHandle, "bar") == NULL ) { - printf("[FAIL] dlsym-handle: bar not found with barHandle\n"); - return 0; + FAIL("bar not found with barHandle"); } // verify barHandle can find base if ( dlsym(barHandle, "base") == NULL ) { - printf("[FAIL] dlsym-handle: base not found with barHandle\n"); - return 0; + FAIL("base not found with barHandle"); } // verify barHandle cannot find foo if ( dlsym(barHandle, "foo") != NULL ) { - printf("[FAIL] dlsym-handle: foo found with barHandle\n"); - return 0; + FAIL("foo found with barHandle"); } // verify renamed and re-exported symbols work if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) { - printf("[FAIL] dlsym-handle: strcmp not found\n"); - return 0; + FAIL("strcmp not found"); } // verify bad handle errors if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) { - printf("[FAIL] dlsym-handle: malloc found with bad handle\n"); - return 0; + FAIL("malloc found with bad handle"); } else { const char* message = dlerror(); if ( strstr(message, "invalid") == NULL ) { - printf("[FAIL] dlsym-handle: invalid handle error message missing 'invalid'\n"); - return 0; + FAIL(" invalid handle error message missing 'invalid'"); } } - printf("[PASS] dlsym-handle\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c b/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c index 802f30e..ef66f2d 100644 --- a/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c +++ b/testing/test-cases/dlsym-in-interposed-malloc.dtest/interposer.c @@ -5,6 +5,8 @@ #include #include +#include "test_support.h" + static bool inMalloc = false; static bool forceSystemMalloc = false; @@ -18,8 +20,7 @@ void* mymalloc(size_t size) if (inMalloc) { // Recursion! This shouldn't happen. forceSystemMalloc = true; - printf("[FAIL] dlsym-in-interposed-malloc mymalloc() is recursive\n"); - exit(1); + FAIL("mymalloc() is recursive"); } inMalloc = true; @@ -28,14 +29,12 @@ void* mymalloc(size_t size) void* sym = dlsym(RTLD_DEFAULT, "malloc"); if (sym == NULL) { forceSystemMalloc = true; - printf("[FAIL] dlsym-in-interposed-malloc dlsym failed\n"); - exit(1); + FAIL("dlsym failed"); } if (sym != mymalloc) { forceSystemMalloc = true; - printf("[FAIL] dlsym-in-interposed-malloc dlsym result %p != mymalloc %p\n", sym, &mymalloc); - exit(1); + FAIL("dlsym result %p != mymalloc %p", sym, &mymalloc); } void* result = malloc(size); diff --git a/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c b/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c index 70adcf0..a7faa59 100644 --- a/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c +++ b/testing/test-cases/dlsym-in-interposed-malloc.dtest/main.c @@ -9,16 +9,12 @@ #include #include -int main() -{ - printf("[BEGIN] dlsym-in-interposed-malloc\n"); +#include "test_support.h" +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // malloc should have been called when dyld3's libdyld was initialized, but // call it one more time anyway just to make sure its working (void)malloc(1); - - //printf("%p %p %p %p\n", p1, p2, p3, p4); - printf("[PASS] dlsym-in-interposed-malloc\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dlsym-re-export.dtest/main.c b/testing/test-cases/dlsym-re-export.dtest/main.c index b1752d6..4bc5ee3 100644 --- a/testing/test-cases/dlsym-re-export.dtest/main.c +++ b/testing/test-cases/dlsym-re-export.dtest/main.c @@ -1,5 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2 // BUILD: $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib -o $BUILD_DIR/sub1/libsub1.dylib // BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib -o $BUILD_DIR/sub2/libsub2.dylib // BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib @@ -12,32 +11,25 @@ #include #include -int main() -{ - printf("[BEGIN] dlsym-re-export\n"); +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports) - void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); - if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlsym-re-export\n"); - return 0; - } + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); + if ( handle == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - void* sym1 = dlsym(handle, "sub1"); - if ( sym1 == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlsym-re-export\n"); - return 0; - } + void* sym1 = dlsym(handle, "sub1"); + if ( sym1 == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - void* sym2 = dlsym(handle, "sub2"); - if ( sym2 == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] dlsym-re-export\n"); - return 0; - } + void* sym2 = dlsym(handle, "sub2"); + if ( sym2 == NULL ) { + FAIL("dlerror(): %s", dlerror()); + } - printf("[PASS] dlsym-re-export\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dtrace.dtest/main.c b/testing/test-cases/dtrace.dtest/main.c index 088e50c..3223223 100644 --- a/testing/test-cases/dtrace.dtest/main.c +++ b/testing/test-cases/dtrace.dtest/main.c @@ -1,32 +1,28 @@ -// BUILD_ONLY: MacOSX - // if we ever re-enable this on iOS we will need to add // BOOT_ARGS: dtrace_dof_mode=1 -// BUILD: /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h -// BUILD: $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe -// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe - -// RUN: $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe +// BUILD(macos): $DTRACE -h -s main.d -o $BUILD_DIR/probes.h +// BUILD(macos): $CC main.c -I$BUILD_DIR -o $BUILD_DIR/dtrace.exe $DEPENDS_ON $BUILD_DIR/probes.h +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe +// BUILD(ios,tvos,watchos,bridgeos): +// RUN: $SUDO /usr/sbin/dtrace -o /dev/null 2> /dev/null -n 'dyld_testing*:dtrace.exe:main:callback' -c $RUN_DIR/dtrace.exe #include #include #include #include +#include "test_support.h" + #include "probes.h" -int main() -{ - printf("[BEGIN] dtrace\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { DYLD_TESTING_CALLBACK(); if (!DYLD_TESTING_CALLBACK_ENABLED()) - printf("[FAIL] dtrace: DYLD_TESTING_CALLBACK_ENABLED() returned false\n"); + FAIL("DYLD_TESTING_CALLBACK_ENABLED() returned false"); else - printf("[PASS] dtrace\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld-insert-library-double.dtest/main.cpp b/testing/test-cases/dyld-insert-library-double.dtest/main.cpp index 1139647..cc29682 100644 --- a/testing/test-cases/dyld-insert-library-double.dtest/main.cpp +++ b/testing/test-cases/dyld-insert-library-double.dtest/main.cpp @@ -1,7 +1,7 @@ // BOOT_ARGS: dyld_flags=2 -// BUILD: $CC main.cpp -std=c++11 -o $BUILD_DIR/double_insert_main.exe +// BUILD: $CXX main.cpp -std=c++11 -o $BUILD_DIR/double_insert_main.exe // 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: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/double_insert_main.exe @@ -16,6 +16,8 @@ #include #include +#include "test_support.h" + bool gFoundLibrary = false; const char* gLibraryName = NULL; @@ -28,19 +30,14 @@ bool wasImageLoaded(const char* libraryName) { } }); if (!gFoundLibrary) - printf("[FAIL] dyld-insert-library-double: expected insert to pass for '%s'\n", libraryName); + FAIL("Expected insert to pass for '%s'", libraryName); return gFoundLibrary; } -int main() -{ - printf("[BEGIN] dyld-insert-library-double\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if (!wasImageLoaded("libfoo.dylib") || !wasImageLoaded("libbar.dylib")) { return 0; } - printf("[PASS] dyld-insert-library-double\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp b/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp index 3bf1204..d714e93 100644 --- a/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp +++ b/testing/test-cases/dyld-insert-library-rpath.dtest/main.cpp @@ -1,8 +1,7 @@ // BOOT_ARGS: dyld_flags=2 -// BUILD: mkdir -p $BUILD_DIR/lib -// BUILD: $CC main.cpp -std=c++11 -o $BUILD_DIR/rpath_insert_main.exe -Wl,-rpath,$RUN_DIR/lib +// BUILD: $CXX main.cpp -std=c++11 -o $BUILD_DIR/rpath_insert_main.exe -Wl,-rpath,$RUN_DIR/lib // BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/lib/libfoo.dylib // BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib // BUILD: $CC baz.c -dynamiclib -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib @@ -22,6 +21,8 @@ #include #include +#include "test_support.h" + bool gFoundLibrary = false; const char* gLibraryName = NULL; @@ -36,13 +37,9 @@ bool wasImageLoaded(const char* libraryName) { return gFoundLibrary; } -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dyld-insert-library-rpath\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if (argc != 2) { - printf("[FAIL] dyld-insert-library-rpath: expected library name\n"); - return 0; + FAIL("Expected library name"); } bool expectInsertFailure = getenv("DYLD_AMFI_FAKE") != NULL; @@ -50,18 +47,14 @@ int main(int argc, const char* argv[]) if (wasImageLoaded(argv[1])) { // Image was loaded, but make sure that is what we wanted to happen if ( expectInsertFailure ) { - printf("[FAIL] dyld-insert-library-rpath: expected insert to fail for '%s'\n", argv[1]); - return 0; + FAIL("Expected insert to fail for '%s'", argv[1]); } } else { // Image was not loaded, so make sure we are ok with that if ( !expectInsertFailure ) { - printf("[FAIL] dyld-insert-library-rpath: expected insert to pass for '%s'\n", argv[1]); - return 0; + FAIL("Expected insert to pass for '%s'", argv[1]); } } - printf("[PASS] dyld-insert-library-rpath\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_abort_payload.dtest/main.c b/testing/test-cases/dyld_abort_payload.dtest/main.c deleted file mode 100644 index 3657491..0000000 --- a/testing/test-cases/dyld_abort_payload.dtest/main.c +++ /dev/null @@ -1,178 +0,0 @@ - -// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib -// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib -// BUILD: $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib -o $BUILD_DIR/prog_missing_dylib.exe -// 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -static bool sSignalCaught = false; -static bool sChildAbortInfoCorrect = false; -static pid_t sChildPid = 0; -static uint64_t sExpectedDyldReason = 0; -static const char* sExpectedDylibPath = NULL; -static const char* sExpectedSymbol = NULL; - - -static void childDied(int sig) -{ - sSignalCaught = true; - //printf("sigchld for pid=%d\n", sChildPid); - - struct proc_exitreasoninfo info; - bzero(&info, sizeof(info)); - uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; - bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); - info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; - info.eri_kcd_buf = (user_addr_t)packReasonData; - //fprintf(stderr, "info=%p\n", &info); - 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 (%d) != OS_REASON_DYLD\n", info.eri_namespace); - return; - } - if ( info.eri_code != 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 ( !kcdata_iter_valid(iter) ) { - printf("invalid kcdata iterator from payload data\n"); - return; - } - - if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){ - printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n"); - return; - } - - kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD); - if ( !kcdata_iter_valid(payloadIter) ) { - printf("invalid kcdata payload iterator from payload data\n"); - return; - } - const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter); - - if ( dyldInfo->version != 1 ) { - printf("dyld payload is not version 1\n"); - return; - } - - if ( (dyldInfo->flags & 1) == 0 ) { - printf("dyld flags should have low bit set to me process terminated at launch\n"); - return; - } - - if ( sExpectedDylibPath != NULL ) { - if ( dyldInfo->targetDylibPathOffset != 0 ) { - const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset; - if ( strstr(targetDylib, sExpectedDylibPath) == NULL ) { - printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath); - return; - } - } - else { - printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath); - return; - } - } - - if ( sExpectedSymbol != NULL ) { - if ( dyldInfo->targetDylibPathOffset != 0 ) { - const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset; - if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) { - printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol); - return; - } - } - else { - printf("symbol (%s) not provided by dyld\n", sExpectedSymbol); - return; - } - } - - sChildAbortInfoCorrect = true; -} - - -bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) -{ - sSignalCaught = false; - sChildAbortInfoCorrect = false; - sExpectedDyldReason = dyldReason; - sExpectedDylibPath = expectedDylibPath; - sExpectedSymbol = expectedSymbol; - - // fork and exec child - sChildPid = fork(); - if ( sChildPid < 0 ) - err(EXIT_FAILURE, "fork"); - if ( sChildPid == 0 ) { - // child side - char* childArgv[] = { (char*)prog, NULL }; - int result = execvp(prog, childArgv); - err(EXIT_FAILURE, "exec(\"%s\",...)", prog); - } - for(int i=0; i < 10; ++i) { - if ( sSignalCaught ) - break; - sleep(1); - } - - return sChildAbortInfoCorrect; -} - - -int main(int argc, const char* argv[]) -{ - bool someTestFailed = false; - printf("[BEGIN] dyld_abort_payload\n"); - - // set up signal handler for catching child terminations - signal(SIGCHLD, childDied); - - // test launch program with missing library - if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) { - printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n"); - someTestFailed = true; - } - - // test launch program with missing symbol - if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) { - printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n"); - someTestFailed = true; - } - - if ( !someTestFailed ) - printf("[PASS] dyld_abort_payload\n"); - - return 0; -} - diff --git a/testing/test-cases/dyld_abort_payload.dtest/main.cpp b/testing/test-cases/dyld_abort_payload.dtest/main.cpp new file mode 100644 index 0000000..67abdf0 --- /dev/null +++ b/testing/test-cases/dyld_abort_payload.dtest/main.cpp @@ -0,0 +1,138 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libMissingDylib.dylib -o $BUILD_DIR/libMissingDylib.dylib +// BUILD: $CC emptyMain.c $BUILD_DIR/libMissingDylib.dylib -o $BUILD_DIR/prog_missing_dylib.exe +// 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: $CXX main.cpp -o $BUILD_DIR/dyld_abort_tests.exe -DRUN_DIR="$RUN_DIR" + +// NO_CRASH_LOG: prog_missing_dylib.exe +// NO_CRASH_LOG: prog_missing_symbol.exe + +// RUN: ./dyld_abort_tests.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + + +void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) { + dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{}); + LOG("Running test for %s / %s / %s", prog, expectedDylibPath, expectedSymbol); + _process process; + process.set_executable_path(prog); + process.set_crash_handler(^(task_t task) { + LOG("Crash for task=%u", task); + mach_vm_address_t corpse_data; + mach_vm_size_t corpse_size; + kern_return_t kr = task_map_corpse_info_64(mach_task_self(), task, &corpse_data, &corpse_size); + if (kr != KERN_SUCCESS) { + FAIL("Could not read corpse data kr=(%d)", kr); + } + + kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size); + if (!kcdata_iter_valid(autopsyData)) { + FAIL("Corpse Data Invalid"); + } + + kcdata_iter_t pidIter = kcdata_iter_find_type(autopsyData, TASK_CRASHINFO_PID); + pid_t pid = *(pid_t *) (kcdata_iter_payload(pidIter)); + LOG("Crash for pid=%u", pid); + + kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT); + if (!kcdata_iter_valid(exitReasonData)) { + FAIL("Could not find exit data"); + } + struct exit_reason_snapshot *ers = (struct exit_reason_snapshot *)kcdata_iter_payload(exitReasonData); + + if ( ers->ers_namespace != OS_REASON_DYLD ) { + FAIL("eri_namespace (%d) != OS_REASON_DYLD", ers->ers_namespace); + } + if ( ers->ers_code != dyldReason ) { + FAIL("eri_code (%llu) != dyldReason (%lld)", ers->ers_code, dyldReason); + } + kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size); + bool foundOSReason = false; + + KCDATA_ITER_FOREACH(iter) { + if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) { + kcdata_iter_t nestedIter = kcdata_iter(kcdata_iter_payload(iter), kcdata_iter_size(iter)); + if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){ + return; + } + foundOSReason = true; + kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD); + if ( !kcdata_iter_valid(payloadIter) ) { + FAIL("invalid kcdata payload iterator from payload data"); + } + const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter); + + if ( dyldInfo->version != 1 ) { + FAIL("dyld payload is not version 1"); + } + + if ( (dyldInfo->flags & 1) == 0 ) { + FAIL("dyld flags should have low bit set to indicate process terminated during launch"); + } + + if ( expectedDylibPath != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset; + if ( strstr(targetDylib, expectedDylibPath) == NULL ) { + FAIL("dylib path (%s) not what expected (%s)", targetDylib, expectedDylibPath); + } + } else { + FAIL("dylib path (%s) not provided by dyld", expectedDylibPath); + } + } + + if ( expectedSymbol != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset; + if ( strcmp(expectedSymbol, missingSymbol) != 0 ) { + FAIL("symbol (%s) not what expected (%s)", missingSymbol, expectedSymbol); + } + } else { + FAIL("symbol (%s) not provided by dyld", expectedSymbol); + } + } + } + } + if (!foundOSReason) { + FAIL("Did not find KCDATA_BUFFER_BEGIN_OS_REASON"); + } + oneShotSemaphoreBlock(); + }); + + pid_t pid = process.launch(); + LOG("Launch pid: %u", pid); + // We need to wait for the task to crash in before we call PASS(). Ideally we would return the block and wait + // in main(), that way we could run multiple processes simultaneously, but when we do that there are strange crashes + // and deadlocks, I suspect it has something to do with block capture of parameters sometimes being references. + // but it is not entirely clear what the intended semantics are. + dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER); +} + + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // test launch program with missing library + runTest(RUN_DIR "/prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL); + runTest(RUN_DIR "/prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol"); + PASS("Success"); +} + diff --git a/testing/test-cases/dyld_fork-locks.dest/main.c b/testing/test-cases/dyld_fork-locks.dest/main.c index 371c5a8..db60130 100644 --- a/testing/test-cases/dyld_fork-locks.dest/main.c +++ b/testing/test-cases/dyld_fork-locks.dest/main.c @@ -7,6 +7,8 @@ #include #include +#include "test_support.h" + bool isParent = true; static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide) @@ -19,8 +21,7 @@ static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide // fork and exec child pid_t sChildPid = fork(); if ( sChildPid < 0 ) { - printf("[FAIL] dyld_fork_test didn't fork\n"); - return; + FAIL("Didn't fork"); } if ( sChildPid == 0 ) { // child side @@ -28,15 +29,12 @@ static void notifyBeforeFork(const struct mach_header* mh, intptr_t vmaddr_slide } } -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dyld_fork_test\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { _dyld_register_func_for_add_image(¬ifyBeforeFork); if (isParent) { - printf("[PASS] dyld_fork_test\n"); + PASS("Success"); } return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/dyld_get_image_versions.dtest/main.c b/testing/test-cases/dyld_get_image_versions.dtest/main.c index 71f6bc2..a8c4a97 100644 --- a/testing/test-cases/dyld_get_image_versions.dtest/main.c +++ b/testing/test-cases/dyld_get_image_versions.dtest/main.c @@ -7,15 +7,14 @@ #include #include +#include "test_support.h" + extern struct mach_header __dso_handle; -int main() -{ - printf("[BEGIN] dyld_get_image_versions\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // 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); + LOG("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X", platform, sdkVersion, minOS); }); uint8_t badFile[4096]; @@ -31,11 +30,9 @@ int main() // 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); + LOG("bad binary: platform=%d, sdk=0x%08X, minOS-0x%08X", platform, sdkVersion, minOS); }); - - printf("[PASS] dyld_get_image_versions\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt b/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt deleted file mode 100644 index 43b3565..0000000 --- a/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt +++ /dev/null @@ -1 +0,0 @@ -bad file diff --git a/testing/test-cases/dyld_get_sdk_version.dtest/main.c b/testing/test-cases/dyld_get_sdk_version.dtest/main.c index c5032a0..b2df752 100644 --- a/testing/test-cases/dyld_get_sdk_version.dtest/main.c +++ b/testing/test-cases/dyld_get_sdk_version.dtest/main.c @@ -7,23 +7,20 @@ #include #include +#include "test_support.h" + extern struct mach_header __dso_handle; -int main() -{ - printf("[BEGIN] dyld_get_sdk_version\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // should succeed if ( dyld_get_sdk_version(&__dso_handle) == 0 ) { - printf("[FAIL] dyld_get_sdk_version: expected SDK\n"); - return 0; + FAIL("dyld_get_sdk_version: expected SDK"); } // should fail const char* text = "bad text"; if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) { - printf("[FAIL] dyld_get_sdk_version: expected failure\n"); - return 0; + FAIL("dyld_get_sdk_version: expected failure"); } @@ -31,19 +28,16 @@ int main() 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; + FAIL("dyld_get_program_sdk_watch_os_version"); } #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; + FAIL("dyld_get_program_sdk_watch_os_version"); } #endif - printf("[PASS] dyld_get_sdk_version\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c b/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c index 765b47d..27df5a1 100644 --- a/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c +++ b/testing/test-cases/dyld_has_inserted_or_interposing_libraries.dtest/main.c @@ -15,15 +15,15 @@ #include #include -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dyld_has_inserted_or_interposing_libraries\n"); +#include "test_support.h" +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { bool actual = dyld_has_inserted_or_interposing_libraries(); bool expected = (argc == 2) && (strcmp(argv[1], "true") == 0); - const char* result = (actual == expected) ? "PASS" : "FAIL"; - printf("[%s] dyld_has_inserted_or_interposing_libraries\n", result); + if (actual != expected) { + FAIL("dyld_has_inserted_or_interposing_libraries"); + } - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_image_path_containing_address.dtest/main.c b/testing/test-cases/dyld_image_path_containing_address.dtest/main.c index 9064caf..cf31cda 100644 --- a/testing/test-cases/dyld_image_path_containing_address.dtest/main.c +++ b/testing/test-cases/dyld_image_path_containing_address.dtest/main.c @@ -10,23 +10,19 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dyld_image_path_containing_address-test\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { int count = _dyld_image_count(); for (int i=0; i < count; ++i) { const struct mach_header* mh = _dyld_get_image_header(i); const char* name1 = _dyld_get_image_name(i); const char* name2 = dyld_image_path_containing_address(mh); if ( strcmp(name1, name2) != 0 ) { - printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2); - return 0; + FAIL("%s != %s", name1, name2); } } - printf("[PASS] dyld_image_path_containing_address-test\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_need_closure.dtest/main.c b/testing/test-cases/dyld_need_closure.dtest/main.c index 68aa687..9800168 100644 --- a/testing/test-cases/dyld_need_closure.dtest/main.c +++ b/testing/test-cases/dyld_need_closure.dtest/main.c @@ -8,24 +8,20 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] dyld_need_closure\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // We only support trying to save to containerised paths, so anything not // of that form should fail - if ( !dyld_need_closure("./foo.exe", "/tmp/Containers/Data/") ) { - printf("[FAIL] dyld_closure: Should have needed a closure for containerised path\n"); - return 0; + +// FIXME: dyld_need_closure() needs an existing directory structure, so we can't run this in BATS +// if ( !dyld_need_closure("./foo.exe", "/private/var/mobile/Containers/Data/Application") ) { +// FAIL("Should have needed a closure for containerised path"); +// } + + if ( dyld_need_closure("./foo.exe", "/private/var/mobile/Other/Stuff") ) { + FAIL("Should have rejected a closure for non-containerised path"); } - if ( dyld_need_closure("./foo.exe", "/tmp/Containers/Data2/") ) { - printf("[FAIL] dyld_closure: Should have rejected a closure for non-containerised path\n"); - return 0; - } - - printf("[PASS] dyld_need_closure\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/dyld_process_info.dtest/linksWithCF.c b/testing/test-cases/dyld_process_info.dtest/linksWithCF.c index 862da98..96c3a65 100644 --- a/testing/test-cases/dyld_process_info.dtest/linksWithCF.c +++ b/testing/test-cases/dyld_process_info.dtest/linksWithCF.c @@ -1,13 +1,21 @@ +#include #include -#include -#include +#include +#include __attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy; -int main() -{ - (void)kill(getpid(), SIGSTOP); - return 0; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + dispatch_source_t exitSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(), + DISPATCH_PROC_EXIT, dispatch_get_main_queue()); + dispatch_source_set_event_handler(exitSource, ^{ + exit(0); + }); + dispatch_resume(exitSource); + dispatch_async(dispatch_get_main_queue(), ^{ + kill(getppid(), SIGUSR1); + }); + dispatch_main(); } diff --git a/testing/test-cases/dyld_process_info.dtest/main.c b/testing/test-cases/dyld_process_info.dtest/main.c deleted file mode 100644 index edd700b..0000000 --- a/testing/test-cases/dyld_process_info.dtest/main.c +++ /dev/null @@ -1,107 +0,0 @@ - -// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation -// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe -ldarwintest -// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe - -// RUN: $SUDO ./dyld_process_info.exe - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dyld_test.h" - -static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac) -{ - kern_return_t result; - dyld_process_info info = _dyld_process_info_create(task, 0, &result); - T_EXPECT_MACH_SUCCESS(result, "dyld_process_info() should succeed"); - T_ASSERT_NOTNULL(info, "dyld_process_info(task, 0) alwats return a value"); - - dyld_process_state_info stateInfo; - bzero(&stateInfo, sizeof(stateInfo)); - _dyld_process_info_get_state(info, &stateInfo); - T_EXPECT_EQ_UINT((stateInfo.dyldState == dyld_process_state_not_started), launchedSuspended, "If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started"); - if ( !launchedSuspended ) { - T_EXPECT_GE_UCHAR(stateInfo.dyldState, dyld_process_state_libSystem_initialized, "libSystem should be initalized by now"); - T_EXPECT_GT_UINT(stateInfo.imageCount, 0, "image count should be > 0"); - T_EXPECT_GT_UINT(stateInfo.initialImageCount, 0, "initial image count should be > 0"); - T_EXPECT_GE_UINT(stateInfo.imageCount, stateInfo.initialImageCount, "image count should be >= initial image count"); - } - - if (launchedSuspended) { - T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), 0, "_dyld_process_info_get_platform() should be 0 for launchSuspended processes"); - } else if (forceIOSMac) { - T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), PLATFORM_IOSMAC, "_dyld_process_info_get_platform() should be PLATFORM_IOSMAC"); - } else { - T_EXPECT_EQ_UINT(_dyld_process_info_get_platform(info), dyld_get_active_platform(), "_dyld_process_info_get_platform() should be the same dyld_get_active_platform()"); - } - - __block bool foundDyld = false; - __block bool foundMain = false; - __block bool foundCF = false; - _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { - if ( strstr(path, "/dyld") != NULL ) - foundDyld = true; - if ( strstr(path, "/linksWithCF.exe") != NULL ) - foundMain = true; - if ( strstr(path, "/dyld_process_info.exe") != NULL ) - foundMain = true; - if ( strstr(path, "/CoreFoundation.framework/") != NULL ) - foundCF = true; - }); - T_EXPECT_TRUE(foundDyld, "dyld should always be in the image list"); - T_EXPECT_TRUE(foundMain, "The main executable should always be in the image list"); - if (expectCF) { - T_EXPECT_TRUE(foundCF, "CF should be in the image list"); - } - - _dyld_process_info_release(info); -} - -static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac) -{ - if (forceIOSMac) { setenv("DYLD_FORCE_PLATFORM", "6", 1); } - pid_t pid = T_POSIXSPAWN_ASSERT(launchSuspended, launchOtherArch, INSTALL_PATH "/linksWithCF.exe"); - task_t task = T_TASK_FOR_PID_ASSERT(pid); - if (forceIOSMac) { unsetenv("DYLD_FORCE_PLATFORM"); } - - // wait until process is up and has suspended itself - struct task_basic_info info; - do { - unsigned count = TASK_BASIC_INFO_COUNT; - kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count); - usleep(10000); - } while ( info.suspend_count == 0 ); - - inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac); - int r = kill(pid, SIGKILL); - waitpid(pid, &r, 0); -} - -T_DECL_DYLD(dyld_process_info, "Test basic dyld_process_info functionality", T_META_ASROOT(true)) { - launchTest(false, false, false); - launchTest(false, true, false); -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // FIXME: Reenable these ones i386 is turned back on for simulators - //launchTest(true, false, false); - //launchTest(true, true, false); - launchTest(false, false, true); - launchTest(false, true, true); - //FIXME: This functionality is broken, but it is an edge case no one should ever hit - //launchTest(true, true, true); -#endif - inspectProcess(mach_task_self(), false, false, false); -} diff --git a/testing/test-cases/dyld_process_info.dtest/main.cpp b/testing/test-cases/dyld_process_info.dtest/main.cpp new file mode 100644 index 0000000..82ebc28 --- /dev/null +++ b/testing/test-cases/dyld_process_info.dtest/main.cpp @@ -0,0 +1,167 @@ + +// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation +// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_process_info.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe + +// RUN: $SUDO ./dyld_process_info.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + + +static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, bool forceIOSMac) +{ + kern_return_t result; + dyld_process_info info = _dyld_process_info_create(task, 0, &result); + LOG("_dyld_process_info_create(): return(%u), info(0x%llx)", result, (uint64_t)info); + + if (result != KERN_SUCCESS) { + FAIL("_dyld_process_info_create() should succeed"); + } + if (info == NULL) { + FAIL("_dyld_process_info_create(task, 0) alwats return a value"); + } + + dyld_process_state_info stateInfo; + bzero(&stateInfo, sizeof(stateInfo)); + _dyld_process_info_get_state(info, &stateInfo); + if ((stateInfo.dyldState == dyld_process_state_not_started) != launchedSuspended) { + FAIL("If launchSuspended then stateInfo.dyldState shoould be dyld_process_state_not_started"); + } + if ( !launchedSuspended ) { + if (stateInfo.dyldState < dyld_process_state_libSystem_initialized) { FAIL("libSystem should be initalized by now"); } + if (stateInfo.imageCount == 0) { return FAIL("image count should be > 0"); } + if (stateInfo.initialImageCount == 0) { return FAIL("initial image count should be > 0"); } + if (stateInfo.imageCount < stateInfo.initialImageCount) { FAIL("image count should be >= initial image count"); } + } + + dyld_platform_t remotePlatform = _dyld_process_info_get_platform(info); + dyld_platform_t localPlatform = dyld_get_active_platform(); + if (launchedSuspended) { + if (remotePlatform != 0) { + FAIL("_dyld_process_info_get_platform() should be 0 for launchSuspended processes"); + } + } else if (forceIOSMac && (remotePlatform != PLATFORM_MACCATALYST)) { + FAIL("_dyld_process_info_get_platform(%u) should be PLATFORM_MACCATALYST", remotePlatform); + } else if (!forceIOSMac && (remotePlatform != localPlatform)) { + FAIL("_dyld_process_info_get_platform(%u) should be the same dyld_get_active_platform(%u)", + remotePlatform, localPlatform); + } + + __block bool foundDyld = false; + __block bool foundMain = false; + __block bool foundCF = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + if ( strstr(path, "/dyld") != NULL ) + foundDyld = true; + if ( strstr(path, "/linksWithCF.exe") != NULL ) + foundMain = true; + if ( strstr(path, "/dyld_process_info.exe") != NULL ) + foundMain = true; + if ( strstr(path, "/CoreFoundation.framework/") != NULL ) + foundCF = true; + }); + if (!foundDyld) { FAIL("dyld should always be in the image list"); } + if (!foundMain) { FAIL("The main executable should always be in the image list"); } + if (expectCF && !foundCF) { FAIL("CF should be in the image list"); } + + _dyld_process_info_release(info); +} + + + +#if __x86_64__ +cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ +cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ +cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ +cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + + +static void launchTest(bool launchOtherArch, bool launchSuspended, bool forceIOSMac) +{ + LOG("launchTest %s", launchSuspended ? "suspended" : "unsuspended"); + const char * program = RUN_DIR "/linksWithCF.exe"; + + _process process; + process.set_executable_path(RUN_DIR "/linksWithCF.exe"); + process.set_launch_suspended(launchSuspended); + if (forceIOSMac) { + LOG("Launching native"); + const char* env[] = { "TEST_OUTPUT=None", "DYLD_FORCE_PLATFORM=6", NULL}; + process.set_env(env); + } else { + LOG("Launching iOSMac"); + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + } + pid_t pid = process.launch(); + LOG("launchTest pid: %d", pid); + + task_t task; + kern_return_t kr = task_read_for_pid(mach_task_self(), pid, &task); + LOG("task_read_for_pid(mach_task_self(): return(%u), task(%u)", kr, task); + if (kr != KERN_SUCCESS) { + FAIL("task_read_for_pid() failed"); + } + + // wait until process is up and has suspended itself + if (!launchSuspended) { + dispatch_queue_t queue = dispatch_queue_create("com.apple.test.dyld_process_info", NULL); + // We do this instead of using a dispatch_semaphore to prevent priority inversions + dispatch_block_t oneShotSemaphore = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{}); + dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, + 0, queue); + dispatch_source_set_event_handler(signalSource, ^{ + LOG("Recieved signal"); + oneShotSemaphore(); + dispatch_source_cancel(signalSource); + }); + dispatch_resume(signalSource); + dispatch_block_wait(oneShotSemaphore, DISPATCH_TIME_FOREVER); + } + LOG("task running"); + + inspectProcess(task, launchSuspended, !launchSuspended, forceIOSMac); +} + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + signal(SIGUSR1, SIG_IGN); + launchTest(false, false, false); + launchTest(false, true, false); +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // FIXME: Reenable these ones i386 is turned back on for simulators + //launchTest(true, false, false); + //launchTest(true, true, false); + launchTest(false, false, true); + launchTest(false, true, true); + //FIXME: This functionality is broken, but it is an edge case no one should ever hit + //launchTest(true, true, true); +#endif + dispatch_async( dispatch_get_main_queue(), ^{ + inspectProcess(mach_task_self(), false, false, false); + PASS("Success"); + }); + dispatch_main(); +} + diff --git a/testing/test-cases/dyld_process_info_notify.dtest/main.c b/testing/test-cases/dyld_process_info_notify.dtest/main.c deleted file mode 100644 index eaab560..0000000 --- a/testing/test-cases/dyld_process_info_notify.dtest/main.c +++ /dev/null @@ -1,377 +0,0 @@ - -// BUILD: $CC target.c -o $BUILD_DIR/target.exe -// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe -// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe - -// RUN_TIMEOUT: 2400 -// RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -extern char** environ; - -#if __x86_64__ - cpu_type_t otherArch[] = { CPU_TYPE_I386 }; -#elif __i386__ - cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; -#elif __arm64__ - cpu_type_t otherArch[] = { CPU_TYPE_ARM }; -#elif __arm__ - cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; -#endif - -struct task_and_pid { - pid_t pid; - task_t task; -}; - -static struct task_and_pid launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended) -{ - //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1); - posix_spawnattr_t attr = 0; - if ( posix_spawnattr_init(&attr) != 0 ) { - printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n"); - exit(0); - } - if ( launchSuspended ) { - if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { - printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n"); - exit(0); - } - } - if ( launchOtherArch ) { - size_t copied; - if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { - printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n"); - exit(0); - } - } - - struct task_and_pid child; - const char* argv[] = { testProgPath, arg1, NULL }; - int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ); - if ( psResult != 0 ) { - printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); - exit(0); - } - if (posix_spawnattr_destroy(&attr) != 0) { - printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n"); - exit(0); - } - if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) { - printf("[FAIL] dyld_process_info_notify task_for_pid()\n"); - (void)kill(child.pid, SIGKILL); - exit(0); - } - - return child; -} - -static void killTest(struct task_and_pid tp) { - int r = kill(tp.pid, SIGKILL); - waitpid(tp.pid, &r, 0); -} - -static void wait_util_task_suspended(task_t task) -{ - struct task_basic_info info; - do { - unsigned count = TASK_BASIC_INFO_COUNT; - kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count); - //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count); - sleep(1); - } while ( info.suspend_count == 0 ); -} - - -static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate) -{ - kern_return_t kr; - __block bool sawMainExecutable = false; - __block bool sawlibSystem = false; - __block bool gotTerminationNotice = false; - __block bool gotEarlyNotice = false; - __block bool gotMainNotice = false; - __block bool gotMainNoticeBeforeAllInitialDylibs = false; - __block bool gotFooNoticeBeforeMain = false; - - __block int libFooLoadCount = 0; - __block int libFooUnloadCount = 0; - dispatch_semaphore_t taskDone = dispatch_semaphore_create(0); - - dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - - unsigned count = 0; - dyld_process_info_notify handle; - do { - handle = _dyld_process_info_notify(tp.task, serviceQueue, - ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { - if ( strstr(path, "/target.exe") != NULL ) - sawMainExecutable = true; - if ( strstr(path, "/libSystem") != NULL ) - sawlibSystem = true; - if ( strstr(path, "/libfoo.dylib") != NULL ) { - if ( !gotMainNotice ) - gotFooNoticeBeforeMain = true; - if ( unload ) - ++libFooUnloadCount; - else - ++libFooLoadCount; - if ( disconnectEarly ) { - gotEarlyNotice = true; - dispatch_semaphore_signal(taskDone); - } - } - }, - ^{ - gotTerminationNotice = true; - dispatch_semaphore_signal(taskDone); - }, - &kr); - ++count; - if ( handle == NULL ) - fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count); - } while ( (handle == NULL) && (count < 5) ); - - if ( handle == NULL ) { - return false; - } - - if (!attachLate) { - // If the process starts suspended register for main(), - // otherwise skip since this test is a race between - // process setup and notification registration - _dyld_process_info_notify_main(handle, ^{ - //fprintf(stderr, "target entering main()\n"); - gotMainNotice = true; - if ( !sawMainExecutable || !sawlibSystem ) - gotMainNoticeBeforeAllInitialDylibs = true; - }); - } else { - // if process suspends itself, wait until it has done so - wait_util_task_suspended(tp.task); - } - - // resume from initial suspend - kill(tp.pid, SIGCONT); - - // block waiting for notification that target has exited - bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0); - _dyld_process_info_notify_release(handle); - - - if ( !gotSignal ) { - fprintf(stderr, "did not get exit signal\n"); - return false; - } - - // Do not run any tests associated with startup unless the kernel suspended us - // before main() - if (!attachLate) { - if ( !sawMainExecutable ) { - fprintf(stderr, "did not get load notification of main executable\n"); - return false; - } - - if ( !gotMainNotice ) { - fprintf(stderr, "did not get notification of main()\n"); - return false; - } - - if ( gotMainNoticeBeforeAllInitialDylibs ) { - fprintf(stderr, "notification of main() arrived before all initial dylibs\n"); - return false; - } - - if ( gotFooNoticeBeforeMain ) { - fprintf(stderr, "notification of main() arrived after libfoo load notice\n"); - return false; - } - - if ( !sawlibSystem ) { - fprintf(stderr, "did not get load notification of libSystem\n"); - return false; - } - } - - if ( disconnectEarly ) { - if ( libFooLoadCount != 1 ) { - fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount); - return false; - } - if ( libFooUnloadCount != 0 ) { - fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount); - return false; - } - } - else { - if ( libFooLoadCount != 3 ) { - fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount); - return false; - } - if ( libFooUnloadCount != 3 ) { - fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount); - return false; - } - } - - return true; -} - -static void validateMaxNotifies(struct task_and_pid tp) -{ - 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, - ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { - //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n", - // unload, machHeader, uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - // uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path); - }, - ^{ - //fprintf(stderr, "target exited\n"); - }, - &kr); - if ( handles[i] == NULL ) { - if ( i == 8 ) { - // expected failure, because only 8 simultaneous connections allowed - // release one and try again - _dyld_process_info_notify_release(handles[4]); - handles[4] = NULL; - } - else { - fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i); - killTest(tp); - exit(0); - } - } - } - // release all - for (int i=0; i < 10; ++i) { - if ( handles[i] != NULL ) { - _dyld_process_info_notify_release(handles[i]); - } - } - dispatch_release(serviceQueue); -} - -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[]) -{ - if ( argc < 2 ) { - printf("[FAIL] dyld_process_info_notify missing argument\n"); - exit(0); - } - const char* testProgPath = argv[1]; - - dispatch_async(dispatch_get_main_queue(), ^{ - 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"); - killTest(child); - 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); - exit(0); - } - killTest(child); - printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (same arch)\n"); - -#if 0 - // 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"); - killTest(child); - 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"); - killTest(child); - 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"); - killTest(child); - 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"); - - exit(0); - }); - - dispatch_main(); -} diff --git a/testing/test-cases/dyld_process_info_notify.dtest/main.mm b/testing/test-cases/dyld_process_info_notify.dtest/main.mm new file mode 100644 index 0000000..febb844 --- /dev/null +++ b/testing/test-cases/dyld_process_info_notify.dtest/main.mm @@ -0,0 +1,295 @@ + +// BUILD: $CC target.c -o $BUILD_DIR/target.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib +// BUILD: $CXX main.mm -o $BUILD_DIR/dyld_process_info_notify.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe + +// RUN: $SUDO ./dyld_process_info_notify.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + +//FIXME: We need to add some concurrent access tests +//FIXME: Add cross architecture tests back now that arm64e macOS exists + +extern char** environ; + +// This is a one shot semaphore implementation that is QoS aware with integreated logging +struct OneShotSemaphore { + OneShotSemaphore(const char* N) :_name(strdup(N)), _block(dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{})) {} + bool wait() { + LOG("Waiting for semaphore %s", _name); + dispatch_time_t tenSecondFromNow = dispatch_time(DISPATCH_WALLTIME_NOW, 10 * NSEC_PER_SEC); + if (dispatch_block_wait(_block, tenSecondFromNow) != 0) { + LOG("Timeout for semaphore %s", _name); + return false; + } + return true; + } + void signal() { + LOG("Signalling semaphore %s", _name); + _block(); + } +private: + const char* _name; + dispatch_block_t _block; +}; + +void launchTest(bool launchSuspended, bool disconnectEarly) +{ + + LOG("launchTest (%s)", launchSuspended ? "suspended" : "unsuspened"); + LOG("launchTest (%s)", disconnectEarly ? "disconnect early" : "normal disconnnect"); + dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info", NULL); + dispatch_queue_t signalQueue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info.signals", NULL); + + // We use these blocks as semaphores. We do it this way so have ownership for QOS and so we get logging + __block OneShotSemaphore childReady("childReady"); + __block OneShotSemaphore childExit("childExit"); + __block OneShotSemaphore childDone("childDone"); + __block OneShotSemaphore childExitNotification("childExitNotification"); + + // We control our interactions with the sub ordinate process via signals, but if we send signals before its signal handlers + // are installed it will terminate. We wait for it to SIGUSR1 us to indicate it is ready, so we need to setup a signal handler for + // that. + signal(SIGUSR1, SIG_IGN); + dispatch_source_t usr1SignalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, signalQueue); + dispatch_source_set_event_handler(usr1SignalSource, ^{ + LOG("Got SIGUSR1"); + childReady.signal(); + }); + dispatch_resume(usr1SignalSource); + + signal(SIGUSR2, SIG_IGN); + dispatch_source_t usr2SignalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR2, 0, signalQueue); + dispatch_source_set_event_handler(usr2SignalSource, ^{ + LOG("Got SIGUSR2"); + childDone.signal(); + }); + dispatch_resume(usr2SignalSource); + + pid_t pid; + task_t task; + __block bool sawMainExecutable = false; + __block bool sawlibSystem = false; + __block bool gotMainNotice = false; + __block bool gotMainNoticeBeforeAllInitialDylibs = false; + __block bool gotFooNoticeBeforeMain = false; + __block int libFooLoadCount = 0; + __block int libFooUnloadCount = 0; + __block dyld_process_info_notify handle; + + _process process; + process.set_executable_path(RUN_DIR "/target.exe"); + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + process.set_launch_suspended(launchSuspended); + process.set_exit_handler(^(pid_t pid) { + // This is almost all logging code, the only functional element of it + // is calling the childExit() semaphore + int status = 0; + int dispStatus = 0; + (void)waitpid(pid, &status, 0); + const char* exitType = "UNKNOWN"; + if (WIFEXITED(status)) { + exitType = "exit()"; + dispStatus = WEXITSTATUS(status); + } + if (WIFSIGNALED(status)) { + exitType = "signal"; + dispStatus = WTERMSIG(status); + } + LOG("DIED via %s (pid: %d, status: %d)", exitType, pid, dispStatus); + childExit.signal(); + }); + + // Launch process + pid = process.launch(); + LOG("launchTest pid (%u)", pid); + if ( task_read_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS ) { + FAIL("task_read_for_pid()"); + } + + // Attach notifier + kern_return_t kr; + unsigned count = 0; + do { + handle = _dyld_process_info_notify( task, queue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + LOG("Handler called"); + if ( strstr(path, "/target.exe") != NULL ) + sawMainExecutable = true; + if ( strstr(path, "/libSystem") != NULL ) + sawlibSystem = true; + if ( strstr(path, "/libfoo.dylib") != NULL ) { + if ( !gotMainNotice ) { + gotFooNoticeBeforeMain = true; + } + if ( unload ) { + ++libFooUnloadCount; + } else { + ++libFooLoadCount; + if (disconnectEarly) { + _dyld_process_info_notify_release(handle); + } + } + } + }, + ^{ + LOG("TERMINATED (pid: %d)", pid); + childExitNotification.signal(); + }, + &kr); + ++count; + if ( handle == NULL ) + LOG("_dyld_process_info_notify() returned NULL, result=%d, count=%d", kr, count); + } while ( (handle == NULL) && (count < 5) ); + LOG("launchTest handler registered"); + + if ( handle == NULL ) { + FAIL("Did not not get handle"); + } + + // if suspended attach main notifier and unsuspend + if (launchSuspended) { + // If the process starts suspended register for main(), + // otherwise skip since this test is a race between + // process setup and notification registration + _dyld_process_info_notify_main(handle, ^{ + LOG("target entering main()"); + gotMainNotice = true; + if ( !sawMainExecutable || !sawlibSystem ) + gotMainNoticeBeforeAllInitialDylibs = true; + }); + kill(pid, SIGCONT); + LOG("Sent SIGCONT"); + } + + if (!childReady.wait()) { + FAIL("Timed out waiting for child to signal it is ready"); + } + kill(pid, SIGUSR1); + LOG("Sent SIGUSR1"); + if (!childDone.wait()) { + FAIL("Timed out waiting for child to finish dlopen()/dlclose() operations"); + } + if (launchSuspended) { + if ( !sawMainExecutable ) { + FAIL("Did not get load notification of main executable"); + } + if ( !gotMainNotice ) { + FAIL("Did not get notification of main()"); + } + if ( gotMainNoticeBeforeAllInitialDylibs ) { + FAIL("Notification of main() arrived before all initial dylibs"); + } + if ( gotFooNoticeBeforeMain ) { + FAIL("Notification of main() arrived after libfoo load notice"); + } + if ( !sawlibSystem ) { + FAIL("Did not get load notification of libSystem"); + } + } + kill(pid, SIGTERM); + LOG("Sent SIGTERM"); + if (!childExitNotification.wait()) { + FAIL("Timed out waiting for child exit notification via _dyld_process_info_notify"); + } + if ( disconnectEarly ) { + if ( libFooLoadCount != 1 ) { + FAIL("Got %d load notifications about libFoo instead of 1", libFooLoadCount); + } + if ( libFooUnloadCount != 0 ) { + FAIL("Got %d unload notifications about libFoo instead of 1", libFooUnloadCount); + } + } else { + if ( libFooLoadCount != 3 ) { + FAIL("Got %d load notifications about libFoo instead of 3", libFooLoadCount); + } + if ( libFooUnloadCount != 3 ) { + FAIL("Got %d unload notifications about libFoo instead of 3", libFooUnloadCount); + } + } + if (!childExit.wait()) { + FAIL("Timed out waiting for child cleanup"); + } + + // Tear down + dispatch_source_cancel(usr1SignalSource); + dispatch_source_cancel(usr2SignalSource); + if (!disconnectEarly) { + _dyld_process_info_notify_release(handle); + } +} + +static void testSelfAttach(void) { + __block OneShotSemaphore teardownSempahore("self test teardownSempahore"); + __block bool dylibLoadNotified = false; + kern_return_t kr = KERN_SUCCESS; + dispatch_queue_t queue = dispatch_queue_create("com.apple.dyld.test.dyld_process_info.self-attach", NULL); + dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), queue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + if ( strstr(path, "/libfoo.dylib") != NULL ) { + dylibLoadNotified = true; + } + }, + ^{ teardownSempahore.signal(); }, + &kr); + if ( handle == NULL ) { + LOG("_dyld_process_info_notify() returned NULL, result=%d", kr); + } + void* h = dlopen(RUN_DIR "/libfoo.dylib", 0); + dlclose(h); + if (!dylibLoadNotified) { + FAIL("testSelfAttach"); + } + _dyld_process_info_notify_release(handle); + teardownSempahore.wait(); + + // Get the all image info + task_dyld_info_data_t taskDyldInfo; + mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT; + if (task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount) != KERN_SUCCESS) { + FAIL("Could not find all image info"); + } + dyld_all_image_infos* infos = (dyld_all_image_infos*)taskDyldInfo.all_image_info_addr; + + // Find a slot for the right + uint8_t notifySlot; + for (uint8_t notifySlot = 0; notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++notifySlot) { + if (infos->notifyPorts[notifySlot] != 0) { + FAIL("Port array entry %u not cleaned up, expected 0, got %u", notifySlot, infos->notifyPorts[notifySlot]); + } + } +} + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // test 1) attempt to monitor the monitoring process + testSelfAttach(); + // test 2) launch test program suspended and wait for it to run to completion + launchTest(true, false); + // test 3) launch test program in unsuspended and wait for it to run to completion + launchTest(false, false); + // test 4) launch test program suspended and disconnect from it after the first dlopen() in target.exe + launchTest(true, true); + // test 5) launch test program unsuspended and disconnect from it after the first dlopen() in target.exe + launchTest(false, true); + + PASS("Success"); +} diff --git a/testing/test-cases/dyld_process_info_notify.dtest/target.c b/testing/test-cases/dyld_process_info_notify.dtest/target.c index 55fd668..6797519 100644 --- a/testing/test-cases/dyld_process_info_notify.dtest/target.c +++ b/testing/test-cases/dyld_process_info_notify.dtest/target.c @@ -2,22 +2,49 @@ #include #include #include +#include #include #include #include +#include +#include +// The process starts, then sends its parent a SIGUSR1 to indiicate it is ready +// At that point it waits for SIGUSR1, and when it recieves one it loads and unloads libfoo.dylib 3 times +// The process remains running until it recieves a SIGTERM +// This process will clean itself up in the event its parent dies -int main(int argc, const char* argv[]) -{ - if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) ) - (void)kill(getpid(), SIGSTOP); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + // Setup parent death handler + dispatch_source_t parentDeathSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(), DISPATCH_PROC_EXIT, dispatch_get_main_queue()); + dispatch_source_set_event_handler(parentDeathSource, ^{ + exit(0); + }); + dispatch_resume(parentDeathSource); - for (int i=0; i < 3; ++i) { - void* h = dlopen("./libfoo.dylib", 0); - dlclose(h); - } + // Setup SIGTERM handler + signal(SIGTERM, SIG_IGN); + dispatch_source_t exitSignalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, dispatch_get_main_queue()); + dispatch_source_set_event_handler(exitSignalSource, ^{ + exit(0); + }); + dispatch_resume(exitSignalSource); - return 0; + // Setup SIGUSR1 handler + signal(SIGUSR1, SIG_IGN); + dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGUSR1, 0, dispatch_get_main_queue()); + dispatch_source_set_event_handler(signalSource, ^{ + for (int i=0; i < 3; ++i) { + void* h = dlopen(RUN_DIR "/libfoo.dylib", 0); + dlclose(h); + } + kill(getppid(), SIGUSR2); + }); + dispatch_resume(signalSource); + + // Message our parent to let them know our signal handlers are ready + kill(getppid(), SIGUSR1); + dispatch_main(); } diff --git a/testing/test-cases/dyld_process_info_unload.dtest/main.c b/testing/test-cases/dyld_process_info_unload.dtest/main.c deleted file mode 100644 index 3872e7d..0000000 --- a/testing/test-cases/dyld_process_info_unload.dtest/main.c +++ /dev/null @@ -1,139 +0,0 @@ - -// BUILD: $CC target.c -o $BUILD_DIR/target.exe -// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib -// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_unload.exe -// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe - -// RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -extern char** environ; - -#if __x86_64__ - cpu_type_t otherArch[] = { CPU_TYPE_I386 }; -#elif __i386__ - cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; -#elif __arm64__ - cpu_type_t otherArch[] = { CPU_TYPE_ARM }; -#elif __arm__ - cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; -#endif - -struct task_and_pid { - pid_t pid; - task_t task; -}; - -static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended) -{ - posix_spawnattr_t attr = 0; - if ( posix_spawnattr_init(&attr) != 0 ) { - printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n"); - exit(0); - } - if ( launchSuspended ) { - if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { - printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n"); - exit(0); - } - } - if ( launchOtherArch ) { - size_t copied; - if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { - printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n"); - exit(0); - } - } - - struct task_and_pid child; - const char* argv[] = { testProgPath, NULL }; - int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ); - if ( psResult != 0 ) { - printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); - exit(0); - } - if (posix_spawnattr_destroy(&attr) != 0) { - printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n"); - exit(0); - } - - if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) { - printf("[FAIL] dyld_process_info_unload task_for_pid()\n"); - kill(child.pid, SIGKILL); - exit(0); - } - - // wait until process is up and has suspended itself - struct task_basic_info info; - do { - unsigned count = TASK_BASIC_INFO_COUNT; - kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count); - sleep(1); - } while ( info.suspend_count == 0 ); - - return child; -} - -static void killTest(struct task_and_pid tp) { - int r = kill(tp.pid, SIGKILL); - waitpid(tp.pid, &r, 0); -} - -static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended) -{ - int failCount = 0; - for (int i=0; i < 100; ++i ) { - kern_return_t result; - dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result); - //fprintf(stderr, "info=%p, result=%08X\n", info, result); - if ( i == 0 ) - (void)kill(tp.pid, SIGCONT); - if ( info == NULL ) { - failCount++; - //fprintf(stderr, "info=%p, result=%08X\n", info, result); - } - else { - usleep(100); - _dyld_process_info_release(info); - } - } - // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images. - // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast. - // The important thing is to not crash. Getting NULL back is ok. - return true; -} - - -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] dyld_process_info_unload\n"); - - if ( argc < 2 ) { - printf("[FAIL] dyld_process_info_unload missing argument\n"); - exit(0); - } - const char* testProgPath = argv[1]; - struct task_and_pid child; - - // launch test program suspended - child = launchTest(testProgPath, false, true); - if ( ! alwaysGetImages(child, true) ) { - killTest(child); - exit(0); - } - killTest(child); - - printf("[PASS] dyld_process_info_unload\n"); - return 0; -} diff --git a/testing/test-cases/dyld_process_info_unload.dtest/main.cpp b/testing/test-cases/dyld_process_info_unload.dtest/main.cpp new file mode 100644 index 0000000..8302481 --- /dev/null +++ b/testing/test-cases/dyld_process_info_unload.dtest/main.cpp @@ -0,0 +1,60 @@ + +// BUILD: $CC target.c -o $BUILD_DIR/target.exe +// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CXX main.cpp -o $BUILD_DIR/dyld_process_info_unload.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe + +// RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + _process process; + process.set_executable_path(RUN_DIR "/target.exe"); + process.set_launch_suspended(true); + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + pid_t pid = process.launch(); + task_t task; + if (task_read_for_pid(mach_task_self(), pid, &task) != KERN_SUCCESS) { + FAIL("task_read_for_pid() failed"); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + int failCount = 0; + for (int i=0; i < 100; ++i ) { + kern_return_t result; + dyld_process_info info = _dyld_process_info_create(task, 0, &result); + LOG("info=%p, result=%08X", info, result); + if ( i == 0 ) + (void)kill(pid, SIGCONT); + if ( info == NULL ) { + //FIXME: Compact info will never fail, make this a FAIL() + failCount++; + // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images. + // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast. + // The important thing is to not crash. Getting NULL back is ok. + LOG("info=%p, result=%08X", info, result); + } + else { + usleep(100); + _dyld_process_info_release(info); + } + } + PASS("Success"); + }); + + dispatch_main(); +} diff --git a/testing/test-cases/dyld_process_info_unload.dtest/target.c b/testing/test-cases/dyld_process_info_unload.dtest/target.c index be923e7..b70c268 100644 --- a/testing/test-cases/dyld_process_info_unload.dtest/target.c +++ b/testing/test-cases/dyld_process_info_unload.dtest/target.c @@ -3,22 +3,31 @@ #include #include #include -#include +#include +#include "test_support.h" +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + signal(SIGUSR1, SIG_IGN); + dispatch_source_t signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, getppid(), + DISPATCH_PROC_EXIT, dispatch_get_main_queue()); + dispatch_source_set_event_handler(signalSource, ^{ + exit(0); + }); + dispatch_resume(signalSource); -int main(int argc, const char* argv[]) -{ - //fprintf(stderr, "target starting\n"); - usleep(1000); - // load and unload in a loop - for (int i=1; i < 10000; ++i) { - void* h = dlopen("./libfoo.dylib", 0); - usleep(100000/(i*100)); - dlclose(h); - } - //fprintf(stderr, "target done\n"); + dispatch_async(dispatch_get_main_queue(), ^{ + LOG("target starting"); + usleep(1000); + // load and unload in a loop + for (int i=1; i < 10000; ++i) { + void* h = dlopen("./libfoo.dylib", 0); + usleep(100000/(i*100)); + dlclose(h); + } + LOG("target done"); + }); - return 0; + dispatch_main(); } diff --git a/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest-strings b/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest-strings new file mode 100644 index 0000000..e69de29 diff --git a/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c b/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c index 827e893..06abd8a 100644 --- a/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c +++ b/testing/test-cases/dyld_shared_cache_some_image_overridden.dtest/main.c @@ -1,6 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/override -// BUILD: mkdir -p $BUILD_DIR/re-export-override // BUILD: $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 // BUILD: $CC main.c -o $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe -lz // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dyld_shared_cache_some_image_overridden.exe @@ -21,35 +19,31 @@ #include #include +#include "test_support.h" + // The test here is to override libz.1.dylib which is in the dyld cache with our own implementation. // We then ensure that dyld_shared_cache_some_image_overridden returns the correct value to match whether we took a root extern const char* zlibVersion(); -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // If we aren't using a shared cache, eg, have DYLD_SHARED_REGION=avoid, then just assume we work uuid_t currentCacheUUID; if ( !_dyld_get_shared_cache_uuid(currentCacheUUID) ) { - printf("[BEGIN] dyld_shared_cache_some_image_overridden\n"); if (dyld_shared_cache_some_image_overridden()) - printf("[FAIL] dyld_shared_cache_some_image_overridden\n"); + FAIL("Overriden but no shared cache "); else - printf("[PASS] dyld_shared_cache_some_image_overridden\n"); - return 0; + PASS("No shared cache"); } #if NO_LZ - // This run doesn't link lz so instead dlopen's it + // This run doesn't link lz so instead dlopen's it bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL); - printf("[BEGIN] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); - void* handle = dlopen("/usr/lib/libz.1.dylib", RTLD_NOLOAD); if ( handle != NULL ) { // Uh oh. Someone else has started linking libz so we can't use it as our root any more - printf("[FAIL] dyld_shared_cache_some_image_overridden, libz is hard linked now. Update test to use a new dylib\n"); - return 0; + FAIL("libz is hard linked now. Update test to use a new dylib"); } bool launchedWithOverriddenBinary = dyld_shared_cache_some_image_overridden(); @@ -57,76 +51,62 @@ int main() // Now dlopen libz handle = dlopen("/usr/lib/libz.1.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dyld_shared_cache_some_image_overridden: /usr/lib/libz.1.dylib could not be loaded, %s\n", dlerror()); - return 0; + FAIL("/usr/lib/libz.1.dylib could not be loaded, %s", dlerror()); } // verify handle has the version symbol __typeof(&zlibVersion) versionSymbol = (__typeof(&zlibVersion))dlsym(handle, "zlibVersion"); if ( versionSymbol == NULL ) { - printf("[FAIL] dyld_shared_cache_some_image_overridden: zlibVersion was not found\n"); - return 0; + FAIL("zlibVersion was not found"); } bool usingMyDylib = (strcmp(versionSymbol(), "my") == 0); - if ( usingMyDylib != expectMyDylib ) { - // Not using the right dylib - printf("[FAIL] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); - return 0; + if ( usingMyDylib != expectMyDylib ) { + // Not using the right dylib + FAIL("%s", expectMyDylib ? "my" : "os"); } // Using the right dylib, so now see if we returned the correct value for dyld_shared_cache_some_image_overridden if (usingMyDylib) { - if (!dyld_shared_cache_some_image_overridden()) { - printf("[FAIL] dyld_shared_cache_some_image_overridden, my dylib but not some dylib overridden\n"); - return 0; - } + if (!dyld_shared_cache_some_image_overridden()) { + FAIL("My dylib but not some dylib overridden"); + } } else if (!launchedWithOverriddenBinary) { // We didn't have a root when we launched, so now we can make sure we do have a root after the dlopen // Assume we aren't testing against a root of libz in the system itself... if (dyld_shared_cache_some_image_overridden()) { - printf("[FAIL] dyld_shared_cache_some_image_overridden, system dylib was overridden\n"); - return 0; + FAIL("System dylib was overridden"); } } else { - // We can't actually be sure of the result here. There may be other roots on the system so call the API to - // make sure it doesn't crash, but don't actually check it. + // We can't actually be sure of the result here. There may be other roots on the system so call the API to + // make sure it doesn't crash, but don't actually check it. dyld_shared_cache_some_image_overridden(); } - - - printf("[PASS] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); - #else // This run links libz directly bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL); - printf("[BEGIN] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); - bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0); - if ( usingMyDylib != expectMyDylib ) { - // Not using the right dylib - printf("[FAIL] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); - return 0; + if ( usingMyDylib != expectMyDylib ) { + // Not using the right dylib + FAIL("%s", expectMyDylib ? "my" : "os"); } // Using the right dylib, so now see if we returned the correct value for dyld_shared_cache_some_image_overridden if (usingMyDylib) { - if (!dyld_shared_cache_some_image_overridden()) { - printf("[FAIL] dyld_shared_cache_some_image_overridden, my dylib but not some dylib overridden\n"); - return 0; - } + if (!dyld_shared_cache_some_image_overridden()) { + FAIL("My dylib but not some dylib overridden"); + } } else { - // We can't actually be sure of the result here. There may be other roots on the system so call the API to - // make sure it doesn't crash, but don't actually check it. + // We can't actually be sure of the result here. There may be other roots on the system so call the API to + // make sure it doesn't crash, but don't actually check it. dyld_shared_cache_some_image_overridden(); } - - printf("[PASS] dyld_shared_cache_some_image_overridden, %s\n", expectMyDylib ? "my" : "os"); #endif + PASS("%s", expectMyDylib ? "my" : "os"); - return 0; + return 0; } diff --git a/testing/test-cases/dyld_usage_json.dtest/foo.c b/testing/test-cases/dyld_usage_json.dtest/foo.c new file mode 100644 index 0000000..8eb2dc5 --- /dev/null +++ b/testing/test-cases/dyld_usage_json.dtest/foo.c @@ -0,0 +1,4 @@ +int foo() +{ + return 0; +} diff --git a/testing/test-cases/dyld_usage_json.dtest/main.mm b/testing/test-cases/dyld_usage_json.dtest/main.mm new file mode 100644 index 0000000..8953dc3 --- /dev/null +++ b/testing/test-cases/dyld_usage_json.dtest/main.mm @@ -0,0 +1,235 @@ +// BUILD(macos): $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/lifoo.dylib +// BUILD(macos): $CC target.c -o $BUILD_DIR/dyld_usage_target.exe -DRUN_DIR="$RUN_DIR" +// BUILD(macos): $CXX main.mm -o $BUILD_DIR/dyld_usage_json.exe -DRUN_DIR="$RUN_DIR" -std=c++14 -framework Foundation + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: $SUDO ./dyld_usage_json.exe + +#import + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "test_support.h" + +enum class NodeValueType { + Default, + String, + RawValue, +}; + +struct Node +{ + NodeValueType type = NodeValueType::Default; + std::string value; + std::map map; + std::vector array; + + inline Node() + : type(NodeValueType::Default), value(), map(), array() { } + + inline Node(std::string string) + : type(NodeValueType::String), value(string), map(), array() { } + + inline Node(const char *string) : Node(std::string(string)) { } + + inline Node(bool b) + : type(NodeValueType::RawValue), value(b ? "true" : "false") + , map(), array() { } + + inline Node(int64_t i64) + : type(NodeValueType::RawValue), value(), map(), array() + { + std::ostringstream os; + os << i64; + value = os.str(); + } + + inline Node(uint64_t u64) + : type(NodeValueType::RawValue), value(), map(), array() + { + std::ostringstream os; + os << u64; + value = os.str(); + } +}; + +static Node parseNode(id jsonObject) { + __block Node node; + + // NSDictionary -> map + if ([jsonObject isKindOfClass:[NSDictionary class]]) { + NSDictionary* dict = (NSDictionary*)jsonObject; + + [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) { + if (![key isKindOfClass:[NSString class]]) { + fprintf(stderr, "JSON map key is not of string type\n"); + *stop = true; + return; + } + Node childNode = parseNode(value); + + node.map[[key UTF8String]] = childNode; + }]; + + return node; + } + + // NSArray -> array + if ([jsonObject isKindOfClass:[NSArray class]]) { + NSArray* array = (NSArray*)jsonObject; + + [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { + Node childNode = parseNode(obj); + node.array.push_back(childNode); + }]; + + return node; + } + + // NSString -> value + if ([jsonObject isKindOfClass:[NSString class]]) { + node.value = [(NSString*)jsonObject UTF8String]; + return node; + } + + fprintf(stderr, "Unknown json deserialized type\n"); + return Node(); +} + +Node readJSON(const void * contents, size_t length) { + NSData* data = [NSData dataWithBytes:contents length:length]; + NSError* error = nil; + id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; + if (!jsonObject) { + fprintf(stderr, "Could not deserialize json because '%s'",[[error localizedFailureReason] UTF8String]); + return Node(); + } + + return parseNode(jsonObject); +} + +char* mergeJsonRoots(char* jsonBuffer, size_t size) +{ + char *mergedJson = (char*)malloc((size + 2) * sizeof(char)); + mergedJson[0] = '['; + mergedJson[size+1] = '\0'; + mergedJson[size+1] = ']'; + for (size_t i = 0; i < size; i++) { + mergedJson[i+1] = jsonBuffer[i]; + if (jsonBuffer[i] == '\n') { + if ( i > 0 && i < size - 1 ) { + if (jsonBuffer[i-1] == '}' && jsonBuffer[i+1] == '{') + mergedJson[i+1] = ','; + } + } + } + return mergedJson; +} + +void validateJson(Node json, pid_t pid) +{ + size_t expectedSize = 4; + if (json.array.size() != expectedSize) + FAIL("dyld_usage reported number of events is incorrect. Reported %lu instead of %lu", json.array.size(), expectedSize); + + std::string handle = json.array[1].map["event"].map["result"].value; + + for (size_t i = 0; i < json.array.size(); i++) { + + if ( json.array[i].map["command"].value.compare("dyld_usage_target.exe") != 0 ) + FAIL("Incorrect command name for event at index %lu", i); + + int jpid = std::stoi(json.array[i].map["pid"].value); + if ( jpid != pid) + FAIL("Incorrect pid for event at index %lu. Reported %d intead of %d (%s)", i, jpid, pid, json.array[i].map["pid"].value.c_str()); + + if (i == 0) { + if ( json.array[i].map["event"].map["type"].value.compare("app_launch") != 0 ) + FAIL("dyld_usage did not report app launch event"); + } + + if (i == 1) { + if ( json.array[1].map["event"].map["type"].value.compare("dlopen") != 0 ) + FAIL("dyld_usage did not report dlopen event"); + if ( json.array[1].map["event"].map["path"].value.compare(RUN_DIR "/libfoo.dylib") != 0 ) + FAIL("Incorrect dlopen library path"); + } + + if (i == 2) { + if ( json.array[i].map["event"].map["type"].value.compare("dlsym") != 0 ) + FAIL("dyld_usage did not report dlsym event"); + if ( json.array[i].map["event"].map["symbol"].value.compare("foo") != 0 ) + FAIL("incorrect dlsym symbol reported"); + if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 ) + FAIL("dlsym handle does not match dlopen result"); + } + + if (i == 3) { + if ( json.array[i].map["event"].map["type"].value.compare("dlclose") != 0 ) + FAIL("dyld_usage did not report dlclose event"); + if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 ) + FAIL("dlclose handle does not match dlopen result"); + } + } +} + +int main(int argc, const char* argv[], char *env[]) +{ + _process dyldUsage; + dyldUsage.set_executable_path("/usr/local/bin/dyld_usage"); + const char* args[] = { "-j", "dyld_usage_target.exe", NULL }; + dyldUsage.set_args(args); + __block dispatch_data_t output = NULL; + dyldUsage.set_stdout_handler(^(int fd) { + ssize_t size = 0; + do { + char buffer[16384] = {0}; + size = read(fd, buffer, 16384); + if ( size == -1 ) + break; + dispatch_data_t data = dispatch_data_create(buffer, size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + output = output ? dispatch_data_create_concat(output, data) : data; + } while ( size > 0 ); + }); + + pid_t pid = dyldUsage.launch(); + usleep(2000000); + + // Launch target + _process target; + target.set_executable_path(RUN_DIR "/dyld_usage_target.exe"); + pid_t tpid = target.launch(); + + usleep(2000000); + // Kill dyld_usage + kill(pid, SIGTERM); + + + int status; + if (waitpid(pid, &status, 0) == -1) + FAIL("waitpid failed"); + if ( !output ) + FAIL("No dyld_usage output"); + + const void* buffer; + size_t size; + (void)dispatch_data_create_map(output, &buffer, &size); + char* jsonBuffer = mergeJsonRoots((char*)buffer, size); + size += 2; + Node node = readJSON(jsonBuffer, size); + free(jsonBuffer); + validateJson(node, tpid); + + PASS("Success"); + return 0; +} diff --git a/testing/test-cases/dyld_usage_json.dtest/target.c b/testing/test-cases/dyld_usage_json.dtest/target.c new file mode 100644 index 0000000..d6ad4f2 --- /dev/null +++ b/testing/test-cases/dyld_usage_json.dtest/target.c @@ -0,0 +1,11 @@ +#include +#include + +#include "test_support.h" +int main(int argc, const char* argv[]) +{ + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW); + void *foo = dlsym(handle, "foo"); + dlclose(handle); + return 0; +} diff --git a/testing/test-cases/dyld_version_spis.dtest/main.c b/testing/test-cases/dyld_version_spis.dtest/main.c index f5c77ce..afd3b58 100644 --- a/testing/test-cases/dyld_version_spis.dtest/main.c +++ b/testing/test-cases/dyld_version_spis.dtest/main.c @@ -7,84 +7,90 @@ #include #include +#include "test_support.h" + extern struct mach_header __dso_handle; -int main() -{ - printf("[BEGIN] dyld_version_spi\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { 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 }; + // We choose high platform value that is unlikely to ever be used, and a non-zero version number + // If the platform number we choose here is ever used it will fail on that platform and this test will need to be fixed. + dyld_build_version_t bogusPlatformVersion = { .platform = 0xffff0000, .version = 1 }; #if TARGET_OS_OSX if ( base != PLATFORM_MACOS ) { - printf("[FAIL] base.platform %u incorrect for macOS\n", base); - return 0; + FAIL("base.platform %u incorrect for macOS", base); } #elif TARGET_OS_IOS if ( base != PLATFORM_IOS ) { - printf("[FAIL] base.platform %u incorrect for iOS\n", base); - return 0; + FAIL("base.platform %u incorrect for iOS", base); } #elif TARGET_OS_TV if ( base != PLATFORM_TVOS ) { - printf("[FAIL] base.platform %u incorrect for tvOS\n", base); - return 0; + FAIL("base.platform %u incorrect for tvOS", base); } #elif TARGET_OS_BRIDGE if ( base != PLATFORM_BRIDGEOS ) { - printf("[FAIL] base.platform %u incorrect for wacthOS\n", base); - return 0; + FAIL("base.platform %u incorrect for wacthOS", base); } #elif TARGET_OS_WATCH if ( base != PLATFORM_WATCHOS ) { - printf("[FAIL] base.platform %u incorrect for bridgeOS\n", base); - return 0; + FAIL("base.platform %u incorrect for bridgeOn", base); } #else - printf("[FAIL] Running on unknown platform\n"); - return 0; + FAIL("Running on unknown platform"); #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; + FAIL("active platform %u should be a simulator", active); } #else if (dyld_is_simulator_platform(active) == true) { - printf("[FAIL] active platform %u should not be a simulator\n", active); - return 0; + FAIL("active platform %u should not be a simulator", active); } #endif if (dyld_is_simulator_platform(base) == true) { - printf("[FAIL] base platform %u should not be a simulator\n", base); - return 0; + FAIL("base platform %u should not be a simulator", base); } if (!dyld_sdk_at_least(&__dso_handle, absoluteMin)) { - printf("[FAIL] executable sdk version should not < 1.0.0\n"); - return 0; + FAIL("executable sdk version should not < 1.0.0"); } if (dyld_sdk_at_least(&__dso_handle, absoluteMax)) { - printf("[FAIL] executable sdk version should not > 65536.0.0\n"); - return 0; + FAIL("executable sdk version should not > 65536.0.0"); } if (!dyld_minos_at_least(&__dso_handle, absoluteMin)) { - printf("[FAIL] executable min version should not < 1.0.0\n"); - return 0; + FAIL("executable min version should not < 1.0.0"); } if (dyld_minos_at_least(&__dso_handle, absoluteMax)) { - printf("[FAIL] executable min version should not > 65536.0.0\n"); - return 0; + FAIL("executable min version should not > 65536.0.0"); } - printf("[PASS] dyld_version_spi\n"); - return 0; + if (dyld_minos_at_least(&__dso_handle, bogusPlatformVersion)) { + FAIL("dyld_minos_at_least should be false for bogus platform"); + } + + if (dyld_program_minos_at_least(bogusPlatformVersion)) { + FAIL("dyld_program_minos_at_least should be false for bogus platform"); + } + + if (dyld_sdk_at_least(&__dso_handle, bogusPlatformVersion)) { + FAIL("dyld_sdk_at_least should be false for bogus platform"); + } + + if (dyld_program_sdk_at_least(bogusPlatformVersion)) { + FAIL("dyld_program_sdk_at_least should be false for bogus platform"); + } + + + PASS("Success"); } diff --git a/testing/test-cases/dyld_version_spis.dtest/version_set.c b/testing/test-cases/dyld_version_spis.dtest/version_set.c new file mode 100644 index 0000000..786dad4 --- /dev/null +++ b/testing/test-cases/dyld_version_spis.dtest/version_set.c @@ -0,0 +1,207 @@ +// FIXME: -Wl,-platform_version triggers linker warnings, we need to find a way to stop clang from emitting -platform_version +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,10.14,10.14 -o $BUILD_DIR/version_set_10.14.exe +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,10.14.9,10.14.9 -o $BUILD_DIR/version_set_10.14.9.exe +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,10.15,10.15 -o $BUILD_DIR/version_set_10.15.exe +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,10.15.1,10.15.1 -o $BUILD_DIR/version_set_10.15.1.exe +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,10.16,10.16 -o $BUILD_DIR/version_set_10.16.exe +// BUILD(macos): $CC version_set.c -Wl,-platform_version,macos,11.0,11.0 -o $BUILD_DIR/version_set_11.exe +// RUN(macos): ./version_set_10.14.exe +// RUN(macos): ./version_set_10.14.9.exe +// RUN(macos): ./version_set_10.15.exe +// RUN(macos): ./version_set_10.15.1.exe +// RUN(macos): ./version_set_10.16.exe +// RUN(macos): ./version_set_11.exe + +// BUILD(tvos): $CC version_set.c -Wl,-platform_version,tvos,12.0,12.0 -o $BUILD_DIR/version_set_12.exe +// BUILD(tvos): $CC version_set.c -Wl,-platform_version,tvos,12.9,12.9 -o $BUILD_DIR/version_set_12.9.exe +// BUILD(tvos): $CC version_set.c -Wl,-platform_version,tvos,13.0,13.0 -o $BUILD_DIR/version_set_13.exe +// BUILD(tvos): $CC version_set.c -Wl,-platform_version,tvos,13.1,13.1 -o $BUILD_DIR/version_set_13.1.exe +// BUILD(tvos): $CC version_set.c -Wl,-platform_version,tvos,14.0,14.0 -o $BUILD_DIR/version_set_14.exe +// RUN(tvos): ./version_set_12.exe +// RUN(tvos): ./version_set_12.9.exe +// RUN(tvos): ./version_set_13.exe +// RUN(tvos): ./version_set_13.1.exe +// RUN(tvos): ./version_set_14.exe + +// BUILD(ios): $CC version_set.c -Wl,-platform_version,ios,12.0,12.0 -o $BUILD_DIR/version_set_12.exe +// BUILD(ios): $CC version_set.c -Wl,-platform_version,ios,12.9,12.9 -o $BUILD_DIR/version_set_12.9.exe +// BUILD(ios): $CC version_set.c -Wl,-platform_version,ios,13.0,13.0 -o $BUILD_DIR/version_set_13.exe +// BUILD(ios): $CC version_set.c -Wl,-platform_version,ios,13.1,13.1 -o $BUILD_DIR/version_set_13.1.exe +// BUILD(ios): $CC version_set.c -Wl,-platform_version,ios,14.0,14.0 -o $BUILD_DIR/version_set_14.exe +// RUN(ios): ./version_set_12.exe +// RUN(ios): ./version_set_12.9.exe +// RUN(ios): ./version_set_13.exe +// RUN(ios): ./version_set_13.1.exe +// RUN(ios): ./version_set_14.exe + +// BUILD(watchos): $CC version_set.c -Wl,-platform_version,watchos,5.0,5.0 -o $BUILD_DIR/version_set_5.exe +// BUILD(watchos): $CC version_set.c -Wl,-platform_version,watchos,5.9,5.9 -o $BUILD_DIR/version_set_5.9.exe +// BUILD(watchos): $CC version_set.c -Wl,-platform_version,watchos,6.0,6.0 -o $BUILD_DIR/version_set_6.exe +// BUILD(watchos): $CC version_set.c -Wl,-platform_version,watchos,6.1,6.1 -o $BUILD_DIR/version_set_6.1.exe +// BUILD(watchos): $CC version_set.c -Wl,-platform_version,watchos,7.0,7.0 -o $BUILD_DIR/version_set_7.exe +// RUN(watchos): ./version_set_5.exe +// RUN(watchos): ./version_set_5.9.exe +// RUN(watchos): ./version_set_6.exe +// RUN(watchos): ./version_set_6.1.exe +// RUN(watchos): ./version_set_7.exe + +// BUILD(bridgeos): + +#include +#include +#include +#include + +#include "test_support.h" + +extern struct mach_header __dso_handle; + +#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101400) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "macOS 10.14" +#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101409) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "macOS 10.14.9" +#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101500) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "macOS 10.15" +#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101501) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "macOS 10.15.1" +#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101600) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 true +#define VERSION_NAME "macOS 10.16" +#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 110000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 true +#define VERSION_NAME "macOS 11" +#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 120000) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "tvOS 12" +#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 120900) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "tvOS 12.9" +#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 130000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "tvOS 13" +#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 130100) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "tvOS 13.1" +#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 140000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 true +#define VERSION_NAME "tvOS 14" +#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 120000) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "iOS 12" +#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 120900) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "iOS 12.9" +#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 130000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "iOS 13" +#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 130100) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "iOS 13.1" +#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 140000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 true +#define VERSION_NAME "iOS 14" +#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 50000) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "watchOS 5" +#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 50900) +#define FALL_2018 true +#define FALL_2019 false +#define FALL_2020 false +#define VERSION_NAME "watchOS 5.9" +#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 60000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "watchOS 6" +#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 60100) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 false +#define VERSION_NAME "watchOS 6.1" +#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 70000) +#define FALL_2018 true +#define FALL_2019 true +#define FALL_2020 true +#define VERSION_NAME "watchOS 7" +#else +#error Unknown version +#endif + +void testVersionChecks(const char* versionName, dyld_build_version_t testVersion, bool expected) { + if (expected != dyld_minos_at_least(&__dso_handle, testVersion)) { + FAIL(VERSION_NAME "should be %s than %s for dyld_minos_at_least()", versionName, expected ? "newer" : "older"); + } + if (expected != dyld_sdk_at_least(&__dso_handle, testVersion)) { + FAIL(VERSION_NAME "should be %s than %s for dyld_sdk_at_least()", versionName, expected ? "newer" : "older"); + } + if (expected != dyld_program_minos_at_least(testVersion)) { + FAIL(VERSION_NAME "should be %s than %s for dyld_program_minos_at_least()", versionName, expected ? "newer" : "older"); + } + if (expected != dyld_program_sdk_at_least(testVersion)) { + FAIL(VERSION_NAME "should be %s than %s for dyld_program_sdk_at_least()", versionName, expected ? "newer" : "older"); + } +} + +int main(void) { + testVersionChecks("dyld_fall_2018_os_versions", dyld_fall_2018_os_versions, FALL_2018); + testVersionChecks("dyld_fall_2019_os_versions", dyld_fall_2019_os_versions, FALL_2019); + testVersionChecks("dyld_fall_2020_os_versions", dyld_fall_2020_os_versions, FALL_2020); + +#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) + testVersionChecks("dyld_platform_version_macOS_10_14", dyld_platform_version_macOS_10_14, FALL_2018); + testVersionChecks("dyld_platform_version_macOS_10_15", dyld_platform_version_macOS_10_15, FALL_2019); + testVersionChecks("dyld_platform_version_macOS_10_16", dyld_platform_version_macOS_10_16, FALL_2020); +#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) + testVersionChecks("dyld_platform_version_tvOS_12_0", dyld_platform_version_tvOS_12_0, FALL_2018); + testVersionChecks("dyld_platform_version_tvOS_13_0", dyld_platform_version_tvOS_13_0, FALL_2019); + testVersionChecks("dyld_platform_version_tvOS_14_0", dyld_platform_version_tvOS_14_0, FALL_2020); +#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) + testVersionChecks("dyld_platform_version_iOS_12_0", dyld_platform_version_iOS_12_0, FALL_2018); + testVersionChecks("dyld_platform_version_iOS_13_0", dyld_platform_version_iOS_13_0, FALL_2019); + testVersionChecks("dyld_platform_version_iOS_14_0", dyld_platform_version_iOS_14_0, FALL_2020); +#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) + testVersionChecks("dyld_platform_version_watchOS_5_0", dyld_platform_version_watchOS_5_0, FALL_2018); + testVersionChecks("dyld_platform_version_watchOS_6_0", dyld_platform_version_watchOS_6_0, FALL_2019); + testVersionChecks("dyld_platform_version_watchOS_7_0", dyld_platform_version_watchOS_7_0, FALL_2020); +#endif + + PASS("Success"); +}; diff --git a/testing/test-cases/dylib-re-export-old-format.dtest/main.c b/testing/test-cases/dylib-re-export-old-format.dtest/main.c index ea4d7f0..edef925 100644 --- a/testing/test-cases/dylib-re-export-old-format.dtest/main.c +++ b/testing/test-cases/dylib-re-export-old-format.dtest/main.c @@ -1,26 +1,27 @@ -// BUILD_ONLY: MacOSX -// BUILD_MIN_OS: 10.5 -// 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 +// BUILD(macos|x86_64): $CC bar.c -mmacosx-version-min=10.5 -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD(macos|x86_64): $CC foo.c -mmacosx-version-min=10.5 -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD(macos|x86_64): $CC main.c -mmacosx-version-min=10.5 -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR + +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./dylib-re-export.exe #include +#include "test_support.h" + extern int bar(); -int main() -{ - printf("[BEGIN] dylib-re-export-old-format\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + PASS("Success"); +#if 0 if ( bar() == 42 ) - printf("[PASS] dylib-re-export-old-format\n"); + PASS("Success"); else - printf("[FAIL] dylib-re-export-old-format, wrong value\n"); - - return 0; + FAIL("Wrong value"); +#endif } diff --git a/testing/test-cases/dylib-re-export.dtest/main.c b/testing/test-cases/dylib-re-export.dtest/main.c index 879b0b6..3484893 100644 --- a/testing/test-cases/dylib-re-export.dtest/main.c +++ b/testing/test-cases/dylib-re-export.dtest/main.c @@ -9,16 +9,16 @@ #include +#include "test_support.h" + extern int bar(); -int main() -{ - printf("[BEGIN] dylib-re-export\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( bar() == 42 ) - printf("[PASS] dylib-re-export\n"); + PASS("Success"); else - printf("[FAIL] dylib-re-export, wrong value\n"); + FAIL("Wrong value"); return 0; } diff --git a/testing/test-cases/dylib-static-link.dtest/main.c b/testing/test-cases/dylib-static-link.dtest/main.c new file mode 100644 index 0000000..6dd84b6 --- /dev/null +++ b/testing/test-cases/dylib-static-link.dtest/main.c @@ -0,0 +1,23 @@ + + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-link.exe + +// RUN: ./dylib-static-link.exe + + +#include + +#include "test_support.h" + +extern int foo; + + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( foo == 42 ) + PASS("Success"); + else + FAIL("Wrong value"); +} + + diff --git a/testing/test-cases/dylib-static-link.dtest/missing.c b/testing/test-cases/dylib-static-link.dtest/missing.c deleted file mode 100644 index 6a4a62d..0000000 --- a/testing/test-cases/dylib-static-link.dtest/missing.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include - -int main() -{ - printf("[BEGIN] dylib-static-link missing\n"); - printf("[FAIL] dylib-static-link missing, program should not have launched\n"); - - return 0; -} - - diff --git a/testing/test-cases/dylib-static-link.dtest/present.c b/testing/test-cases/dylib-static-link.dtest/present.c deleted file mode 100644 index 2eb3393..0000000 --- a/testing/test-cases/dylib-static-link.dtest/present.c +++ /dev/null @@ -1,28 +0,0 @@ - - -// 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 $RUN_DIR/libfoomissing.dylib -// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe - -// RUN: ./dylib-static-present.exe -// RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe - - -#include - -extern int foo; - - -int main() -{ - printf("[BEGIN] dylib-static-link present\n"); - if ( foo == 42 ) - printf("[PASS] dylib-static-link present\n"); - else - printf("[FAIL] dylib-static-link present, wrong value\n"); - - return 0; -} - - diff --git a/testing/test-cases/dylib-static-weak-link.dtest/missing.c b/testing/test-cases/dylib-static-weak-link.dtest/missing.c index c633047..af32304 100644 --- a/testing/test-cases/dylib-static-weak-link.dtest/missing.c +++ b/testing/test-cases/dylib-static-weak-link.dtest/missing.c @@ -1,19 +1,14 @@ -#include -#include +#include "test_support.h" extern int foo __attribute__((weak_import)); -int main() -{ - printf("[BEGIN] dylib-static-weak-link missing\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // dylib won't be found at runtime, so &foo should be NULL if ( &foo == NULL ) - printf("[PASS] dylib-static-weak-link missing\n"); + PASS("Success"); else - printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n"); - - return 0; + FAIL("&foo != NULL"); } diff --git a/testing/test-cases/dylib-static-weak-link.dtest/present.c b/testing/test-cases/dylib-static-weak-link.dtest/present.c index f51e384..6f1aa5b 100644 --- a/testing/test-cases/dylib-static-weak-link.dtest/present.c +++ b/testing/test-cases/dylib-static-weak-link.dtest/present.c @@ -1,7 +1,9 @@ // 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 $RUN_DIR/libfoomissing.dylib -// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib +// BUILD: $CC missing.c $BUILD_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe + +// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib // RUN: ./dylib-static-weak-present.exe // RUN: ./dylib-static-weak-missing.exe @@ -10,24 +12,22 @@ #include #include +#include "test_support.h" + extern int foo __attribute__((weak_import)); -int main() -{ - printf("[BEGIN] dylib-static-weak-link present\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // dylib will be found at runtime, so &foo should never be NULL if ( &foo != NULL ) { if ( foo == 42 ) - printf("[PASS] dylib-static-weak-link present\n"); + PASS("Success"); else - printf("[FAIL] dylib-static-weak-link present, wrong value\n"); + FAIL("Wrong value"); } else { - printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n"); + FAIL("&foo == NULL"); } - - return 0; } diff --git a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c index 6b547bb..d3c94cf 100644 --- a/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c @@ -1,25 +1,23 @@ -// BUILD: mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework -// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42 -// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $TEMP_DIR/Foo.framework/Foo +// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_FRAMEWORK_PATH.exe +// BUILD: $SKIP_INSTALL $BUILD_DIR/Foo.framework/Foo + // RUN: DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./env-DYLD_FALLBACK_FRAMEWORK_PATH.exe #include #include +#include "test_support.h" + extern int foo(); -int main() -{ - printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); - - if ( foo() == 42 ) - printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( foo() == 42 ) + PASS("Success"); else - printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); - - return 0; + FAIL("foo() was not 42"); } diff --git a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c index 9972627..0422db7 100644 --- a/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c @@ -1,25 +1,23 @@ -// BUILD: mkdir -p $BUILD_DIR/fallback -// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42 -// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $TEMP_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_FALLBACK_LIBRARY_PATH.exe +// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo.dylib + // RUN: DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./env-DYLD_FALLBACK_LIBRARY_PATH.exe #include +#include "test_support.h" + extern int foo(); -int main() -{ - printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n"); - - if ( foo() == 42 ) - printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( foo() == 42 ) + PASS("Success"); else - printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n"); - - return 0; + FAIL("libfoo.dylib incorrectly used fallback"); } diff --git a/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c b/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c index 9fcf357..3cf8ec3 100644 --- a/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c +++ b/testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c @@ -1,38 +1,42 @@ +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe +// BUILD(macos): $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe +// BUILD(macos): $TASK_FOR_PID_ENABLE $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe -// BUILD_ONLY: MacOSX -// BUILD: $CC main.c -o $BUILD_DIR/DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1 -ldarwintest -// BUILD: $CC main.c -o $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe -ldarwintest -// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/DYLD_FORCE_PLATFORM.exe -// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/DYLD_FORCE_PLATFORM_FAIL.exe +// BUILD(ios,tvos,watchos,bridgeos): -// RUN: ./DYLD_FORCE_PLATFORM.exe -// RUN: ./DYLD_FORCE_PLATFORM_FAIL.exe +// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM.exe +// RUN: DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM-fail.exe #include -#include "dyld_test.h" +#include + +#include "test_support.h" #if ENABLE_ALT_PLATFORMS __attribute__((section("__DATA,__allow_alt_plat"))) uint64_t dummy; -T_DECL_DYLD(DYLD_FORCE_PLATFORM, "Test that DYLD_FORCE_PLATFORM works correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) { - dyld_build_version_t ios12 = { .platform = PLATFORM_IOSMAC, .version = 0x000c0000 }; - T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_IOSMAC, "dyld_get_active_platform() should return PLATFORM_IOSMAC"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + dyld_build_version_t ios12 = { .platform = PLATFORM_IOS, .version = 0x000c0000 }; + if (dyld_get_active_platform() != PLATFORM_MACCATALYST) { FAIL("dyld_get_active_platform() should return PLATFORM_MACCATALYST"); } // libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift // We should be able to dlopen it only if we are correctly prepending the /System/iOSSupport root path - T_EXPECT_TRUE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should be able to dlopen libswiftUIKit but %s", dlerror()); #if 0 - //FIXME: This has to be disabled until we can fix rdar://47156760 - T_EXPECT_TRUE(dyld_program_minos_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0"); - T_EXPECT_TRUE(dyld_program_sdk_at_least(ios12), "DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0"); + // FIXME: We don't want to bring in such large dylib graphs. We can repurpose root testing support for this + if (!dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should be able to dlopen libswiftUIKit but %s", dlerror()); } #endif + if (!dyld_program_minos_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS min version greater than 12.0"); } + if (!dyld_program_sdk_at_least(ios12)) { FAIL("DYLD_FORCE_PLATFORM should synthesize an iOS sdk versio greater than 12.0"); } + PASS("Success"); } #else -T_DECL_DYLD(DYLD_FORCE_PLATFORM_FAIL, "Test that DYLD_FORCE_PLATFORM fails correctly", T_META_ENVVAR("DYLD_FORCE_PLATFORM=6")) { - T_EXPECT_EQ_UINT(dyld_get_active_platform(), PLATFORM_MACOS, "dyld_get_active_platform() should return PLATFORM_IOSMAC"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if (dyld_get_active_platform() != PLATFORM_MACOS) { FAIL("dyld_get_active_platform() should return PLATFORM_MACOS"); } // libswiftUIKit.dylib exists in /System/iOSSupport/usr/lib/swift // We should not be able to dlopen this as we don't expect to find it in a macOS location. If it starts // being in a macOS location then we should update this test - T_EXPECT_FALSE(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib"), "Should not be able to dlopen libswiftUIKit"); + if(dlopen_preflight("/usr/lib/swift/libswiftUIKit.dylib")) { FAIL("Should not be able to dlopen libswiftUIKit"); } + PASS("Success"); } #endif diff --git a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c index 8a2b4f6..c1da4eb 100644 --- a/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c @@ -1,5 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42 // BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_FRAMEWORK_PATH.exe $BUILD_DIR/Frameworks/Foo.framework/Foo @@ -11,19 +10,16 @@ #include #include +#include "test_support.h" + extern int foo(); -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1; - printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected); - if ( foo() == expected ) - printf("[PASS] env-DYLD_FRAMEWORK_PATH\n"); + PASS("Success"); else - printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n"); - - return 0; + FAIL("Incorrect libfoo.dylib loaded"); } diff --git a/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c index 49101de..5b4f4f8 100644 --- a/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c +++ b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c @@ -1,5 +1,4 @@ -// 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 @@ -26,17 +25,17 @@ #include #include +#include "test_support.h" + extern int foo(); extern int bar(); typedef int (*IntProc)(); -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { 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;; @@ -44,23 +43,19 @@ int main() #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; + FAIL("libfoo.dylib could not be loaded, %s", dlerror()); } 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; + FAIL("Bar.framework/Bar could not be loaded, %s", dlerror()); } 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; + FAIL("symbol 'foo' not found %s", dlerror()); } 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; + FAIL("symbol 'bar' not found %s", dlerror()); } int fooValue = (*fooProc)(); int barValue = (*barProc)(); @@ -69,12 +64,10 @@ int main() int barValue = bar(); #endif if ( fooValue != expectedFoo ) - printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, foo()=%d expected=%d\n", suffix, fooValue, expectedFoo); + FAIL("foo()=%d expected=%d", fooValue, expectedFoo); else if ( barValue != expectedBar ) - printf("[FAIL] env-DYLD_IMAGE_SUFFIX-%s, bar()=%d expected=%d\n", suffix, barValue, expectedBar); + FAIL("bar()=%d expected=%d", barValue, expectedBar); else - printf("[PASS] env-DYLD_IMAGE_SUFFIX-%s\n", suffix); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c new file mode 100644 index 0000000..d367fee --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c @@ -0,0 +1,34 @@ +// BUILD(macos): $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi +// BUILD(macos): $CC reexported-myzlib.c -dynamiclib -o $BUILD_DIR/re-export-override/reexported.dylib -compatibility_version 1.0 -framework CoreFoundation -install_name $RUN_DIR/re-export-override/reexported.dylib -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi +// BUILD(macos): $CC reexporter.c -dynamiclib -o $BUILD_DIR/re-export-override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -Wl,-reexport_library,$BUILD_DIR/re-export-override/reexported.dylib -Wl,-debug_variant -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_LIBRARY_PATH-cache-iOSMac.exe -lz -target x86_64-apple-ios14.0-macabi +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_LIBRARY_PATH-cache-iOSMac.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/override/ ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/re-export-override/ ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe + +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + +// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation. + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL) && !_dyld_shared_cache_optimized(); + + bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0); + + if ( usingMyDylib == expectMyDylib ) + PASS("Succes"); + else + FAIL("Expected %s, got %s", expectMyDylib ? "my" : "os", expectMyDylib ? "os" : "my"); +} + diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c new file mode 100644 index 0000000..5bd0c77 --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c @@ -0,0 +1,4 @@ +const char* zlibVersion() +{ + return "my"; +} diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c new file mode 100644 index 0000000..5bd0c77 --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c @@ -0,0 +1,4 @@ +const char* zlibVersion() +{ + return "my"; +} diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c new file mode 100644 index 0000000..f5f664f --- /dev/null +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c @@ -0,0 +1,4 @@ + +int foo() { + return 0; +} diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c index bcdc22f..3062bea 100644 --- a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c @@ -1,6 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/override -// BUILD: mkdir -p $BUILD_DIR/re-export-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 reexported-myzlib.c -dynamiclib -o $BUILD_DIR/re-export-override/reexported.dylib -compatibility_version 1.0 -framework CoreFoundation -install_name $RUN_DIR/re-export-override/reexported.dylib // BUILD: $CC reexporter.c -dynamiclib -o $BUILD_DIR/re-export-override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -Wl,-reexport_library,$BUILD_DIR/re-export-override/reexported.dylib -Wl,-debug_variant @@ -18,21 +16,18 @@ #include #include +#include "test_support.h" + // The test here is to override libz.1.dylib which is in the dyld cache with our own implementation. -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL) && !_dyld_shared_cache_optimized(); - 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"); + PASS("Succes"); else - printf("[FAIL] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os"); - - return 0; + FAIL("Expected %s, got %s", expectMyDylib ? "my" : "os", expectMyDylib ? "os" : "my"); } diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c index 9402412..6683eb5 100644 --- a/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c +++ b/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c @@ -1,5 +1,4 @@ -// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1 // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42 // BUILD: $CC main.c -o $BUILD_DIR/env-DYLD_LIBRARY_PATH.exe $BUILD_DIR/door1/libfoo.dylib @@ -11,19 +10,16 @@ #include #include +#include "test_support.h" + extern int foo(); -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1; - printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected); - if ( foo() == expected ) - printf("[PASS] env-DYLD_LIBRARY_PATH\n"); + PASS("Success"); else - printf("[FAIL] env-DYLD_LIBRARY_PATH\n"); - - return 0; + FAIL("Wrong dylib loaded"); } diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c b/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/foo.c similarity index 100% rename from unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c rename to testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/foo.c diff --git a/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c new file mode 100644 index 0000000..a411278 --- /dev/null +++ b/testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c @@ -0,0 +1,64 @@ + +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=9 -current_version 9 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt9/Foo.framework/Foo +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/Foo.framework/Foo +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt12/Foo.framework/Foo + +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/Foo2.framework/Foo2 +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/alt12/Foo2.framework/Foo2 + +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH-missing.exe -Wl,-dyld_env,DYLD_VERSIONED_FRAMEWORK_PATH=@loader_path/alt12 $BUILD_DIR/Foo2.framework/Foo2 + +// BUILD(macos): $SYMLINK Versions/A/Foo $BUILD_DIR/alt11/Foo.framework/Foo $DEPENDS_ON $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 10 +// RUN: DYLD_VERSIONED_FRAMEWORK_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 11 "alt11/Foo.framework/Versions/A/Foo" +// RUN: DYLD_VERSIONED_FRAMEWORK_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 10 +// RUN: DYLD_VERSIONED_FRAMEWORK_PATH=$RUN_DIR/alt9:$RUN_DIR/alt11 ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 11 +// RUN: DYLD_VERSIONED_FRAMEWORK_PATH=$RUN_DIR/alt11:$RUN_DIR/alt12 ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 12 +// FIXME: Forcibly disable testing with closures since macOS does not use them and they are currently broken +// RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_FRAMEWORK_PATH-missing.exe 12 + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include + +#include "test_support.h" + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( argc > 2 ) { + bool found = false; + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, argv[2]) != NULL ) { + found = true; + } + } + if ( !found ) { + FAIL("Dylib has wrong path"); + return EXIT_SUCCESS; + } + } + + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + if ( actualResult != expectedResult ) { + FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + } else { + PASS("Success"); + } + return 0; +} + diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c new file mode 100644 index 0000000..ee21326 --- /dev/null +++ b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c @@ -0,0 +1,5 @@ + +const char* zlibVersion() +{ + return RESULT; +} diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c new file mode 100644 index 0000000..c03c0d8 --- /dev/null +++ b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c @@ -0,0 +1,83 @@ +// This tests DYLD_VERSIONED_LIBRARY_PATH where the library we are doing a versioned check against is only in the shared +// cache and not on disk. This is the case when macOS moves to MRM. + +// libz.1.dylib was chosen as (on 10.15) it had a current version of 1.2.11 so we can write test dylibs with higer/lower versions + +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT="1.0.0" -current_version 1.0.0 -install_name /usr/lib/libz.1.dylib -o $BUILD_DIR/alt-1.0.0/libz.1.dylib +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT="2000.0.0" -current_version 2000.0.0 -install_name /usr/lib/libz.1.dylib -o $BUILD_DIR/alt-2000.0.0/libz.1.dylib + +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe -lz +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe -lz -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt-1.0.0 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-2000.0.0.exe -lz -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt-2000.0.0 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe -DUSE_DLOPEN + +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// Use the host when we have no ENV variable, or we check against the 1.0.0 test dylib which is lower than the host +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "host" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "host" +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe "host" +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "host" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "host" + +// Use the 2000.0.0 dylib when its present +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe "2000.0.0" +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-2000.0.0.exe "2000.0.0" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0" + +// 2000.0.0 should also override the 1.0.0 version if we specify both (in either order) +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0:$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0:$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0:$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0" +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0:$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0" + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include // for atoi() + +#include "test_support.h" + +#if USE_DLOPEN +#include +#else +extern const char* zlibVersion(); // returns "1.2.11" on the host dylib +#endif + +int main(int argc, const char* argv[]) +{ + const char* expectedResult = argv[1]; + +#if USE_DLOPEN + void * handle = dlopen("/usr/lib/libz.1.dylib", RTLD_LAZY); + if (!handle) { + FAIL("dlopen(\"%s\") failed with error \"%s\"", "/usr/lib/libz.1.dylib", dlerror()); + } + const char* (*zlibVersion)() = dlsym(handle, "zlibVersion"); + if (!zlibVersion) { + FAIL("dlsym(\"zlibVersion\") failed with error \"%s\"", dlerror()); + } +#endif + + const char* actualResult = zlibVersion(); + if ( !strcmp(expectedResult, "host") ) { + // We don't know what the host version is, but we know we wanted it instead of the test dylibs + if ( !strcmp(actualResult, "1.0.0") ) { + FAIL("Using wrong dylib. zlibVersion() returned %s, expected host version", actualResult); + } else { + PASS("Success"); + } + } else { + if ( strcmp(actualResult, expectedResult) ) { + FAIL("Using wrong dylib. zlibVersion() returned %s, expected %s", actualResult, expectedResult); + } else { + PASS("Success"); + } + } + return 0; +} + diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/foo.c similarity index 100% rename from unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c rename to testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/foo.c diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c new file mode 100644 index 0000000..bb800b0 --- /dev/null +++ b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c @@ -0,0 +1,88 @@ +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=9 -current_version 9 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt9/libfoo.dylib +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt11/libfoo.dylib +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt12/libfoo.dylib + +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib +// BUILD(macos): $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/alt12/libfoo2.dylib + +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-10.exe $BUILD_DIR/libfoo.dylib +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911c.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@executable_path/alt9:@executable_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe $BUILD_DIR/libfoo2.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo.dylib" +// BUILD(macos): $CC main.c -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo2.dylib" + +// BUILD(macos): $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib + +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe +// BUILD(macos): $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 10 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 11 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 10 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9:$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 11 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11:$RUN_DIR/alt12 ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 12 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-10.exe 10 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-11.exe 11 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-911.exe 11 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-911b.exe 11 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-911c.exe 11 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1112.exe 12 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1112b.exe 12 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1211.exe 12 +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1211b.exe 12 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-11.exe 11 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt12 ./env-DYLD_VERSIONED_LIBRARY_PATH-11.exe 12 +// FIXME: Forcibly disable testing with closures since macOS does not use them and they are currently broken +// RUN: DYLD_USE_CLOSURES=0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe 12 + +// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 11 +// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt9 ./env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe 10 +// RUN: DYLD_VERSIONED_LIBRARY_PATH="/AppleInternal/CoreOS/tests/dyld/env-DYLD_VERSIONED_LIBRARY_PATH/alt12" ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe 12 + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include // for atoi() + +#include "test_support.h" + +#if USE_DLOPEN +#include +#else +extern int foo(); +#endif + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); +#if USE_DLOPEN + void * handle = dlopen(RUN_DIR "/" DYLIB_NAME, RTLD_LAZY); + if (!handle) { + FAIL("dlopen(\"%s\") failed with error \"%s\"", RUN_DIR "/" DYLIB_NAME, dlerror()); + } + int (*foo)() = dlsym(handle, "foo"); + if (!foo) { + FAIL("dlsym(\"foo\") failed with error \"%s\"", dlerror()); + } +#endif + int actualResult = foo(); + if ( actualResult != expectedResult ) { + FAIL("Using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + } else { + PASS("Success"); + } + return 0; +} + diff --git a/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c b/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c index 6fcd005..c07fe63 100644 --- a/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c +++ b/testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC foo.s -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD(macos): $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace -// BUILD: $CC foo.s -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./flat-namespace.exe @@ -11,18 +11,15 @@ #include #include +#include "test_support.h" + extern int myAbs1; int* ptr = &myAbs1; -int main() -{ - printf("[BEGIN] flat-namespace-absolute-symbol\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( ptr != 0 ) { - printf("[FAIL] absolute symbol not bound to zero with flat lookup\n"); - return 0; + FAIL("Absolute symbol not bound to zero with flat lookup"); } - printf("[PASS] flat-namespace-absolute-symbol\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/flat-namespace.dtest/main.c b/testing/test-cases/flat-namespace.dtest/main.c index 37c99fc..8787091 100644 --- a/testing/test-cases/flat-namespace.dtest/main.c +++ b/testing/test-cases/flat-namespace.dtest/main.c @@ -1,7 +1,7 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD(macos): $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace -// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./flat-namespace.exe @@ -11,18 +11,15 @@ #include #include -int main() -{ - printf("[BEGIN] flat-namespace\n"); +#include "test_support.h" +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer // strncmp is tricky for flat namespace because it is re-exporte and renamed char* p1 = malloc(10); if ( strncmp(p1, "##########", 10) != 0 ) { - printf("[FAIL] malloc() from main executable not interposed\n"); - return 0; + FAIL("malloc() from main executable not interposed"); } - printf("[PASS] flat-namespace\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/image_infos-uuids.dtest/main.c b/testing/test-cases/image_infos-uuids.dtest/main.c index b96c698..a592be5 100644 --- a/testing/test-cases/image_infos-uuids.dtest/main.c +++ b/testing/test-cases/image_infos-uuids.dtest/main.c @@ -11,6 +11,8 @@ #include #include +#include "test_support.h" + extern const struct mach_header __dso_handle; static void printUUIDs(const struct dyld_all_image_infos* infos) @@ -20,35 +22,29 @@ static void printUUIDs(const struct dyld_all_image_infos* infos) const struct dyld_uuid_info* nonCacheInfo = &infos->uuidArray[i]; uuid_string_t uuidStr; uuid_unparse_upper(nonCacheInfo->imageUUID, uuidStr); - printf("%p %s\n", nonCacheInfo->imageLoadAddress, uuidStr); + LOG("%p %s", nonCacheInfo->imageLoadAddress, uuidStr); } } } -int main() -{ - printf("[BEGIN] image_infos-uuids\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // NOTE: dyld_all_image_infos is private, but currently looked at by kernel during stackshots // This test is to validate that the data available to the kernel is correct task_dyld_info_data_t task_dyld_info; mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { - printf("[FAIL] image_infos-uuids: task_info() failed\n"); - return 0; + FAIL("task_info() failed"); } const struct dyld_all_image_infos* infos = (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; if ( infos->uuidArray == NULL ) { - printf("[FAIL] infos->uuidArray == NULL\n"); - return 0; + FAIL("infos->uuidArray == NULL"); } if ( infos->uuidArrayCount < 2 ) { // expect to contain main executable and dyld - printf("[FAIL] infos->uuidArrayCount != 2 (is %lu)\n", infos->uuidArrayCount); - return 0; + FAIL("infos->uuidArrayCount != 2 (is %lu)", infos->uuidArrayCount); } printUUIDs(infos); uint32_t initialCount = infos->uuidArrayCount; @@ -63,30 +59,25 @@ int main() foundDyld = true; } if ( !foundMain ) { - printf("[FAIL] image_infos-uuids uuid array does not contain main program\n"); - return 0; + FAIL("image_infos-uuids uuid array does not contain main program"); } if ( !foundDyld ) { - printf("[FAIL] image_infos-uuids uuid array does not contain dyld\n"); - return 0; + FAIL("image_infos-uuids uuid array does not contain dyld"); } void* handle = dlopen(RUN_DIR "/test.bundle", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] image_infos-uuids %s\n", dlerror()); - return 0; + FAIL("image_infos-uuids %s", dlerror()); } - printf("loaded test.bundle\n"); + LOG("loaded test.bundle"); // now expect UUID list to be three if ( infos->uuidArrayCount != initialCount+1 ) { // expect to contain main executable and dyld - printf("[FAIL] infos->uuidArrayCount was not incremented (is %lu)\n", infos->uuidArrayCount); - return 0; + FAIL("infos->uuidArrayCount was not incremented (is %lu)", infos->uuidArrayCount); } printUUIDs(infos); - printf("[PASS] image_infos-uuids\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/init-term-segments.dtest/foo.c b/testing/test-cases/init-term-segments.dtest/foo.c index 77291a6..ad7471a 100644 --- a/testing/test-cases/init-term-segments.dtest/foo.c +++ b/testing/test-cases/init-term-segments.dtest/foo.c @@ -32,6 +32,7 @@ bool foo(bool* ptr) { return true; } +#if !__arm64e__ #if SUPPORT_CUSTOM_SEGMENTS __attribute__((section(("__MORETEXT,__text")))) #endif @@ -40,4 +41,5 @@ void myterm() { if ( gRanTerm != NULL ) *gRanTerm = true; -} \ No newline at end of file +} +#endif diff --git a/testing/test-cases/init-term-segments.dtest/main.c b/testing/test-cases/init-term-segments.dtest/main.c index e91a2cd..b3012b4 100644 --- a/testing/test-cases/init-term-segments.dtest/main.c +++ b/testing/test-cases/init-term-segments.dtest/main.c @@ -9,42 +9,41 @@ #include #include +#include "test_support.h" + extern bool foo(bool* ptr); -int main() -{ - printf("[BEGIN] init-term-segments\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { void* h = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW); if (h == NULL) { - printf("[FAIL] init-term-segments - dlerror = %s\n", dlerror()); - return 0; + FAIL("dlerror = %s", dlerror()); } void* fooSym = dlsym(RTLD_DEFAULT, "foo"); if ( fooSym == NULL ) { - printf("[FAIL] init-term-segments - dlsym failure\n"); - return 0; + FAIL("dlsym failure"); } bool ranTerm = false; bool ranInit = ((__typeof(&foo))fooSym)(&ranTerm); if (!ranInit) { - printf("[FAIL] init-term-segments - didn't run init\n"); - return 0; + FAIL("didn't run init"); } if ( dlclose(h) != 0 ) { - printf("[FAIL] init-term-segments - didn't dlclose\n"); - return 0; + FAIL("didn't dlclose"); } +#if __arm64e__ + if (ranTerm) { + FAIL("unexpectedly ran term"); + } +#else if (!ranTerm) { - printf("[FAIL] init-term-segments - didn't run term\n"); - return 0; + FAIL("didn't run term"); } +#endif - printf("[PASS] init-term-segments\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/interpose-malloc.dtest/main.c b/testing/test-cases/interpose-malloc.dtest/main.c index 9ef1842..5baba7d 100644 --- a/testing/test-cases/interpose-malloc.dtest/main.c +++ b/testing/test-cases/interpose-malloc.dtest/main.c @@ -11,38 +11,32 @@ #include #include +#include "test_support.h" + extern void* myalloc1(size_t); extern void* myalloc2(size_t); -int main() -{ - printf("[BEGIN] interpose-malloc\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { char* p1 = malloc(10); if ( strncmp(p1+10, "##########", 10) != 0 ) { - printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n"); - return 0; + FAIL("malloc() from main executable not interposed"); } void* p2 = myalloc1(6); if ( strncmp(p2+6, "######", 6) != 0 ) { - printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n"); - return 0; + FAIL("myalloc1() from libfoo.dylib not interposed"); } void* p3 = myalloc2(10); if ( strncmp(p3+10, "##########", 10) != 0 ) { - printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n"); - return 0; + FAIL("myalloc2() from libfoo.dylib not interposed"); } void* p4 = strdup("hello"); if ( strncmp(p4+6, "#######", 6) != 0 ) { - printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n"); - return 0; + FAIL("malloc() from strdup not interposed"); } - //printf("%p %p %p %p\n", p1, p2, p3, p4); - printf("[PASS] interpose-malloc\n"); - return 0; + LOG("%p %p %p %p", p1, p2, p3, p4); + PASS("Success"); } diff --git a/testing/test-cases/interpose-resolver.dtest/main.c b/testing/test-cases/interpose-resolver.dtest/main.c index 89651e8..bee0659 100644 --- a/testing/test-cases/interpose-resolver.dtest/main.c +++ b/testing/test-cases/interpose-resolver.dtest/main.c @@ -11,27 +11,25 @@ #include #include +#include "test_support.h" + extern int foo(); int (*pFoo)() = &foo; -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { #if INTERPOSED - printf("[BEGIN] interpose-resolver\n"); if ( foo() != 11 ) - printf("[FAIL] interpose-resolver: foo() != 11\n"); + FAIL("foo() != 11"); else if ( (*pFoo)() != 11 ) - printf("[FAIL] interpose-resolver: *pFoo() != 11\n"); + FAIL("*pFoo() != 11"); else - printf("[PASS] interpose-resolver\n"); + PASS("Success"); #else - printf("[BEGIN] resolver-only\n"); if ( foo() != 10 ) - printf("[FAIL] resolver-only: foo() != 10\n"); + FAIL(" foo() != 10"); else if ( (*pFoo)() != 10 ) - printf("[FAIL] resolver-only: *pFoo() != 10\n"); + FAIL(" *pFoo() != 10"); else - printf("[PASS] resolver-only\n"); + PASS("Success"); #endif - return 0; } diff --git a/testing/test-cases/interpose-then-dlopen.dtest/main.c b/testing/test-cases/interpose-then-dlopen.dtest/main.c index e712974..8e7e99e 100644 --- a/testing/test-cases/interpose-then-dlopen.dtest/main.c +++ b/testing/test-cases/interpose-then-dlopen.dtest/main.c @@ -1,7 +1,7 @@ // BUILD: $CC fooimpl.c -dynamiclib -o $BUILD_DIR/libfooimpl.dylib -install_name $RUN_DIR/libfooimpl.dylib -// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libfoo.dylib -Wl,-interposable_list,interposable.txt -install_name $RUN_DIR/libfoo.dylib -// BUILD: $CC bar.c -bundle $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libbar.bundle -Wl,-interposable_list,interposable.txt -// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -Wl,-interposable_list,interposable.txt -o $BUILD_DIR/interpose-then-dlopen.exe +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libfoo.dylib -Wl,-interposable_list,$SRC_DIR/interposable.txt -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC bar.c -bundle $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libbar.bundle -Wl,-interposable_list,$SRC_DIR/interposable.txt +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -Wl,-interposable_list,$SRC_DIR/interposable.txt -o $BUILD_DIR/interpose-then-dlopen.exe // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-then-dlopen.exe // BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/libfooimpl.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib @@ -14,6 +14,8 @@ #include #include +#include "test_support.h" + // Note, libinterposer.dylib interposes interposableFoo extern int interposableFoo(); @@ -23,55 +25,38 @@ extern int interposableBar(); extern int callFunc(); -static int tryImage(const char* path, int expectedFoo, int expectedBar) +static void tryImage(const char* path, int expectedFoo, int expectedBar) { void* handle = dlopen(path, RTLD_LAZY); if ( handle == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] interpose-then-dlopen %s\n", path); - return 1; + FAIL("dlopen(\"%s\") error: %s", path, dlerror()); } __typeof(&callFunc) callFooSym = (__typeof(&callFunc))dlsym(handle, "callFoo"); if ( callFooSym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] interpose-then-dlopen %s\n", path); - return 1; + FAIL("dlsym(\"callFoo\") error: %s", dlerror()); } int fooResult = callFooSym(); if ( fooResult != expectedFoo ) { - printf("[FAIL] interpose-then-dlopen callFoo() from %s not interposed as it returned %d\n", path, fooResult); - return 1; + FAIL("callFoo() from %s not interposed as it returned %d", path, fooResult); } __typeof(&callFunc) callBarSym = (__typeof(&callFunc))dlsym(handle, "callBar"); if ( callBarSym == NULL ) { - printf("dlerror(): %s\n", dlerror()); - printf("[FAIL] interpose-then-dlopen %s\n", path); - return 1; + FAIL("dlsym(\"callBar\") error: %s", dlerror()); } int barResult = callBarSym(); if ( barResult != expectedBar ) { - printf("[FAIL] interpose-then-dlopen callBar() from %s not interposed as it returned %d\n", path, barResult); - return 1; + FAIL("callBar() from %s not interposed as it returned %d", path, barResult); } - return 0; } -int main() -{ - printf("[BEGIN] interpose-then-dlopen\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + tryImage(RUN_DIR "/libfoo.dylib", 4, 2); + tryImage(RUN_DIR "/libbar.bundle", 4, 100); - if (tryImage(RUN_DIR "/libfoo.dylib", 4, 2)) - return 0; - - if (tryImage(RUN_DIR "/libbar.bundle", 4, 100)) - return 0; - - //printf("%p %p %p %p\n", p1, p2, p3, p4); - printf("[PASS] interpose-then-dlopen\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/interpose-weak.dtest/main.c b/testing/test-cases/interpose-weak.dtest/main.c index 5248427..07024b0 100644 --- a/testing/test-cases/interpose-weak.dtest/main.c +++ b/testing/test-cases/interpose-weak.dtest/main.c @@ -3,12 +3,13 @@ // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-present.exe // BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib -// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/foo34/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib // BUILD: $CC foo.c -DNO_FOO34 -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib -// BUILD: $CC main.c -DNO_FOO34 $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe +// BUILD: $CC main.c -DNO_FOO34 $BUILD_DIR/foo34/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe // BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-missing.exe -// BUILD: $CC interposer.c -dynamiclib $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib +// BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/foo34/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib +// BUILD: $SKIP_INSTALL $BUILD_DIR/foo34/libfoo2.dylib // RUN: DYLD_INSERT_LIBRARIES=libinterposer.dylib ./interpose-weak-present.exe // RUN: DYLD_INSERT_LIBRARIES=libinterposer2.dylib ./interpose-weak-missing.exe @@ -19,6 +20,7 @@ #include #include +#include "test_support.h" extern int foo1(); extern int foo2(); @@ -31,42 +33,32 @@ extern int foo4() __attribute__((weak_import)); #define MODE "missing" #endif -int main() -{ - printf("[BEGIN] interpose-weak-" MODE "\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( foo1() != 1 ) { - printf("[FAIL] interpose-weak-" MODE ", foo1() != 1\n"); - return 0; + FAIL(MODE ", foo1() != 1"); } if ( foo2() != 12 ) { - printf("[FAIL] interpose-weak-" MODE ", foo2() != 12\n"); - return 0; + FAIL(MODE ", foo2() != 12"); } #ifndef NO_FOO34 if ( foo3() != 3 ) { - printf("[FAIL] interpose-weak-" MODE ", foo3() != 3\n"); - return 0; + FAIL(MODE ", foo3() != 3"); } if ( foo4() != 14 ) { - printf("[FAIL] interpose-weak-" MODE ", foo4() != 14\n"); - return 0; + FAIL(MODE ", foo4() != 14"); } #else if ( &foo3 != NULL ) { - printf("[FAIL] interpose-weak-" MODE ", &foo3 != NULL\n"); - return 0; + FAIL(MODE ", &foo3 != NULL"); } if ( &foo4 != NULL ) { - printf("[FAIL] interpose-weak-" MODE ", &foo4 != NULL\n"); - return 0; + FAIL(MODE ", &foo4 != NULL"); } #endif - printf("[PASS] interpose-weak-" MODE "\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/bar.c b/testing/test-cases/kernel-auxkc-fixups.dtest/bar.c new file mode 100644 index 0000000..c5635c0 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/bar.c @@ -0,0 +1,25 @@ + +#include "../kernel-test-runner.h" + +extern int kernelExport(); +__typeof(&kernelExport) kernelExportPtr = &kernelExport; + +int bar() { + return kernelExportPtr() + 2; +} + +extern int kernelExportDirect(); + +// Test direct pointer fixups to the kernel. On x86_64 these would be emitted as just +// a branch relocation so we needed to synthesize a stub +__attribute__((constructor)) +int testDirectToKernel(const TestRunnerFunctions* funcs) { + LOG("testDirectToKernel(): start"); + // The kernel returned 42 + int v = kernelExportDirect(); + if ( v != 42 ) { + FAIL("kernelExportDirect() returned %d vs expected 42", v); + } + LOG("testDirectToKernel(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..e470d05 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.kernel.export + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..d7245d1 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + apfs + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist new file mode 100644 index 0000000..3376dea --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleExecutable + kernel-export + CFBundleIdentifier + com.apple.kernel.export + CFBundleName + kernel-export + CFBundlePackageType + KEXT + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/foo.c b/testing/test-cases/kernel-auxkc-fixups.dtest/foo.c new file mode 100644 index 0000000..3e5b437 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/foo.c @@ -0,0 +1,38 @@ + +#include "../kernel-test-runner.h" + +extern int bar(); +__typeof(&bar) barPtr = &bar; + +int foo() { + return barPtr() + 4; +} + +__attribute__((constructor)) +int test(const TestRunnerFunctions* funcs) { + LOG("test(): start"); + // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = foo(); + if ( v != 7 ) { + FAIL("foo() returned %d vs expected 7", v); + } + LOG("test(): end"); + return 0; +} + +int fooDirect() { + return bar() + 4; +} + +// Test direct pointer fixups, ie, not via a GOT +__attribute__((constructor)) +int testDirect(const TestRunnerFunctions* funcs) { + LOG("testDirect(): start"); + // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = fooDirect(); + if ( v != 7 ) { + FAIL("fooDirect() returned %d vs expected 7", v); + } + LOG("testDirect(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c b/testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c new file mode 100644 index 0000000..3313ca8 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c @@ -0,0 +1,10 @@ + +#include "../kernel-test-runner.h" + +int kernelExport() { + return 1; +} + +int kernelExportDirect() { + return 42; +} diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/main.c b/testing/test-cases/kernel-auxkc-fixups.dtest/main.c new file mode 100644 index 0000000..1a0aff6 --- /dev/null +++ b/testing/test-cases/kernel-auxkc-fixups.dtest/main.c @@ -0,0 +1,90 @@ + +// BOOT_ARGS: amfi=3 cs_enforcement_disable=1 + +// Create the base kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/kernel-export.kext/Info.plist $BUILD_DIR/extensions/kernel-export-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination -ftrivial-auto-var-init=uninitialized +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC kernel-export.c -o $BUILD_DIR/extensions/kernel-export-kext/kernel-export -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe -extensions $BUILD_DIR/extensions -bundle-id com.apple.kernel.export $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/kernel-export + +// Create the aux kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-aux-kernel-collection $BUILD_DIR/aux.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar + +// BUILD(watchos): + +// RUN_STATIC: $RUN_STATIC $RUN_DIR/kernel.kc - - $RUN_DIR/aux.kc + +#include "../kernel-test-runner.h" +#include "../kernel-fixups.h" +#include "../kernel-classic-relocs.h" +#include "../kernel-helpers.h" + +#define printf(...) funcs->printf(__VA_ARGS__) + +int x = 1; +int *g = &x; + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + + const void* slideBasePointers[4]; + slideBasePointers[0] = funcs->basePointers[0]; + slideBasePointers[1] = funcs->basePointers[1]; + slideBasePointers[2] = funcs->basePointers[2]; + slideBasePointers[3] = funcs->basePointers[3]; + int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[0] slide = %d\n", slideReturnCode); + return 0; + } + + int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf); + if ( slideClassicReturnCode != 0 ) { + FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode); + return 0; + } + + if ( g[0] != x ) { + FAIL("g[0] != x, %d != %d\n", g[0], x); + return 0; + } + + // First slide the auxKC using the top level fixups. These handle the branch GOTs + slideReturnCode = slide(funcs->mhs[3], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[3] slide = %d\n", slideReturnCode); + return 0; + } + +#if __x86_64__ + // On x86 only, slide the auxKC individually + // Then slide pageable using the fixups attached to the kexts own mach headers + slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[3], slideBasePointers, funcs->printf, funcs); + if ( slideReturnCode != 0 ) { + FAIL("mhs[3] slide = %d\n", slideReturnCode); + return 0; + } +#endif + + // If we have any mod init funcs, then lets run them now + int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[3], funcs->printf, funcs); + if ( runModInitFuncs != 0 ) { + FAIL("runModInitFuncs = %d\n", runModInitFuncs); + return 0; + } + + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-classic-relocs.h b/testing/test-cases/kernel-classic-relocs.h new file mode 100644 index 0000000..639ead7 --- /dev/null +++ b/testing/test-cases/kernel-classic-relocs.h @@ -0,0 +1,157 @@ + +#include +#include + +typedef int (*FixupsLogFunc)(const char*, ...); +static const int LogFixupsClassic = 0; + +// We may not have strcmp, so make our own +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int areEqualClassic(const char* a, const char* b) { + while (*a && *b) { + if (*a != *b) + return 0; + ++a; + ++b; + } + return *a == *b; +} + +// Temporary until we have +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int slideClassic(const struct mach_header* mh, FixupsLogFunc logFunc) { + +#if !__x86_64__ + return 0; +#endif + + // First find the slide and dysymtab fixups load command + uint64_t textVMAddr = 0; + uint64_t firstWritableVMAddr = ~0ULL; + const struct dysymtab_command* dynSymbolTable = 0; + uint64_t linkeditVMAddr = 0; + uint64_t linkeditFileOffset = 0; + + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: mh %p\n", mh); + } + + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: parsing load commands\n"); + } + + const struct load_command* startCmds = 0; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)mh; + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < mh->ncmds; ++i) { + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_DYSYMTAB ) { + dynSymbolTable = (const struct dysymtab_command*)cmd; + } else if ( cmd->cmd == LC_SEGMENT_64 ) { + const struct segment_command_64* seg = (const struct segment_command_64*)cmd; + if ( areEqualClassic(seg->segname, "__TEXT") ) { + textVMAddr = seg->vmaddr; + } else if ( areEqualClassic(seg->segname, "__LINKEDIT") ) { + linkeditVMAddr = seg->vmaddr; + linkeditFileOffset = seg->fileoff; + } + if ( (seg->initprot & VM_PROT_WRITE) && (firstWritableVMAddr == ~0ULL) ) { + firstWritableVMAddr = seg->vmaddr; + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: first writable segment %s = 0x%llx\n", seg->segname, seg->vmaddr); + } + } + } + cmd = nextCmd; + } + + uintptr_t slide = (uintptr_t)mh - textVMAddr; + + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: slide 0x%llx\n", slide); + } + + if ( dynSymbolTable == 0 ) + return 0; + + if ( dynSymbolTable->nlocrel == 0 ) + return 0; + + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: found dynamic symbol table %p\n", dynSymbolTable); + logFunc("[LOG] kernel-classic-relocs: found linkeditVMAddr %p\n", (void*)linkeditVMAddr); + logFunc("[LOG] kernel-classic-relocs: found linkeditFileOffset %p\n", (void*)linkeditFileOffset); + } + + // Now we have the dynamic symbol table, walk it to apply all the rebases + uint32_t offsetInLinkedit = dynSymbolTable->locreloff - linkeditFileOffset; + uintptr_t linkeditStartAddr = linkeditVMAddr + slide; + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: offsetInLinkedit 0x%x\n", offsetInLinkedit); + logFunc("[LOG] kernel-classic-relocs: linkeditStartAddr %p\n", (void*)linkeditStartAddr); + } + + const uint64_t relocsStartAddress = firstWritableVMAddr; + const struct relocation_info* const relocsStart = (const struct relocation_info*)(linkeditStartAddr + offsetInLinkedit); + const struct relocation_info* const relocsEnd = &relocsStart[dynSymbolTable->nlocrel]; + for (const struct relocation_info* reloc = relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_length == 2 ) { + uint32_t* fixupLoc = (uint32_t*)(relocsStartAddress + reloc->r_address + slide); + uint32_t slidValue = *fixupLoc + slide; + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: fixupLoc %p = 0x%x + 0x%x + 0x%x\n", fixupLoc, relocsStartAddress, reloc->r_address, slide); + logFunc("[LOG] kernel-classic-relocs: slidValue *%p = 0x%x\n", fixupLoc, slidValue); + } + *fixupLoc = slidValue; + continue; + } + if ( reloc->r_length == 3 ) { + uint64_t* fixupLoc = (uint64_t*)(relocsStartAddress + reloc->r_address + slide); + uint64_t slidValue = *fixupLoc + slide; + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: fixupLoc %p = 0x%x + 0x%x + 0x%x\n", fixupLoc, relocsStartAddress, reloc->r_address, slide); + logFunc("[LOG] kernel-classic-relocs: slidValue *%p = 0x%llx\n", fixupLoc, slidValue); + } + *fixupLoc = slidValue; + continue; + } + logFunc("[LOG] kernel-fixups: unknown reloc size\n", reloc->r_length); + return 1; + } + + if (LogFixupsClassic) { + logFunc("[LOG] kernel-classic-relocs: Done\n"); + } + + return 0; +} + + diff --git a/testing/test-cases/kernel-fixups-x86_64.dtest/main.c b/testing/test-cases/kernel-fixups-x86_64.dtest/main.c new file mode 100644 index 0000000..76094ca --- /dev/null +++ b/testing/test-cases/kernel-fixups-x86_64.dtest/main.c @@ -0,0 +1,74 @@ + +// BOOT_ARGSxx: amfi=3 cs_enforcement_disable=1 +// FIXME: re-enable for macOS when it work +// xxBUILDxx(macos|x86_64): $CC main.c -o $BUILD_DIR/kernel-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 +// xxBUILDxx(macos|x86_64): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-fixups.exe + +// BUILDxx(macos,ios,tvos,watchos,bridgeos): + +// xxRUN_STATIC: $RUN_STATIC ./kernel.kc + +// This tests that unaligned fixups work in x86_64 + +#include "../kernel-test-runner.h" +#include "../kernel-fixups.h" +#include "../kernel-classic-relocs.h" + +#define printf(...) funcs->printf(__VA_ARGS__) + +int x = 1; + +struct __attribute__((packed)) __attribute__((aligned((4096)))) PackedS { + int i0; // aligned to 8 + int* p0; // aligned to 4 + int i1; // aligned to 4 + int* p1; // aligned to 8 + char i2; // aligned to 8 + int* p2; // aligned to 1 +}; +struct PackedS ps = { 0, &x, 0, &x, 0, &x }; + +__attribute__((section(("__HIB, __text")))) +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + + const void* slideBasePointers[4]; + slideBasePointers[0] = funcs->basePointers[0]; + slideBasePointers[1] = funcs->basePointers[1]; + slideBasePointers[2] = funcs->basePointers[2]; + slideBasePointers[3] = funcs->basePointers[3]; + int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("slide = %d\n", slideReturnCode); + return 0; + } + + int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf); + if ( slideClassicReturnCode != 0 ) { + FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode); + return 0; + } + + LOG("Done sliding"); + + if ( ps.p0[0] != x ) { + FAIL("ps.p1[0] != x, %d != %d\n", ps.p0[0], x); + return 0; + } + + if ( ps.p1[0] != x ) { + FAIL("ps.p1[0] != x, %d != %d\n", ps.p1[0], x); + return 0; + } + + if ( ps.p2[0] != x ) { + FAIL("ps.p2[0] != x, %d != %d\n", ps.p2[0], x); + return 0; + } + + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-fixups.dtest/main.c b/testing/test-cases/kernel-fixups.dtest/main.c new file mode 100644 index 0000000..0c1347a --- /dev/null +++ b/testing/test-cases/kernel-fixups.dtest/main.c @@ -0,0 +1,58 @@ + +// BOOT_ARGS: amfi=3 cs_enforcement_disable=1 + +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC main.c -o $BUILD_DIR/kernel-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination -ftrivial-auto-var-init=uninitialized +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-fixups.exe + +// BUILD(watchos): + + +// RUN_STATIC: $RUN_STATIC ./kernel.kc + +#include "../kernel-test-runner.h" +#include "../kernel-fixups.h" +#include "../kernel-classic-relocs.h" + +#define printf(...) funcs->printf(__VA_ARGS__) + +int x = 1; +int *g = &x; + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + + const void* slideBasePointers[4]; + slideBasePointers[0] = funcs->basePointers[0]; + slideBasePointers[1] = funcs->basePointers[1]; + slideBasePointers[2] = funcs->basePointers[2]; + slideBasePointers[3] = funcs->basePointers[3]; + int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("slide = %d\n", slideReturnCode); + return 0; + } + + int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf); + if ( slideClassicReturnCode != 0 ) { + FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode); + return 0; + } + + LOG("Done sliding"); + + if ( g[0] != x ) { + FAIL("g[0] != x, %d != %d\n", g[0], x); + return 0; + } + + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-fixups.h b/testing/test-cases/kernel-fixups.h new file mode 100644 index 0000000..6cbf483 --- /dev/null +++ b/testing/test-cases/kernel-fixups.h @@ -0,0 +1,288 @@ + +#ifndef KERNEL_FIXUPS_H +#define KERNEL_FIXUPS_H + +#include +#include + +typedef int (*FixupsLogFunc)(const char*, ...); +static const int LogFixups = 0; + +// We may not have strcmp, so make our own +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int areEqual(const char* a, const char* b) { + while (*a && *b) { + if (*a != *b) + return 0; + ++a; + ++b; + } + return *a == *b; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-declarations" +union ChainedFixupPointerOnDisk +{ + uint64_t raw64; + struct dyld_chained_ptr_64_kernel_cache_rebase fixup64; +}; +#pragma clang diagnostic pop + +// Temporary until we have +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static uint64_t signPointer(struct dyld_chained_ptr_64_kernel_cache_rebase pointer, + void* loc, + uint64_t target) +{ +#if __has_feature(ptrauth_calls) + uint64_t discriminator = pointer.diversity; + if ( pointer.addrDiv ) { + if ( discriminator != 0 ) { + discriminator = __builtin_ptrauth_blend_discriminator(loc, discriminator); + } else { + discriminator = (uint64_t)(uintptr_t)loc; + } + } + switch ( pointer.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; +} + +// Temporary until we have +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +void fixupValue(union ChainedFixupPointerOnDisk* fixupLoc, + const struct dyld_chained_starts_in_segment* segInfo, + uintptr_t slide, + const void* basePointers[4], + int* stop, + FixupsLogFunc logFunc) { + if (LogFixups) { + logFunc("[LOG] kernel-fixups: fixupValue %p\n", fixupLoc); + } + switch (segInfo->pointer_format) { +#if __LP64__ + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: { + const void* baseAddress = basePointers[fixupLoc->fixup64.cacheLevel]; + if ( baseAddress == 0 ) { + logFunc("Invalid cache level: %d\n", fixupLoc->fixup64.cacheLevel); + *stop = 1; + return; + } + uintptr_t slidValue = (uintptr_t)baseAddress + fixupLoc->fixup64.target; + if (LogFixups) { + logFunc("[LOG] kernel-fixups: slidValue %p = %p + %p\n", (void*)slidValue, (void*)baseAddress, (void*)fixupLoc->fixup64.target); + } +#if __has_feature(ptrauth_calls) + if ( fixupLoc->fixup64.isAuth ) { + slidValue = signPointer(fixupLoc->fixup64, fixupLoc, slidValue); + } +#else + if ( fixupLoc->fixup64.isAuth ) { + logFunc("Unexpected authenticated fixup\n"); + *stop = 1; + return; + } +#endif // __has_feature(ptrauth_calls) + fixupLoc->raw64 = slidValue; + break; + } +#endif // __LP64__ + default: + logFunc("unsupported pointer chain format: 0x%04X", segInfo->pointer_format); + *stop = 1; + break; + } +} + +// Temporary until we have +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int walkChain(const struct mach_header* mh, + const struct dyld_chained_starts_in_segment* segInfo, + uint32_t pageIndex, + uint16_t offsetInPage, + uintptr_t slide, + const void* basePointers[4], + FixupsLogFunc logFunc) +{ + if (LogFixups) { + logFunc("[LOG] kernel-fixups: walkChain page[%d]\n", pageIndex); + } + int stop = 0; + uint8_t* pageContentStart = (uint8_t*)mh + segInfo->segment_offset + (pageIndex * segInfo->page_size); + union ChainedFixupPointerOnDisk* chain = (union ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage); + int chainEnd = 0; + while (!stop && !chainEnd) { + // copy chain content, in case handler modifies location to final value + union ChainedFixupPointerOnDisk chainContent = *chain; + fixupValue(chain, segInfo, slide, basePointers, &stop, logFunc); + if ( !stop ) { + switch (segInfo->pointer_format) { +#if __LP64__ + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + if ( chainContent.fixup64.next == 0 ) + chainEnd = 1; + else + chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next*4); + break; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + if ( chainContent.fixup64.next == 0 ) + chainEnd = 1; + else + chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next); + break; +#endif // __LP64__ + default: + logFunc("unknown pointer format 0x%04X", segInfo->pointer_format); + stop = 1; + } + } + } + return stop; +} + +// Temporary until we have +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int slide(const struct mach_header* mh, const void* basePointers[4], FixupsLogFunc logFunc) { + // First find the slide and chained fixups load command + uint64_t textVMAddr = 0; + const struct linkedit_data_command* chainedFixups = 0; + uint64_t linkeditVMAddr = 0; + uint64_t linkeditFileOffset = 0; + + if (LogFixups) { + logFunc("[LOG] kernel-fixups: mh %p\n", mh); + } + + if (LogFixups) { + logFunc("[LOG] kernel-fixups: parsing load commands\n"); + } + + const struct load_command* startCmds = 0; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)mh; + logFunc("[LOG] kernel-fixups: file does not start with MH_MAGIC[_64] 0x%08X 0x%08X\n", h[0], h [1]); + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < mh->ncmds; ++i) { + if (LogFixups) { + logFunc("[LOG] kernel-fixups: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_DYLD_CHAINED_FIXUPS ) { + chainedFixups = (const struct linkedit_data_command*)cmd; + } else if ( cmd->cmd == LC_SEGMENT_64 ) { + const struct segment_command_64* seg = (const struct segment_command_64*)cmd; + if ( areEqual(seg->segname, "__TEXT") ) { + textVMAddr = seg->vmaddr; + } else if ( areEqual(seg->segname, "__LINKEDIT") ) { + linkeditVMAddr = seg->vmaddr; + linkeditFileOffset = seg->fileoff; + } + } + cmd = nextCmd; + } + + uintptr_t slide = (uintptr_t)mh - textVMAddr; + + if (LogFixups) { + logFunc("[LOG] kernel-fixups: slide 0x%llx\n", slide); + } + + if ( chainedFixups == 0 ) + return 0; + + if (LogFixups) { + logFunc("[LOG] kernel-fixups: found chained fixups %p\n", chainedFixups); + logFunc("[LOG] kernel-fixups: found linkeditVMAddr %p\n", (void*)linkeditVMAddr); + logFunc("[LOG] kernel-fixups: found linkeditFileOffset %p\n", (void*)linkeditFileOffset); + } + + // Now we have the chained fixups, walk it to apply all the rebases + uint32_t offsetInLinkedit = chainedFixups->dataoff - linkeditFileOffset; + uintptr_t linkeditStartAddr = linkeditVMAddr + slide; + if (LogFixups) { + logFunc("[LOG] kernel-fixups: offsetInLinkedit 0x%x\n", offsetInLinkedit); + logFunc("[LOG] kernel-fixups: linkeditStartAddr %p\n", (void*)linkeditStartAddr); + } + + const struct dyld_chained_fixups_header* fixupsHeader = (const struct dyld_chained_fixups_header*)(linkeditStartAddr + offsetInLinkedit); + const struct dyld_chained_starts_in_image* fixupStarts = (const struct dyld_chained_starts_in_image*)((uint8_t*)fixupsHeader + fixupsHeader->starts_offset); + if (LogFixups) { + logFunc("[LOG] kernel-fixups: fixupsHeader %p\n", fixupsHeader); + logFunc("[LOG] kernel-fixups: fixupStarts %p\n", fixupStarts); + } + + int stopped = 0; + for (uint32_t segIndex=0; segIndex < fixupStarts->seg_count && !stopped; ++segIndex) { + if (LogFixups) { + logFunc("[LOG] kernel-fixups: segment %d\n", segIndex); + } + if ( fixupStarts->seg_info_offset[segIndex] == 0 ) + continue; + const struct dyld_chained_starts_in_segment* segInfo = (const struct dyld_chained_starts_in_segment*)((uint8_t*)fixupStarts + fixupStarts->seg_info_offset[segIndex]); + for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) { + uint16_t offsetInPage = segInfo->page_start[pageIndex]; + if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE ) + continue; + if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) { + // FIXME: Implement this + return 1; + } + else { + // one chain per page + if ( walkChain(mh, segInfo, pageIndex, offsetInPage, slide, basePointers, logFunc) ) + stopped = 1; + } + } + } + + return stopped; +} + +#endif /* KERNEL_FIXUPS_H */ diff --git a/testing/test-cases/kernel-hello-world.dtest/main.c b/testing/test-cases/kernel-hello-world.dtest/main.c new file mode 100644 index 0000000..0fa22f3 --- /dev/null +++ b/testing/test-cases/kernel-hello-world.dtest/main.c @@ -0,0 +1,30 @@ + +// BOOT_ARGS: amfi=3 cs_enforcement_disable=1 + +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC main.c -o $BUILD_DIR/kernel-hello-world.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination -ftrivial-auto-var-init=uninitialized +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel-hello-world.kc -kernel $BUILD_DIR/kernel-hello-world.exe -platform kernel + +// BUILD(watchos): + +// RUN_STATIC: $RUN_STATIC ./kernel-hello-world.kc + +#include "../kernel-test-runner.h" + +typedef unsigned long long uint64_t; +extern int printf(const char*, ...); + +#define printf(...) funcs->printf(__VA_ARGS__) + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-helpers.h b/testing/test-cases/kernel-helpers.h new file mode 100644 index 0000000..7b944d1 --- /dev/null +++ b/testing/test-cases/kernel-helpers.h @@ -0,0 +1,284 @@ + + +#include +#include + +#include "kernel-fixups.h" + +#ifndef LC_FILESET_ENTRY +#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */ +struct fileset_entry_command { + uint32_t cmd; /* LC_FILESET_ENTRY */ + uint32_t cmdsize; /* includes id string */ + uint64_t vmaddr; /* memory address of the dylib */ + uint64_t fileoff; /* file offset of the dylib */ + union lc_str entry_id; /* contained entry id */ + uint32_t reserved; /* entry_id is 32-bits long, so this is the reserved padding */ +}; +#endif + +typedef int (*ModInitLogFunc)(const char*, ...); +static const int LogModInits = 0; + +struct TestRunnerFunctions; +typedef int (*InitializerFunc)(const TestRunnerFunctions*); + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int getSlide(const struct mach_header* mh, ModInitLogFunc logFunc, + uintptr_t* slide) { + uint64_t textVMAddr = 0; + + if (LogFixups) { + logFunc("[LOG] kernel-slide: mh %p\n", mh); + } + + if (LogFixups) { + logFunc("[LOG] kernel-slide: parsing load commands\n"); + } + + const struct load_command* startCmds = 0; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)mh; + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < mh->ncmds; ++i) { + if (LogFixups) { + logFunc("[LOG] kernel-slide: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_SEGMENT_64 ) { + const struct segment_command_64* seg = (const struct segment_command_64*)cmd; + if ( areEqual(seg->segname, "__TEXT") ) { + textVMAddr = seg->vmaddr; + } + } + cmd = nextCmd; + } + + *slide = (uintptr_t)mh - textVMAddr; + return 0; +} + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int runAllModInitFunctions(const struct mach_header* mh, ModInitLogFunc logFunc, + const TestRunnerFunctions* funcs) { + uintptr_t slide = 0; + if ( getSlide(mh, logFunc, &slide) != 0 ) { + return 1; + } + + const struct load_command* startCmds = 0; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)mh; + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < mh->ncmds; ++i) { + if (LogModInits) { + logFunc("[LOG] kernel-mod-inits: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_SEGMENT_64 ) { + const struct segment_command_64* seg = (const struct segment_command_64*)cmd; + const struct section_64* const sectionsStart = (struct section_64*)((char*)seg + sizeof(struct segment_command_64)); + const struct section_64* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct section_64* sect = sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( LogModInits ) { + logFunc("[LOG] kernel-mod-inits: section: %s %s\n", sect->segname, sect->sectname); + } + if ( type == S_MOD_INIT_FUNC_POINTERS ) { + InitializerFunc* inits = (InitializerFunc*)(sect->addr + slide); + const uintptr_t count = sect->size / sizeof(uintptr_t); + // Ensure __mod_init_func section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) { + logFunc("[LOG] kernel-mod-inits: __mod_init_funcs section has malformed address range\n"); + return 1; + } + for (uintptr_t j = 0; j < count; ++j) { + InitializerFunc func = inits[j]; +#if __has_feature(ptrauth_calls) + func = (InitializerFunc)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0); +#endif + if ( LogModInits ) { + logFunc("[LOG] kernel-mod-inits: running mod init %p\n", (const void*)func); + } + int initResult = func(funcs); + if ( initResult != 0 ) { + logFunc("[LOG] kernel-mod-inits: mod init %p, result = %d\n", (const void*)func, initResult); + return 1; + } + } + } + } + } + cmd = nextCmd; + } + + return 0; +} + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int runAllModInitFunctionsForAppCache(const struct mach_header* appCacheMH, ModInitLogFunc logFunc, + const TestRunnerFunctions* funcs) { + uintptr_t slide = 0; + if ( getSlide(appCacheMH, logFunc, &slide) != 0 ) { + return 1; + } + + if (LogFixups) { + logFunc("[LOG] mod-init: appCacheMH %p\n", appCacheMH); + } + + if (LogFixups) { + logFunc("[LOG] mod-init: parsing load commands\n"); + } + + const struct load_command* startCmds = 0; + if ( appCacheMH->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64)); + else if ( appCacheMH->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)appCacheMH; + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) { + if (LogFixups) { + logFunc("[LOG] mod-init: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_FILESET_ENTRY ) { + const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd; + const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset; + const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slide); + if ( LogModInits ) { + logFunc("[LOG] mod-init: Running mod inits for %p: %s\n", mh, name); + } + int result = runAllModInitFunctions(mh, logFunc, funcs); + if (result != 0) { + return 1; + } + } + cmd = nextCmd; + } + + return 0; +} + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static int slideKextsInsideKernelCollection(const struct mach_header* appCacheMH, const void* basePointers[4], + FixupsLogFunc logFunc, const TestRunnerFunctions* funcs) { + uintptr_t slideAmount = 0; + if ( getSlide(appCacheMH, logFunc, &slideAmount) != 0 ) { + return 1; + } + + if (LogFixups) { + logFunc("[LOG] slide-pageable: appCacheMH %p\n", appCacheMH); + } + + if (LogFixups) { + logFunc("[LOG] slide-pageable: parsing load commands\n"); + } + + const struct load_command* startCmds = 0; + if ( appCacheMH->magic == MH_MAGIC_64 ) + startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64)); + else if ( appCacheMH->magic == MH_MAGIC ) + startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header)); + else { + const uint32_t* h = (uint32_t*)appCacheMH; + //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]); + return 1; // not a mach-o file + } + const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds); + const struct load_command* cmd = startCmds; + for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) { + if (LogFixups) { + logFunc("[LOG] slide-pageable: parsing load command %d with cmd=0x%x\n", i, cmd->cmd); + } + const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize); + return 1; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd); + return 1; + } + if ( cmd->cmd == LC_FILESET_ENTRY ) { + const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd; + const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset; + const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slideAmount); + if ( LogModInits ) { + logFunc("[LOG] slide-pageable: Sliding %p: %s\n", mh, name); + } + int slideReturnCode = slide(mh, basePointers, logFunc); + if ( slideReturnCode != 0 ) { + FAIL("mh slide = %d\n", slideReturnCode); + return 1; + } + } + cmd = nextCmd; + } + + return 0; +} diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c new file mode 100644 index 0000000..87fe853 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c @@ -0,0 +1,25 @@ + +#include "../kernel-test-runner.h" + +extern int pageableExport(); +__typeof(&pageableExport) pageableExportPtr = &pageableExport; + +int bar() { + return pageableExportPtr() + 2; +} + +extern int pageableExportDirect(); + +// Test direct pointer fixups to the pageable KC. On x86_64 these would be emitted as just +// a branch relocation so we needed to synthesize a stub +__attribute__((constructor)) +int testDirectToPageable(const TestRunnerFunctions* funcs) { + LOG("testDirectToPageable(): start"); + // The pageable returned 42 + int v = pageableExportDirect(); + if ( v != 42 ) { + FAIL("pageableExportDirect() returned %d vs expected 42", v); + } + LOG("testDirectToPageable(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..f699ffc --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.pageable + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..d7245d1 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + apfs + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist new file mode 100644 index 0000000..8264e75 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleExecutable + pageable + CFBundleIdentifier + com.apple.pageable + CFBundleName + pageable + CFBundlePackageType + KEXT + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c new file mode 100644 index 0000000..254aab5 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c @@ -0,0 +1,38 @@ + +#include "../kernel-test-runner.h" + +extern int bar(); +__typeof(&bar) barPtr = &bar; + +int foo() { + return barPtr() + 4; +} + +__attribute__((constructor)) +int test(const TestRunnerFunctions* funcs) { + LOG("test(): start"); + // pageable, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = foo(); + if ( v != 7 ) { + FAIL("foo() returned %d vs expected 7", v); + } + LOG("test(): end"); + return 0; +} + +int fooDirect() { + return bar() + 4; +} + +// Test direct pointer fixups, ie, not via a GOT +__attribute__((constructor)) +int testDirect(const TestRunnerFunctions* funcs) { + LOG("testDirect(): start"); + // pageable, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = fooDirect(); + if ( v != 7 ) { + FAIL("fooDirect() returned %d vs expected 7", v); + } + LOG("testDirect(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c new file mode 100644 index 0000000..38215b2 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c @@ -0,0 +1,108 @@ + +// BOOT_ARGS: amfi=3 cs_enforcement_disable=1 + +// Create the base kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination -ftrivial-auto-var-init=uninitialized +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe + +// Create the pageable kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/pageable.kext/Info.plist $BUILD_DIR/extensions/pageable-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC pageable.c -o $BUILD_DIR/extensions/pageable-kext/pageable -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-pageable-kernel-collection $BUILD_DIR/pageable.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions -bundle-id com.apple.pageable $DEPENDS_ON $BUILD_DIR/extensions/pageable-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/pageable-kext/pageable + +// Create the aux kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-aux-kernel-collection $BUILD_DIR/aux.kc -kernel-collection $BUILD_DIR/kernel.kc -pageable-collection $BUILD_DIR/pageable.kc -extensions $BUILD_DIR/extensions -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar + +// BUILD(watchos): + +// RUN_STATIC: $RUN_STATIC $RUN_DIR/kernel.kc $RUN_DIR/pageable.kc - $RUN_DIR/aux.kc + +#include "../kernel-test-runner.h" +#include "../kernel-fixups.h" +#include "../kernel-classic-relocs.h" +#include "../kernel-helpers.h" + +#define printf(...) funcs->printf(__VA_ARGS__) + +int x = 1; +int *g = &x; + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + + const void* slideBasePointers[4]; + slideBasePointers[0] = funcs->basePointers[0]; + slideBasePointers[1] = funcs->basePointers[1]; + slideBasePointers[2] = funcs->basePointers[2]; + slideBasePointers[3] = funcs->basePointers[3]; + int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[0] slide = %d\n", slideReturnCode); + return 0; + } + + int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf); + if ( slideClassicReturnCode != 0 ) { + FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode); + return 0; + } + + if ( g[0] != x ) { + FAIL("g[0] != x, %d != %d\n", g[0], x); + return 0; + } + + // First slide the pageableKC using the top level fixups. These handle the branch GOTs + slideReturnCode = slide(funcs->mhs[1], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[1] slide = %d\n", slideReturnCode); + return 0; + } + + // Then slide pageable using the fixups attached to the kexts own mach headers + slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[1], slideBasePointers, funcs->printf, funcs); + if ( slideReturnCode != 0 ) { + FAIL("mhs[1] slide = %d\n", slideReturnCode); + return 0; + } + + // Slide the auc KC + slideReturnCode = slide(funcs->mhs[3], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[3] slide = %d\n", slideReturnCode); + return 0; + } + + #if __x86_64__ + // On x86 only, slide the auxKC individually + // Then slide pageable using the fixups attached to the kexts own mach headers + slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[3], slideBasePointers, funcs->printf, funcs); + if ( slideReturnCode != 0 ) { + FAIL("mhs[3] slide = %d\n", slideReturnCode); + return 0; + } + #endif + + // If we have any mod init funcs, then lets run them now + // These are the tests inside the auxKC kexts + int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[3], funcs->printf, funcs); + if ( runModInitFuncs != 0 ) { + FAIL("runModInitFuncs = %d\n", runModInitFuncs); + return 0; + } + + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c new file mode 100644 index 0000000..f5638a4 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c @@ -0,0 +1,10 @@ + +#include "../kernel-test-runner.h" + +int pageableExport() { + return 1; +} + +int pageableExportDirect() { + return 42; +} diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c new file mode 100644 index 0000000..c5635c0 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c @@ -0,0 +1,25 @@ + +#include "../kernel-test-runner.h" + +extern int kernelExport(); +__typeof(&kernelExport) kernelExportPtr = &kernelExport; + +int bar() { + return kernelExportPtr() + 2; +} + +extern int kernelExportDirect(); + +// Test direct pointer fixups to the kernel. On x86_64 these would be emitted as just +// a branch relocation so we needed to synthesize a stub +__attribute__((constructor)) +int testDirectToKernel(const TestRunnerFunctions* funcs) { + LOG("testDirectToKernel(): start"); + // The kernel returned 42 + int v = kernelExportDirect(); + if ( v != 42 ) { + FAIL("kernelExportDirect() returned %d vs expected 42", v); + } + LOG("testDirectToKernel(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist new file mode 100644 index 0000000..e470d05 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + bar + CFBundleIdentifier + com.apple.bar + CFBundleName + bar + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.kernel.export + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist new file mode 100644 index 0000000..d7245d1 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist @@ -0,0 +1,23 @@ + + + + + CFBundleExecutable + foo + CFBundleIdentifier + com.apple.foo + CFBundleName + apfs + CFBundlePackageType + KEXT + OSBundleLibraries + + com.apple.bar + 1.0 + + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist new file mode 100644 index 0000000..3376dea --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleExecutable + kernel-export + CFBundleIdentifier + com.apple.kernel.export + CFBundleName + kernel-export + CFBundlePackageType + KEXT + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0.0 + + diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c new file mode 100644 index 0000000..3e5b437 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c @@ -0,0 +1,38 @@ + +#include "../kernel-test-runner.h" + +extern int bar(); +__typeof(&bar) barPtr = &bar; + +int foo() { + return barPtr() + 4; +} + +__attribute__((constructor)) +int test(const TestRunnerFunctions* funcs) { + LOG("test(): start"); + // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = foo(); + if ( v != 7 ) { + FAIL("foo() returned %d vs expected 7", v); + } + LOG("test(): end"); + return 0; +} + +int fooDirect() { + return bar() + 4; +} + +// Test direct pointer fixups, ie, not via a GOT +__attribute__((constructor)) +int testDirect(const TestRunnerFunctions* funcs) { + LOG("testDirect(): start"); + // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked + int v = fooDirect(); + if ( v != 7 ) { + FAIL("fooDirect() returned %d vs expected 7", v); + } + LOG("testDirect(): end"); + return 0; +} diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c new file mode 100644 index 0000000..3313ca8 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c @@ -0,0 +1,10 @@ + +#include "../kernel-test-runner.h" + +int kernelExport() { + return 1; +} + +int kernelExportDirect() { + return 42; +} diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/main.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/main.c new file mode 100644 index 0000000..7123263 --- /dev/null +++ b/testing/test-cases/kernel-pageablekc-fixups.dtest/main.c @@ -0,0 +1,87 @@ + +// BOOT_ARGS: amfi=3 cs_enforcement_disable=1 + +// Create the base kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/kernel-export.kext/Info.plist $BUILD_DIR/extensions/kernel-export-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination -ftrivial-auto-var-init=uninitialized +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC kernel-export.c -o $BUILD_DIR/extensions/kernel-export-kext/kernel-export -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe -extensions $BUILD_DIR/extensions -bundle-id com.apple.kernel.export $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/kernel-export + +// Create the pageable kernel collection +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -mkernel -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -mkernel -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination +// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e): $APP_CACHE_UTIL -create-pageable-kernel-collection $BUILD_DIR/pageable.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar + +// BUILD(watchos): + +// RUN_STATIC: $RUN_STATIC $RUN_DIR/kernel.kc $RUN_DIR/pageable.kc - - + +#include "../kernel-test-runner.h" +#include "../kernel-fixups.h" +#include "../kernel-classic-relocs.h" +#include "../kernel-helpers.h" + +#define printf(...) funcs->printf(__VA_ARGS__) + +int x = 1; +int *g = &x; + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +int _start(const TestRunnerFunctions* funcs) +{ + setFuncs(funcs); + + const void* slideBasePointers[4]; + slideBasePointers[0] = funcs->basePointers[0]; + slideBasePointers[1] = funcs->basePointers[1]; + slideBasePointers[2] = funcs->basePointers[2]; + slideBasePointers[3] = funcs->basePointers[3]; + int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[0] slide = %d\n", slideReturnCode); + return 0; + } + + int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf); + if ( slideClassicReturnCode != 0 ) { + FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode); + return 0; + } + + if ( g[0] != x ) { + FAIL("g[0] != x, %d != %d\n", g[0], x); + return 0; + } + + // First slide the pageableKC using the top level fixups. These handle the branch GOTs + slideReturnCode = slide(funcs->mhs[1], slideBasePointers, funcs->printf); + if ( slideReturnCode != 0 ) { + FAIL("mhs[1] slide = %d\n", slideReturnCode); + return 0; + } + + // Then slide pageable using the fixups attached to the kexts own mach headers + slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[1], slideBasePointers, funcs->printf, funcs); + if ( slideReturnCode != 0 ) { + FAIL("mhs[1] slide = %d\n", slideReturnCode); + return 0; + } + + // If we have any mod init funcs, then lets run them now + int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[1], funcs->printf, funcs); + if ( runModInitFuncs != 0 ) { + FAIL("runModInitFuncs = %d\n", runModInitFuncs); + return 0; + } + + PASS("Success"); + return 0; +} + + diff --git a/testing/test-cases/kernel-test-runner.h b/testing/test-cases/kernel-test-runner.h new file mode 100644 index 0000000..c5858d3 --- /dev/null +++ b/testing/test-cases/kernel-test-runner.h @@ -0,0 +1,63 @@ + +#if __has_feature(ptrauth_calls) +#include +#endif + +typedef unsigned long long uint64_t; +extern int printf(const char*, ...); +extern void exit(int); + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _PASS(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +__attribute__ ((noreturn)) +extern void _FAIL(const char* file, unsigned line, const char* format, ...); + +__attribute__((format(printf, 3, 4))) +extern void _LOG(const char* file, unsigned line, const char* format, ...); + +extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds); +#if __cplusplus +}; +#endif /* __cplusplus */ + +// The arm64e kernel/userland ABIs sign C function pointers differently. +// We aren't trying to test that ABI, so lets just force a different signature +#if __has_feature(ptrauth_calls) +# define PtrAuth __ptrauth(ptrauth_key_function_pointer, 1, 0xc671) +#else +# define PtrAuth +#endif + +typedef struct TestRunnerFunctions { + uint64_t version; + const struct mach_header* mhs[4]; + const void* basePointers[4]; + PtrAuth __typeof(&printf) printf; + PtrAuth __typeof(&exit) exit; + PtrAuth __typeof(&_PASS) testPass; + PtrAuth __typeof(&_FAIL) testFail; + PtrAuth __typeof(&_LOG) testLog; + PtrAuth __typeof(&_TIMEOUT) testTimeout; +} TestRunnerFunctions; + +static const TestRunnerFunctions* funcs = 0; + +#define PASS(...) funcs->testPass(__FILE__,__LINE__,__VA_ARGS__) +#define FAIL(...) funcs->testFail(__FILE__,__LINE__,__VA_ARGS__) +#define LOG(...) funcs->testLog(__FILE__,__LINE__,__VA_ARGS__) +#define TIMEOUT(seconds) funcs->testTimeout(__FILE__,__LINE__,seconds) + +#if __x86_64__ +__attribute__((section(("__HIB, __text")))) +#else +__attribute__((section(("__TEXT_EXEC, __text")))) +#endif +static inline void setFuncs(const TestRunnerFunctions* v) { + funcs = v; +} diff --git a/testing/test-cases/large-load-commands.dtest/extra.cmds b/testing/test-cases/large-load-commands.dtest/extra.cmds new file mode 100644 index 0000000..e4b7bce --- /dev/null +++ b/testing/test-cases/large-load-commands.dtest/extra.cmds @@ -0,0 +1,202 @@ +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/0 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/1 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/2 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/3 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/4 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/5 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/6 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/7 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/8 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/9 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/10 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/11 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/12 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/13 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/14 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/15 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/16 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/17 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/18 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/19 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/20 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/21 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/22 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/23 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/24 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/25 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/26 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/27 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/28 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/29 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/30 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/31 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/32 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/33 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/34 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/35 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/36 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/37 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/38 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/39 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/40 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/41 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/42 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/43 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/44 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/45 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/46 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/47 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/48 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/49 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/50 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/51 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/52 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/53 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/54 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/55 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/56 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/57 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/58 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/59 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/60 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/61 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/62 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/63 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/64 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/65 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/66 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/67 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/68 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/69 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/70 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/71 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/72 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/73 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/74 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/75 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/76 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/77 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/78 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/79 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/81 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/82 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/83 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/84 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/85 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/86 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/87 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/88 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/89 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/91 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/92 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/93 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/94 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/95 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/96 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/97 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/98 +-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/99 + + +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/0 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/1 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/2 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/3 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/4 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/5 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/6 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/7 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/8 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/9 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/10 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/11 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/12 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/13 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/14 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/15 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/16 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/17 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/18 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/19 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/20 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/21 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/22 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/23 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/24 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/25 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/26 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/27 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/28 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/29 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/30 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/31 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/32 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/33 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/34 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/35 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/36 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/37 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/38 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/39 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/40 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/41 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/42 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/43 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/44 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/45 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/46 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/47 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/48 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/49 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/50 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/51 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/52 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/53 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/54 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/55 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/56 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/57 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/58 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/59 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/60 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/61 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/62 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/63 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/64 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/65 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/66 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/67 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/68 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/69 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/70 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/71 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/72 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/73 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/74 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/75 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/76 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/77 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/78 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/79 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/81 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/82 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/83 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/84 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/85 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/86 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/87 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/88 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/89 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/91 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/92 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/93 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/94 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/95 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/96 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/97 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/98 +-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/99 diff --git a/testing/test-cases/large-load-commands.dtest/foo.c b/testing/test-cases/large-load-commands.dtest/foo.c new file mode 100644 index 0000000..e06aa42 --- /dev/null +++ b/testing/test-cases/large-load-commands.dtest/foo.c @@ -0,0 +1,7 @@ + +#include +#include + +void foo() {} + + diff --git a/testing/test-cases/large-load-commands.dtest/main.c b/testing/test-cases/large-load-commands.dtest/main.c new file mode 100644 index 0000000..017057e --- /dev/null +++ b/testing/test-cases/large-load-commands.dtest/main.c @@ -0,0 +1,17 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -Wl,@$SRC_DIR/extra.cmds +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/large-load-commands.exe + +// RUN: ./large-load-commands.exe + + + +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + PASS("Success"); +} diff --git a/testing/test-cases/launch-image-cache.dtest/main.c b/testing/test-cases/launch-image-cache.dtest/main.c index a4f4a1b..48fe6a8 100644 --- a/testing/test-cases/launch-image-cache.dtest/main.c +++ b/testing/test-cases/launch-image-cache.dtest/main.c @@ -45,6 +45,8 @@ #include +#include "test_support.h" + extern int foo1(); extern int foo2(); extern int foo3(); @@ -78,52 +80,46 @@ extern int foo30(); extern int foo31(); extern int foo32(); -int failedCheck(int i, int j) { +void failedCheck(int i, int j) { if (i != j) { - printf("[FAIL] launch-image-cache: expected %d but got %d\n", j, i); - return 1; + FAIL("expected %d but got %d", j, i); } - return 0; } -int main() -{ - printf("[BEGIN] launch-image-cache\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + failedCheck(foo1(), 1); + failedCheck(foo2(), 2); + failedCheck(foo3(), 3); + failedCheck(foo4(), 4); + failedCheck(foo5(), 5); + failedCheck(foo6(), 6); + failedCheck(foo7(), 7); + failedCheck(foo8(), 8); + failedCheck(foo9(), 9); + failedCheck(foo10(), 10); + failedCheck(foo11(), 11); + failedCheck(foo12(), 12); + failedCheck(foo13(), 13); + failedCheck(foo14(), 14); + failedCheck(foo15(), 15); + failedCheck(foo16(), 16); + failedCheck(foo17(), 17); + failedCheck(foo18(), 18); + failedCheck(foo19(), 19); + failedCheck(foo20(), 20); + failedCheck(foo21(), 21); + failedCheck(foo22(), 22); + failedCheck(foo23(), 23); + failedCheck(foo24(), 24); + failedCheck(foo25(), 25); + failedCheck(foo26(), 26); + failedCheck(foo27(), 27); + failedCheck(foo28(), 28); + failedCheck(foo29(), 29); + failedCheck(foo30(), 30); + failedCheck(foo31(), 31); + failedCheck(foo32(), 32); - if (failedCheck(foo1(), 1)) return 0; - if (failedCheck(foo2(), 2)) return 0; - if (failedCheck(foo3(), 3)) return 0; - if (failedCheck(foo4(), 4)) return 0; - if (failedCheck(foo5(), 5)) return 0; - if (failedCheck(foo6(), 6)) return 0; - if (failedCheck(foo7(), 7)) return 0; - if (failedCheck(foo8(), 8)) return 0; - if (failedCheck(foo9(), 9)) return 0; - if (failedCheck(foo10(), 10)) return 0; - if (failedCheck(foo11(), 11)) return 0; - if (failedCheck(foo12(), 12)) return 0; - if (failedCheck(foo13(), 13)) return 0; - if (failedCheck(foo14(), 14)) return 0; - if (failedCheck(foo15(), 15)) return 0; - if (failedCheck(foo16(), 16)) return 0; - if (failedCheck(foo17(), 17)) return 0; - if (failedCheck(foo18(), 18)) return 0; - if (failedCheck(foo19(), 19)) return 0; - if (failedCheck(foo20(), 20)) return 0; - if (failedCheck(foo21(), 21)) return 0; - if (failedCheck(foo22(), 22)) return 0; - if (failedCheck(foo23(), 23)) return 0; - if (failedCheck(foo24(), 24)) return 0; - if (failedCheck(foo25(), 25)) return 0; - if (failedCheck(foo26(), 26)) return 0; - if (failedCheck(foo27(), 27)) return 0; - if (failedCheck(foo28(), 28)) return 0; - if (failedCheck(foo29(), 29)) return 0; - if (failedCheck(foo30(), 30)) return 0; - if (failedCheck(foo31(), 31)) return 0; - if (failedCheck(foo32(), 32)) return 0; - - printf("[PASS] launch-image-cache\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/lazy-symbol-missing.dtest/main-call.c b/testing/test-cases/lazy-symbol-missing.dtest/main-call.c index 6648a12..d2959c3 100644 --- a/testing/test-cases/lazy-symbol-missing.dtest/main-call.c +++ b/testing/test-cases/lazy-symbol-missing.dtest/main-call.c @@ -2,8 +2,12 @@ #include - +#ifdef WEAK +__attribute__((weak_import)) extern int slipperySymbol(); +#else +extern int slipperySymbol(); +#endif int main(int argc, const char* argv[]) diff --git a/testing/test-cases/lazy-symbol-missing.dtest/main.c b/testing/test-cases/lazy-symbol-missing.dtest/main.c index a6c14e6..0bb6540 100644 --- a/testing/test-cases/lazy-symbol-missing.dtest/main.c +++ b/testing/test-cases/lazy-symbol-missing.dtest/main.c @@ -1,23 +1,33 @@ // note: -Os is needed for armv7 to work around compiler issue where at -O0 it computes pointer to function and calls that // BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo-present.dylib -install_name $RUN_DIR/libfoo.dylib -DHAS_SYMBOL=1 -// BUILD: $CC main.c -o $BUILD_DIR/lazy-symbol-missing.exe $TEMP_DIR/libfoo-present.dylib -Os +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo-present.dylib -install_name $RUN_DIR/libfoo.dylib -DHAS_SYMBOL=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libbar-missing.dylib -install_name $RUN_DIR/libbar-missing.dylib -DHAS_SYMBOL=1 +// BUILD: $CC main.c -o $BUILD_DIR/lazy-symbol-missing.exe $BUILD_DIR/libfoo-present.dylib -Os // BUILD: $CC main.c -o $BUILD_DIR/lazy-symbol-missing-flat.exe -undefined dynamic_lookup -Os -DFLAT=1 -// BUILD: $CC main-call.c -o $BUILD_DIR/lazy-symbol-missing-called.exe $TEMP_DIR/libfoo-present.dylib -Os -// BUILD: $CC runner.c -o $BUILD_DIR/lazy-symbol-runner.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $CC main-call.c -o $BUILD_DIR/lazy-symbol-missing-called.exe $BUILD_DIR/libfoo-present.dylib -Os +// BUILD: $CC main-call.c -o $BUILD_DIR/lazy-symbol-missing-called-weak-lib.exe $BUILD_DIR/libbar-missing.dylib -Os -DWEAK=1 +// BUILD: $CXX runner.cpp -o $BUILD_DIR/lazy-symbol-runner.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $CXX runner.cpp -o $BUILD_DIR/lazy-symbol-runner-weak-lib.exe -DRUN_DIR="$RUN_DIR" -DWEAK=1 + +// BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo-present.dylib +// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar-missing.dylib // NO_CRASH_LOG: lazy-symbol-missing-called.exe +// NO_CRASH_LOG: lazy-symbol-missing-called-weak-lib.exe // RUN: ./lazy-symbol-missing.exe // RUN: ./lazy-symbol-runner.exe // RUN: ./lazy-symbol-missing-flat.exe +// RUN: ./lazy-symbol-runner-weak-lib.exe #include #include #include +#include "test_support.h" + #if __LP64__ extern struct mach_header_64 __dso_handle; #else @@ -32,10 +42,7 @@ extern int slipperySymbol(); #define TESTNAME "lazy-symbol-missing" #endif -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] " TESTNAME "\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { #if __arm64e__ // arm64e always uses chained binds which does not support lazy binding bool supportsLazyBinding = false; @@ -55,9 +62,6 @@ int main(int argc, const char* argv[]) if ( argc < 0 ) slipperySymbol(); } - - printf("[PASS] " TESTNAME "\n"); - - return 0; + PASS("%s", TESTNAME); } diff --git a/testing/test-cases/lazy-symbol-missing.dtest/runner.c b/testing/test-cases/lazy-symbol-missing.dtest/runner.c deleted file mode 100644 index 5aaeff3..0000000 --- a/testing/test-cases/lazy-symbol-missing.dtest/runner.c +++ /dev/null @@ -1,101 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if __arm64e__ - // arm64e uses chained binds which does not support lazy binding - #define SUPPORTS_LAZY_BINDING 0 -#else - #define SUPPORTS_LAZY_BINDING 1 -#endif - - -static bool sSignalCaught = false; -static bool sChildAbortInDyld = false; -static pid_t sChildPid = 0; - - -static void childDied(int sig) -{ - sSignalCaught = true; - //printf("sigchld for pid=%d\n", sChildPid); - - struct proc_exitreasoninfo info; - bzero(&info, sizeof(info)); - uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; - bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); - info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; - info.eri_kcd_buf = (user_addr_t)packReasonData; - //fprintf(stderr, "info=%p\n", &info); - 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 (%d) != OS_REASON_DYLD\n", info.eri_namespace); - return; - } - - sChildAbortInDyld = true; -} - - -bool runTest(const char* prog) -{ - sSignalCaught = false; - sChildAbortInDyld = false; - - // fork and exec child - sChildPid = fork(); - if ( sChildPid < 0 ) - err(EXIT_FAILURE, "fork"); - if ( sChildPid == 0 ) { - // child side - char* childArgv[] = { (char*)prog, NULL }; - int result = execvp(prog, childArgv); - err(EXIT_FAILURE, "exec(\"%s\",...)", prog); - } - for(int i=0; i < 10; ++i) { - if ( sSignalCaught ) - break; - sleep(1); - } - - return sChildAbortInDyld; -} - - -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] lazy-symbol-missing and called\n"); - -#if SUPPORTS_LAZY_BINDING - // set up signal handler for catching child terminations - signal(SIGCHLD, childDied); - - // test launch program with missing library - runTest(RUN_DIR "/lazy-symbol-missing-called.exe"); - - if ( sSignalCaught && sChildAbortInDyld ) - printf("[PASS] lazy-symbol-missing and called\n"); - else - printf("[FAIL] lazy-symbol-missing and called\n"); -#else - printf("[PASS] lazy-symbol-missing and called\n"); -#endif - return 0; -} - diff --git a/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp b/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp new file mode 100644 index 0000000..d7c15a3 --- /dev/null +++ b/testing/test-cases/lazy-symbol-missing.dtest/runner.cpp @@ -0,0 +1,84 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __arm64e__ + // arm64e uses chained binds which does not support lazy binding + #define SUPPORTS_LAZY_BINDING 0 +#else + #define SUPPORTS_LAZY_BINDING 1 +#endif + +#include "test_support.h" + +static const char* getUserDescription(struct proc_exitreasoninfo& info) { + kcdata_iter_t iter = kcdata_iter((void*)info.eri_kcd_buf, info.eri_reason_buf_size); + if ( !kcdata_iter_valid(iter) ) + return NULL; + if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ) + return NULL; + iter = kcdata_iter_find_type(iter, EXIT_REASON_USER_DESC); + if ( !kcdata_iter_valid(iter) ) { + return NULL; + } + return kcdata_iter_string(iter, 0); +} + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { +#if SUPPORTS_LAZY_BINDING + _process process; +#ifdef WEAK + process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called-weak-lib.exe"); +#else + process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called.exe"); +#endif + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + process.set_exit_handler(^(pid_t pid) { + LOG("Child exited pid=%d", pid); + + struct proc_exitreasoninfo info; + bzero(&info, sizeof(info)); + uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; + bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); + info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; + info.eri_kcd_buf = (user_addr_t)packReasonData; + LOG("info=%p", &info); + int procResult = proc_pidinfo(pid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE); + if ( procResult != sizeof(struct proc_exitreasoninfo) ) { + FAIL("bad return size from proc_pidinfo(), %d expected %lu", procResult, PROC_PIDEXITREASONINFO_SIZE); + } + if ( info.eri_namespace != OS_REASON_DYLD ) { + FAIL("eri_namespace (%d) != OS_REASON_DYLD", info.eri_namespace); + } + const char* userDesc = getUserDescription(info); + if ( userDesc != NULL ) { + LOG("user desc=%s", (const char*)userDesc); + } +#ifdef WEAK + // Make sure we print a dylib name, not a dependency library # when referencing a symbol from a missing library + if ( userDesc != NULL ) { + if ( strstr(userDesc, "libbar-missing.dylib") == NULL ) { + FAIL("Expected name of missing dylib"); + } + } +#endif + PASS("Success"); + }); + (void)process.launch(); +#endif + PASS("Success"); +} + diff --git a/testing/test-cases/libdsc.dtest/main.c b/testing/test-cases/libdsc.dtest/main.c new file mode 100644 index 0000000..9fecb97 --- /dev/null +++ b/testing/test-cases/libdsc.dtest/main.c @@ -0,0 +1,40 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/libdsc-test.exe -ldsc + +// RUN: ./libdsc-test.exe + +#include +#include +#include +#include +#include + +#include "test_support.h" +#include "shared-cache/dsc_iterator.h" + +// This program links libdsc.a and verifies that dyld_shared_cache_iterate() works + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + size_t cacheLen; + const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen); + + if ( cacheStart != NULL ) { + dyld_shared_cache_iterate(cacheStart, cacheLen, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + if ( false ) { + printf("%p %s\n", dylibInfo->machHeader, dylibInfo->path); + printf(" dylib.version=%d\n", dylibInfo->version); + printf(" dylib.isAlias=%d\n", dylibInfo->isAlias); + printf(" dylib.inode=%lld\n", dylibInfo->inode); + printf(" dylib.modTime=%lld\n", dylibInfo->modTime); + printf(" segment.name= %s\n", segInfo->name); + printf(" segment.fileOffset= 0x%08llX\n", segInfo->fileOffset); + printf(" segment.fileSize= 0x%08llX\n", segInfo->fileSize); + printf(" segment.address= 0x%08llX\n", segInfo->address); + printf(" segment.addressOffset=0x%08llX\n", segInfo->addressOffset); + } + }); + } + + PASS("Success"); +} diff --git a/testing/test-cases/macOS-cache-rebuild.dtest/main.c b/testing/test-cases/macOS-cache-rebuild.dtest/main.c deleted file mode 100644 index d60aee3..0000000 --- a/testing/test-cases/macOS-cache-rebuild.dtest/main.c +++ /dev/null @@ -1,43 +0,0 @@ -// BUILD_ONLY: MacOSX - -// BUILD: $CC main.c -o $BUILD_DIR/rebuild-dyld-cache.exe - -// RUN: ./rebuild-dyld-cache.exe - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char** environ; - -int main() -{ - printf("[BEGIN] macOS-cache-rebuild\n"); - - const char* argv[] = { "/usr/bin/update_dyld_shared_cache", "-cache_dir", "/tmp/", NULL }; - pid_t child; - int psResult = posix_spawn(&child, "/usr/bin/update_dyld_shared_cache", NULL, NULL, (char**)argv, environ); - if ( psResult != 0 ) { - printf("[FAIL] macOS-cache-rebuild: posix_spawn failed, err=%d\n", psResult); - return 0; - } - - int childStatus; - (void)wait4(child, &childStatus, 0, NULL); - if (WIFEXITED(childStatus) == 0) - printf("[FAIL] macOS-cache-rebuild: update_dyld_shared_cache did not exit\n"); - else if (WEXITSTATUS(childStatus) != 0) - printf("[FAIL] macOS-cache-rebuild: update_dyld_shared_cache failed\n"); - else - printf("[PASS] macOS-cache-rebuild\n"); - - return 0; -} - diff --git a/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp b/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp new file mode 100644 index 0000000..adff7a5 --- /dev/null +++ b/testing/test-cases/macOS-cache-rebuild.dtest/main.cpp @@ -0,0 +1,43 @@ +// BUILD(macos): $CXX main.cpp -o $BUILD_DIR/rebuild-dyld-cache.exe + +// BUILD(ios,tvos,watchos,bridgeos): + +// FIXME: This test will not make sense when remove update_dyld_shared_cache, and the functionality will be subsummed by root testing +// ./rebuild-dyld-cache.exe + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_support.h" + +extern char** environ; + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + _process process; + process.set_executable_path("/usr/bin/update_dyld_shared_cache"); + const char* env[] = { "TEST_OUTPUT=None", NULL}; + process.set_env(env); + const char* args[] = { "-cache_dir", "/tmp/", NULL }; + process.set_args(args); + process.set_exit_handler(^(pid_t pid) { + int childStatus; + (void)wait4(pid, &childStatus, 0, NULL); + if (WIFEXITED(childStatus) == 0) + FAIL("update_dyld_shared_cache did not exit"); + else if (WEXITSTATUS(childStatus) != 0) + FAIL("update_dyld_shared_cache failed"); + else + PASS("Success"); + }); + + process.launch(); +} + diff --git a/testing/test-cases/missing-weak-def.dtest/bar.c b/testing/test-cases/missing-weak-def.dtest/bar.c index 55c56b1..92c8403 100644 --- a/testing/test-cases/missing-weak-def.dtest/bar.c +++ b/testing/test-cases/missing-weak-def.dtest/bar.c @@ -1,4 +1,5 @@ +__attribute__((weak)) int bar() { return 0; -} \ No newline at end of file +} diff --git a/testing/test-cases/missing-weak-def.dtest/main.c b/testing/test-cases/missing-weak-def.dtest/main.c index b4921cd..c6b06d7 100644 --- a/testing/test-cases/missing-weak-def.dtest/main.c +++ b/testing/test-cases/missing-weak-def.dtest/main.c @@ -1,7 +1,9 @@ -// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $TEMP_DIR/libbar.dylib -// BUILD: $CC bar-empty.c -dynamiclib -install_name $BUILD_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -// BUILD: $CC main.c $TEMP_DIR/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/missing/libbar.dylib +// BUILD: $CC bar-empty.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC main.c $BUILD_DIR/missing/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe + +// BUILD: $SKIP_INSTALL $BUILD_DIR/missing/libbar.dylib // RUN: ./missing-weak-def.exe @@ -11,20 +13,17 @@ #include #include -__attribute__((weak)) +#include "test_support.h" + __attribute__((weak_import)) int bar(); -int main() -{ - printf("[BEGIN] missing-weak-def\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if (&bar) { - printf("[FAIL] missing-weak-def\n"); - return 0; + bar(); + FAIL("bar from libbar not bound"); } - printf("[PASS] missing-weak-def\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/no-shared-cache.dtest/main.c b/testing/test-cases/no-shared-cache.dtest/main.c deleted file mode 100644 index 9407740..0000000 --- a/testing/test-cases/no-shared-cache.dtest/main.c +++ /dev/null @@ -1,31 +0,0 @@ -// 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 -#include -#include -#include -#include - - -// 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; -} - diff --git a/testing/test-cases/operator-new.dtest/main.cxx b/testing/test-cases/operator-new.dtest/main.cxx index 1773a03..a6d0c4a 100644 --- a/testing/test-cases/operator-new.dtest/main.cxx +++ b/testing/test-cases/operator-new.dtest/main.cxx @@ -3,10 +3,9 @@ // RUN: ./operator-new.exe -#include #include - +#import "test_support.h" // // This test case verifies that calling operator new[] in libstdc++.dylib @@ -35,35 +34,28 @@ void operator delete(void* p) throw() ::free(p); } -int main() -{ - printf("[BEGIN] operator-new\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // test that OS's operator new[] redirects to my operator new myLastNewAllocation = NULL; char* stuff = new char[24]; if ( (void*)stuff != myLastNewAllocation ) { - printf("[FAIL] operator-new system array allocator not redirected through my operator new\n"); - return 0; + FAIL("system array allocator not redirected through my operator new"); } // 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; + FAIL("allocation not redirected though my operator new"); } // delete foo; if ( (void*)foo != myLastDelete ) { - printf("[FAIL] operator-new deallocation not redirected though my operator delete\n"); - return 0; + FAIL("deallocation not redirected though my operator delete"); } - printf("[PASS] operator-new\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/read-only-data.dtest/main.c b/testing/test-cases/read-only-data.dtest/main.c index 63e43ef..a4797ee 100644 --- a/testing/test-cases/read-only-data.dtest/main.c +++ b/testing/test-cases/read-only-data.dtest/main.c @@ -17,6 +17,8 @@ #include #include +#include "test_support.h" + extern bool isReadOnly(const void* addr); extern bool testLib(); @@ -24,13 +26,11 @@ const void* const funcs[] = { &malloc, &free, &strcmp, &printf }; typedef bool (*TestFunc)(void); -static bool sBadImage = false; - static void notify(const struct mach_header* mh, intptr_t slide) { // only look at images not in dyld shared cache if ( (mh->flags & 0x80000000) == 0 ) { - //fprintf(stderr, "mh=%p flags=0x%08X\n", mh, mh->flags); + LOG("mh=%p flags=0x%08X", mh, mh->flags); const char* path = dyld_image_path_containing_address(mh); bool inTestDir = (strstr(path, RUN_DIR) != NULL); unsigned long size; @@ -40,27 +40,21 @@ static void notify(const struct mach_header* mh, intptr_t slide) uint8_t* p = getsectiondata(mh, "__DATA_CONST", "__const", &size); #endif if ( (p != NULL) && inTestDir && !isReadOnly(p) ) { - printf("[FAIL] read-only-data __DATA_CONST,__const section not read-only in %p %s\n", mh, path); - sBadImage = true; + FAIL("read-only-data __DATA_CONST,__const section not read-only in %p %s", mh, path); } } } -int main() -{ - printf("[BEGIN] read-only-data\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // test __DATA_CONST in main is read-only if ( !isReadOnly(&funcs[2]) ) { - printf("[FAIL] read-only-data main executable not read-only\n"); - return 0; + FAIL("main executable not read-only"); } // test __DATA_CONST in linked dylib is read-only if ( !testLib() ) { - printf("[FAIL] read-only-data librotest.dylib not read-only\n"); - return 0; + FAIL("librotest.dylib not read-only"); } _dyld_register_func_for_add_image(¬ify); @@ -68,18 +62,13 @@ int main() // test __DATA_CONST in dlopen'ed bundle is read-only void* h = dlopen(RUN_DIR "/test.bundle", 0); if ( h == NULL ) { - printf("[FAIL] read-only-data test.bundle not loaded\n"); - return 0; + FAIL("test.bundle not loaded"); } TestFunc tb = (TestFunc)dlsym(h, "testBundle"); if ( !tb() ) { - printf("[FAIL] read-only-data test.bundle not read-only\n"); - return 0; + FAIL("test.bundle not read-only"); } - if ( !sBadImage ) - printf("[PASS] read-only-data\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/restrict-search.dtest/main.c b/testing/test-cases/restrict-search.dtest/main.c index e8355ca..3994076 100644 --- a/testing/test-cases/restrict-search.dtest/main.c +++ b/testing/test-cases/restrict-search.dtest/main.c @@ -1,14 +1,11 @@ -// BUILD_ONLY: MacOSX - -// BUILD: mkdir $BUILD_DIR/lc -// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib -// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1 -// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null -// BUILD: mkdir $BUILD_DIR/rpath -// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib -// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1 -// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null +// BUILD(macos): $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib +// BUILD(macos): $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1 +// BUILD(macos): $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null +// BUILD(macos): $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib +// BUILD(macos): $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1 +// BUILD(macos): $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./restrict-search-lc-find.exe // RUN: ./restrict-search-lc-no-find.exe @@ -20,6 +17,8 @@ #include #include +#include "test_support.h" + // Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT) // These should work for non-restrictured programs. // These should fail for restricted programs. @@ -33,20 +32,18 @@ extern int foo() __attribute__((weak_import)); #define STRINGIFY(x) STRINGIFY2(x) -int main(int argc, const char* argv[]) -{ - printf("[BEGIN] restrict-search %s\n", STRINGIFY(MODE)); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { #if SHOULD_BE_FOUND if ( &foo == NULL ) - printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); + FAIL("Incorrectly found %s", STRINGIFY(MODE)); else - printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); + PASS("Incorrectly did not find %s", STRINGIFY(MODE)); #else // dylib won't be found at runtime, so &foo should be NULL if ( &foo == NULL ) - printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); + PASS("Found %s", STRINGIFY(MODE)); else - printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); + FAIL("Could not find %s", STRINGIFY(MODE)); #endif return 0; diff --git a/testing/test-cases/rpath-absolute.dtest/main.c b/testing/test-cases/rpath-absolute.dtest/main.c index f37c78e..93b2305 100644 --- a/testing/test-cases/rpath-absolute.dtest/main.c +++ b/testing/test-cases/rpath-absolute.dtest/main.c @@ -12,14 +12,13 @@ #include +#include "test_support.h" + extern int foo; -int main() -{ - printf("[BEGIN] rpath-absolute.exe\n"); - printf("[PASS] rpath-absolute.exe\n"); - return 0; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + PASS("Success"); } diff --git a/testing/test-cases/rpath-relative.dtest/foo.c b/testing/test-cases/rpath-relative.dtest/foo.c new file mode 100644 index 0000000..723758f --- /dev/null +++ b/testing/test-cases/rpath-relative.dtest/foo.c @@ -0,0 +1 @@ +int foo = 42; diff --git a/testing/test-cases/rpath-relative.dtest/main.c b/testing/test-cases/rpath-relative.dtest/main.c new file mode 100644 index 0000000..2779b7b --- /dev/null +++ b/testing/test-cases/rpath-relative.dtest/main.c @@ -0,0 +1,29 @@ + +// BOOT_ARGS: dyld_flags=2 + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/librel.dylib -install_name @rpath/librel.dylib +// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable.exe -rpath @executable_path +// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-executable-slash.exe -rpath @executable_path/ +// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader.exe -rpath @loader_path +// BUILD: $CC main.c $BUILD_DIR/librel.dylib -o $BUILD_DIR/rpath-loader-slash.exe -rpath @loader_path/ + +// RUN: ./rpath-executable.exe +// RUN: ./rpath-executable-slash.exe +// RUN: ./rpath-loader.exe +// RUN: ./rpath-loader-slash.exe + +// main prog links with librel.dylib. There are four variants of how LC_RPATH is set up. + +#include + +#include "test_support.h" + +extern char* __progname; + + +int main(int argc, const char* argv[]) +{ + PASS("%s", __progname); +} + + diff --git a/testing/test-cases/rpath-weak-missing.dtest/main.c b/testing/test-cases/rpath-weak-missing.dtest/main.c index 4ed1aa6..bfc258e 100644 --- a/testing/test-cases/rpath-weak-missing.dtest/main.c +++ b/testing/test-cases/rpath-weak-missing.dtest/main.c @@ -1,10 +1,12 @@ // BOOT_ARGS: dyld_flags=2 -// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib -// BUILD: $CC foo.c -dynamiclib -Wl,-weak_library,$TEMP_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name @rpath/libmissing.dylib +// BUILD: $CC foo.c -dynamiclib -Wl,-weak_library,$BUILD_DIR/libmissing.dylib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -rpath $RUN_DIR // BUILD: $CC main.c -o $BUILD_DIR/rpath-weak-missing.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib + // RUN: ./rpath-weak-missing.exe // RUN: DYLD_AMFI_FAKE=0 ./rpath-weak-missing.exe @@ -13,19 +15,16 @@ #include #include +#include "test_support.h" int main() { - printf("[BEGIN] rpath-weak-missing\n"); - void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s\n", RUN_DIR, dlerror()); - return 0; + FAIL("rpath-weak-missing dlopen(\"%s/libfoo.dylib\") - %s", RUN_DIR, dlerror()); } - printf("[PASS] rpath-weak-missing\n"); - return 0; + PASS("rpath-weak-missing"); } diff --git a/testing/test-cases/shared_cache_iterate.dtest/main.c b/testing/test-cases/shared_cache_iterate.dtest/main.c index 2327d3d..a6058f6 100644 --- a/testing/test-cases/shared_cache_iterate.dtest/main.c +++ b/testing/test-cases/shared_cache_iterate.dtest/main.c @@ -18,7 +18,7 @@ #include #endif - +#include "test_support.h" struct dyld_cache_header { @@ -61,10 +61,7 @@ static void forEachCacheInDir(const char* dirPath, void (^handler)(const uuid_t } -int main() -{ - printf("[BEGIN] shared_cache_iterate\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { size_t cacheLen; const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen); uuid_t currentCacheUUID; @@ -80,16 +77,13 @@ int main() ++count; }); if ( result != 0 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() returned non-zero: %d\n", result); - return 0; + FAIL("dyld_shared_cache_iterate_text() returned non-zero: %d", result); } if ( count < 100 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() iterated over less than 100 images: %d\n", count); - return 0; + FAIL("dyld_shared_cache_iterate_text() iterated over less than 100 images: %d", count); } if ( badVersion ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_iterate_text() some dyld_shared_cache_dylib_text_info was not 2\n"); - return 0; + FAIL("dyld_shared_cache_iterate_text() some dyld_shared_cache_dylib_text_info was not 2"); } // iterate current cache @@ -102,16 +96,13 @@ int main() ++count; }); if ( result != 0 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() returned non-zero: %d\n", result); - return 0; + FAIL("dyld_shared_cache_find_iterate_text() returned non-zero: %d", result); } if ( count < 100 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d\n", count); - return 0; + FAIL("dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d", count); } if ( badVersion ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() some dyld_shared_cache_dylib_text_info was not 2\n"); - return 0; + FAIL("dyld_shared_cache_find_iterate_text() some dyld_shared_cache_dylib_text_info was not 2"); } // look for non-existent cache @@ -123,26 +114,22 @@ int main() ++count; }); if ( result == 0 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d\n", result); - return 0; + FAIL("dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d", result); } if ( count != 0 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected iteration count is zero: %d\n", count); - return 0; + FAIL("dyld_shared_cache_find_iterate_text() expected iteration count is zero: %d", count); } // find other cache const char* curCachePath = dyld_shared_cache_file_path(); if ( curCachePath == NULL ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_file_path() returned NULL\n"); - return 0; + FAIL("dyld_shared_cache_file_path() returned NULL"); } char cacheDir[PATH_MAX]; strlcpy(cacheDir, curCachePath, PATH_MAX); char* end = strrchr(cacheDir, '/'); if ( end == NULL ) { - printf("[FAIL] shared_cache_iterate cache path has no '/'\n"); - return 0; + FAIL("cache path has no '/'"); } *end = '\0'; forEachCacheInDir(cacheDir, ^(const uuid_t uuid) { @@ -152,22 +139,19 @@ int main() ++count; }); if ( res != 0 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d\n", result); - exit(0); + FAIL("dyld_shared_cache_find_iterate_text() expected result to be nonzero: %d", result); } if ( count < 100 ) { - printf("[FAIL] shared_cache_iterate dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d\n", count); - exit(0); + FAIL("dyld_shared_cache_find_iterate_text() iterated over less than 100 images: %d", count); } } }); } else { - printf("no dyld cache\n"); + LOG("no dyld cache"); } - printf("[PASS] shared_cache_iterate\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/shared_cache_optimized.dtest/main.c b/testing/test-cases/shared_cache_optimized.dtest/main.c index df44b5e..2d8f4e6 100644 --- a/testing/test-cases/shared_cache_optimized.dtest/main.c +++ b/testing/test-cases/shared_cache_optimized.dtest/main.c @@ -9,17 +9,13 @@ #include #include +#include "test_support.h" -int main() -{ - printf("[BEGIN] shared_cache_optimized\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // tests run on internal installs which use un-optimzed dyld cache if ( _dyld_shared_cache_optimized() ) - printf("[FAIL] shared_cache_optimized unexpectedly returned true\n"); + FAIL("unexpectedly returned true"); else - printf("[PASS] shared_cache_optimized\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/shared_cache_range.dtest/main.c b/testing/test-cases/shared_cache_range.dtest/main.c index 1e08b62..89ddaf6 100644 --- a/testing/test-cases/shared_cache_range.dtest/main.c +++ b/testing/test-cases/shared_cache_range.dtest/main.c @@ -13,6 +13,8 @@ #include #endif +#include "test_support.h" + static const void *stripPointer(const void *ptr) { #if __has_feature(ptrauth_calls) return __builtin_ptrauth_strip(ptr, ptrauth_key_asia); @@ -22,50 +24,41 @@ static const void *stripPointer(const void *ptr) { } -int main() -{ - printf("[BEGIN] shared_cache_range\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // see if image containing malloc is in the dyld cache Dl_info info; if ( dladdr(&malloc, &info) == 0 ) { - printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) failed"); - return 0; + FAIL("shared_cache_range: dladdr(&malloc, xx) fail"); } const struct mach_header* mh = (struct mach_header*)info.dli_fbase; - printf("image with malloc=%p\n", mh); + LOG("image with malloc=%p", mh); if ( mh == NULL ) { - printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) => dli_fbase==NULL"); - return 0; + FAIL("shared_cache_range: dladdr(&malloc, xx) => dli_fbase==NULL"); } bool haveSharedCache = (mh->flags & 0x80000000); - printf("haveSharedCache=%d\n", haveSharedCache); + LOG("haveSharedCache=%d", haveSharedCache); size_t cacheLen; const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen); if ( haveSharedCache ) { if ( cacheStart == NULL ) { - printf("[FAIL] _dyld_get_shared_cache_range() returned NULL even though we have a cache\n"); - return 0; + FAIL("_dyld_get_shared_cache_range() returned NULL even though we have a cache"); } - printf("shared cache start=%p, len=0x%0lX\n", cacheStart, cacheLen); + LOG("shared cache start=%p, len=0x%0lX", cacheStart, cacheLen); const void* cacheEnd = (char*)cacheStart + cacheLen; // verify malloc is in shared cache if ( (stripPointer((void*)&malloc) < cacheStart) || (stripPointer((void*)&malloc) > cacheEnd) ) { - printf("[FAIL] shared_cache_range: malloc is outside range of cache\n"); - return 0; + FAIL("shared_cache_range: malloc is outside range of cache"); } } else { if ( cacheStart != NULL ) { - printf("[FAIL] _dyld_get_shared_cache_range() returned non-NULL even though we don't have a cache\n"); - return 0; + FAIL("returned non-NULL even though we don't have a cache"); } } - printf("[PASS] shared_cache_range\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/static-terminators.dtest/base.c b/testing/test-cases/static-terminators.dtest/base.c index 847dfef..388cb84 100644 --- a/testing/test-cases/static-terminators.dtest/base.c +++ b/testing/test-cases/static-terminators.dtest/base.c @@ -1,7 +1,8 @@ #include -#include #include +#include "test_support.h" + static bool mainCalled = false; static bool libCalled = false; static bool libCalledBeforeMain = false; @@ -23,12 +24,12 @@ static __attribute__((destructor)) void myTerm() { if ( !mainCalled ) - printf("[FAIL] static-terminators, main's terminator not called\n"); + FAIL("main's terminator not called"); else if ( !libCalled ) - printf("[FAIL] static-terminators, libDynamic's terminator not called\n"); + FAIL("libDynamic's terminator not called"); else if ( !libCalledBeforeMain ) - printf("[FAIL] static-terminators, libDynamic's terminator called out of order\n"); + FAIL("libDynamic's terminator called out of order"); else - printf("[PASS] static-terminators\n"); + PASS("Success"); } diff --git a/testing/test-cases/static-terminators.dtest/foo.c b/testing/test-cases/static-terminators.dtest/foo.c index 4afcee7..5da79d5 100644 --- a/testing/test-cases/static-terminators.dtest/foo.c +++ b/testing/test-cases/static-terminators.dtest/foo.c @@ -1,6 +1,7 @@ #include #include +#include "test_support.h" extern void libDynamicTerminated(); @@ -8,7 +9,7 @@ extern void libDynamicTerminated(); static __attribute__((destructor)) void myTerm() { - //fprintf(stderr, "foo static terminator\n"); + LOG("foo static terminator"); libDynamicTerminated(); } diff --git a/testing/test-cases/static-terminators.dtest/main.c b/testing/test-cases/static-terminators.dtest/main.c index 3e6d3ac..7df67c8 100644 --- a/testing/test-cases/static-terminators.dtest/main.c +++ b/testing/test-cases/static-terminators.dtest/main.c @@ -10,6 +10,7 @@ #include #include +#include "test_support.h" // verify all static terminators run in proper order @@ -19,24 +20,18 @@ extern void mainTerminated(); static __attribute__((destructor)) void myTerm() { - //fprintf(stderr, "main's static terminator\n"); + LOG("main's static terminator"); mainTerminated(); } -int main() -{ - printf("[BEGIN] static-terminators\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // load dylib void* handle = dlopen(RUN_DIR "/libdynamic.dylib", RTLD_LAZY); if ( handle == NULL ) { - printf("[FAIL] dlclose-static-terminator: libdynamic.dylib could not be loaded, %s\n", dlerror()); - return 0; + FAIL("libdynamic.dylib could not be loaded, %s", dlerror()); } // PASS is printed in libbase.dylib terminator - - return 0; } diff --git a/testing/test-cases/symbol-resolver-basic.dtest/main.c b/testing/test-cases/symbol-resolver-basic.dtest/main.c index cebb529..17af5fa 100644 --- a/testing/test-cases/symbol-resolver-basic.dtest/main.c +++ b/testing/test-cases/symbol-resolver-basic.dtest/main.c @@ -11,26 +11,27 @@ #include #include +#include "test_support.h" + extern int foo(); extern int fooPlusOne(); -int main() -{ +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { #if TEN if ( foo() != 10 ) - printf("[FAIL] symbol-resolver-basic: foo() != 10\n"); + FAIL("foo() != 10"); else if ( fooPlusOne() != 11 ) - printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 11\n"); + FAIL("fooPlusOne() != 11"); else - printf("[PASS] symbol-resolver-basic\n"); + PASS("Success"); #else if ( foo() != 0 ) - printf("[FAIL] symbol-resolver-basic: foo() != 0\n"); + FAIL("foo() != 0"); else if ( fooPlusOne() != 1 ) - printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 1\n"); + FAIL("fooPlusOne() != 1"); else - printf("[PASS] symbol-resolver-basic\n"); + PASS("Success"); #endif return 0; diff --git a/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp b/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp index 5989115..e2319bf 100644 --- a/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp +++ b/testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp @@ -1,6 +1,6 @@ -// BUILD_ONLY: MacOSX +// BUILD(macos): $CXX main.cpp -std=c++11 -o $BUILD_DIR/thread-local-atexit-macOS.exe -// BUILD: $CXX main.cpp -std=c++11 -o $BUILD_DIR/thread-local-atexit-macOS.exe +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./thread-local-atexit-macOS.exe @@ -8,6 +8,8 @@ #include #include +#include "test_support.h" + // We create an A and a B. // While destroying B we create a C // Given that tlv_finalize has "destroy in reverse order of construction", we @@ -43,15 +45,14 @@ State state; A::A() { if ( state != None ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'None' state\n"); + FAIL("should be in the 'None' state"); } state = ConstructedA; } B::B() { if ( state != ConstructedA ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedA' state\n"); - _Exit(0); + FAIL("should be in the 'ConstructedA' state"); } state = ConstructedB; } @@ -59,8 +60,7 @@ B::B() { C::C() { // We construct C during B's destructor if ( state != DestroyingB ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyingB' state\n"); - _Exit(0); + FAIL("should be in the 'DestroyingB' state"); } state = ConstructedC; } @@ -68,14 +68,12 @@ C::C() { // We destroy B first B::~B() { if ( state != ConstructedB ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedB' state\n"); - _Exit(0); + FAIL("should be in the 'ConstructedB' state"); } state = DestroyingB; static thread_local C c; if ( state != ConstructedC ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'ConstructedC' state\n"); - _Exit(0); + FAIL("should be in the 'ConstructedC' state"); } state = DestroyedB; } @@ -83,8 +81,7 @@ B::~B() { // Then we destroy C C::~C() { if ( state != DestroyedB ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyedB' state\n"); - _Exit(0); + FAIL("should be in the 'DestroyedB' state"); } state = DestroyedC; } @@ -92,11 +89,10 @@ C::~C() { // And finally destroy A A::~A() { if ( state != DestroyedC ) { - printf("[FAIL] thread-local-atexit-macOS: should be in the 'DestroyedC' state\n"); - _Exit(0); + FAIL("should be in the 'DestroyedC' state"); } state = DestroyedA; - printf("[PASS] thread-local-atexit-macOS\n"); + PASS("[Success"); } static void work() @@ -105,11 +101,8 @@ static void work() thread_local B b = {}; } -int main() { - printf("[BEGIN] thread-local-atexit-macOS\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { work(); - return 0; } diff --git a/testing/test-cases/thread-local-atexit.dtest/main.cpp b/testing/test-cases/thread-local-atexit.dtest/main.cpp index d86dcab..950fb11 100644 --- a/testing/test-cases/thread-local-atexit.dtest/main.cpp +++ b/testing/test-cases/thread-local-atexit.dtest/main.cpp @@ -7,6 +7,8 @@ #include #include +#include "test_support.h" + // We create an A and a B. // While destroying B we create a C // Given that tlv_finalize has "destroy in reverse order of construction", we @@ -42,15 +44,14 @@ State state; A::A() { if ( state != None ) { - printf("[FAIL] thread-local-atexit: should be in the 'None' state\n"); + FAIL("Should be in the 'None' state"); } state = ConstructedA; } B::B() { if ( state != ConstructedA ) { - printf("[FAIL] thread-local-atexit: should be in the 'ConstructedA' state\n"); - _Exit(0); + FAIL("Should be in the 'ConstructedA' state"); } state = ConstructedB; } @@ -58,8 +59,7 @@ B::B() { C::C() { // We construct C during B's destructor if ( state != DestroyingB ) { - printf("[FAIL] thread-local-atexit: should be in the 'DestroyingB' state\n"); - _Exit(0); + FAIL("Should be in the 'DestroyingB' state"); } state = ConstructedC; } @@ -67,14 +67,12 @@ C::C() { // We destroy B first B::~B() { if ( state != ConstructedB ) { - printf("[FAIL] thread-local-atexit: should be in the 'ConstructedB' state\n"); - _Exit(0); + FAIL("Should be in the 'ConstructedB' state"); } state = DestroyingB; static thread_local C c; if ( state != ConstructedC ) { - printf("[FAIL] thread-local-atexit: should be in the 'ConstructedC' state\n"); - _Exit(0); + FAIL("Should be in the 'ConstructedC' state"); } state = DestroyedB; } @@ -82,8 +80,7 @@ B::~B() { // Then we destroy C C::~C() { if ( state != DestroyedB ) { - printf("[FAIL] thread-local-atexit: should be in the 'DestroyedB' state\n"); - _Exit(0); + FAIL("Should be in the 'DestroyedB' state"); } state = DestroyedC; } @@ -91,11 +88,10 @@ C::~C() { // And finally destroy A A::~A() { if ( state != DestroyedC ) { - printf("[FAIL] thread-local-atexit: should be in the 'DestroyedC' state\n"); - _Exit(0); + FAIL("Should be in the 'DestroyedC' state"); } state = DestroyedA; - printf("[PASS] thread-local-atexit\n"); + PASS("Success"); } static void* work(void* arg) @@ -106,13 +102,10 @@ static void* work(void* arg) return NULL; } -int main() { - printf("[BEGIN] thread-local-atexit\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { pthread_t worker; if ( pthread_create(&worker, NULL, work, NULL) != 0 ) { - printf("[FAIL] thread-local-atexit, pthread_create\n"); - return 0; + FAIL("pthread_create"); } void* dummy; diff --git a/testing/test-cases/thread-local-cleanup.dtest/main.c b/testing/test-cases/thread-local-cleanup.dtest/main.c index e301792..b7fcf17 100644 --- a/testing/test-cases/thread-local-cleanup.dtest/main.c +++ b/testing/test-cases/thread-local-cleanup.dtest/main.c @@ -7,30 +7,21 @@ #include #include +#include "test_support.h" - - - -int main() -{ - printf("[BEGIN] thread-local-cleanup\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { for (int i=0; i < 1000; ++i) { void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST); if ( handle == NULL ) { - printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror()); - return 0; + FAIL("dlopen error: iteration %d %s", i, dlerror()); } int result = dlclose(handle); if ( result != 0 ) { - printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror()); - return 0; + FAIL("dlclose error: iteration %d %s", i, dlerror()); } } - - printf("[PASS] thread-local-cleanup\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/thread-local-destructors.dtest/main.cpp b/testing/test-cases/thread-local-destructors.dtest/main.cpp index 1c43d27..0386e41 100644 --- a/testing/test-cases/thread-local-destructors.dtest/main.cpp +++ b/testing/test-cases/thread-local-destructors.dtest/main.cpp @@ -16,6 +16,8 @@ #include +#include "test_support.h" + static pthread_t sMainThread; static pthread_t sWorker1; static pthread_t sWorker2; @@ -136,8 +138,7 @@ static void* work1(void* arg) s.doWork(); if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) { - printf("[FAIL] thread-local-destructors, pthread_create\n"); - exit(0); + FAIL("pthread_create"); } void* dummy; pthread_join(sWorker2, &dummy); @@ -157,17 +158,14 @@ void checkMainThreadFinalizer() { bool shouldFinalize = false; #endif if ( sMainThreadFinalized != shouldFinalize ) - printf("[FAIL] thread-local-destructors, main thread finalisation not as expected\n"); + FAIL("Main thread finalisation not as expected"); else if ( sMainThreadFinalized_Another != shouldFinalize ) - printf("[FAIL] thread-local-destructors, main thread other struct finalisation not as expected\n"); + FAIL("Main thread other struct finalisation not as expected"); else - printf("[PASS] thread-local-destructors\n"); + PASS("Success"); } -int main() -{ - printf("[BEGIN] thread-local-destructors\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { sMainThread = pthread_self(); s.doWork(); @@ -178,8 +176,7 @@ int main() atexit(&checkMainThreadFinalizer); if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) { - printf("[FAIL] thread-local-destructors, pthread_create\n"); - return 0; + FAIL("pthread_create"); } void* dummy; @@ -187,25 +184,25 @@ int main() // validate each thread had different addresses for all TLVs if ( !sMainThreadInitialized ) - printf("[FAIL] thread-local-destructors, main thread was not initialized\n"); + FAIL("Main thread was not initialized"); else if ( !sWorker1Initialized ) - printf("[FAIL] thread-local-destructors, thread 1 was not initialized\n"); + FAIL("Thread 1 was not initialized"); else if ( !sWorker2Initialized ) - printf("[FAIL] thread-local-destructors, thread 2 was not initialized\n"); + FAIL("Thread 2 was not initialized"); else if ( !sWorker1Finalized ) - printf("[FAIL] thread-local-destructors, thread 1 was not finalised\n"); + FAIL("Thread 1 was not finalised"); else if ( !sWorker2Finalized ) - printf("[FAIL] thread-local-destructors, thread 2 was not finalised\n"); + FAIL("Thread 2 was not finalised"); else if ( !sMainThreadInitialized_Another ) - printf("[FAIL] thread-local-destructors, main thread other variable was not initialized\n"); + FAIL("Main thread other variable was not initialized"); else if ( !sWorker1Initialized_Another ) - printf("[FAIL] thread-local-destructors, thread 1 other variable was not initialized\n"); + FAIL("Thread 1 other variable was not initialized"); else if ( !sWorker2Initialized_Another ) - printf("[FAIL] thread-local-destructors, thread 2 other variable was not initialized\n"); + FAIL("Thread 2 other variable was not initialized"); else if ( !sWorker1Finalized_Another ) - printf("[FAIL] thread-local-destructors, thread 1 other variable was not finalised\n"); + FAIL("Thread 1 other variable was not finalised"); else if ( !sWorker2Finalized_Another ) - printf("[FAIL] thread-local-destructors, thread 2 other variable was not finalised\n"); + FAIL("Thread 2 other variable was not finalised"); else passedChecksInMain = true; diff --git a/testing/test-cases/thread-local-variables.dtest/main.c b/testing/test-cases/thread-local-variables.dtest/main.c index d7c73b2..272f142 100644 --- a/testing/test-cases/thread-local-variables.dtest/main.c +++ b/testing/test-cases/thread-local-variables.dtest/main.c @@ -14,6 +14,7 @@ #include #include +#include "test_support.h" extern __thread int a; extern __thread int b; @@ -61,8 +62,7 @@ static void* work1(void* arg) checkValues(); if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) { - printf("[FAIL] thread-local-variables, pthread_create\n"); - exit(0); + FAIL("pthread_create"); } void* dummy; pthread_join(sWorker2, &dummy); @@ -83,15 +83,11 @@ static bool someMatch(int* t1, int* t2, int* t3) return false; } -int main() -{ - printf("[BEGIN] thread-local-variables\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { checkValues(); if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) { - printf("[FAIL] thread-local-variables, pthread_create\n"); - return 0; + FAIL("pthread_create"); } getAddresses(sAddr3); @@ -101,15 +97,14 @@ int main() // validate each thread had different addresses for all TLVs if ( someMatch(sAddr1[0], sAddr2[0], sAddr3[0]) ) - printf("[FAIL] thread-local-variables, &a is same across some threads\n"); + FAIL("&a is same across some threads"); else if ( someMatch(sAddr1[1], sAddr2[1], sAddr3[1]) ) - printf("[FAIL] thread-local-variables, &b is same across some threads\n"); + FAIL("&b is same across some threads"); else if ( someMatch(sAddr1[2], sAddr2[2], sAddr3[2]) ) - printf("[FAIL] thread-local-variables, &c is same across some threads\n"); + FAIL("&c is same across some threads"); else if ( someMatch(sAddr1[3], sAddr2[3], sAddr3[3]) ) - printf("[FAIL] thread-local-variables, &d is same across some threads\n"); + FAIL("&d is same across some threads"); else - printf("[PASS] thread-local-variables\n"); - return 0; + PASS("Success"); } diff --git a/testing/test-cases/unix-conformance.dtest/main.c b/testing/test-cases/unix-conformance.dtest/main.c index a6540f8..d5994b2 100644 --- a/testing/test-cases/unix-conformance.dtest/main.c +++ b/testing/test-cases/unix-conformance.dtest/main.c @@ -1,10 +1,11 @@ // This tests that our header such as dlfcn.h pass unix conformance. -// BUILD_ONLY: MacOSX +// BUILD(macos): $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600 +// BUILD(macos): $CC main.c -o $BUILD_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 +// BUILD(macos): $SKIP_INSTALL $BUILD_DIR/scratch.exe -// BUILD: $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600 -// BUILD: $CC main.c -o $TEMP_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112 +// BUILD(ios,tvos,watchos,bridgeos): // RUN: ./unix-conformance.exe @@ -13,11 +14,9 @@ #include #include -int main() -{ - printf("[BEGIN] unix-conformance.dtest\n"); +#include "test_support.h" - printf("[PASS] unix-conformance.dtest\n"); - return 0; +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + PASS("Success"); } diff --git a/testing/test-cases/upward-links-initializers.dtest/init1.c b/testing/test-cases/upward-links-initializers.dtest/init1.c new file mode 100644 index 0000000..b555a48 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init1.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(1); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/init2.c b/testing/test-cases/upward-links-initializers.dtest/init2.c new file mode 100644 index 0000000..fa408d7 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init2.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(2); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/init3.c b/testing/test-cases/upward-links-initializers.dtest/init3.c new file mode 100644 index 0000000..9ce9776 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init3.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(3); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/init4.c b/testing/test-cases/upward-links-initializers.dtest/init4.c new file mode 100644 index 0000000..7547284 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init4.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(4); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/init5.c b/testing/test-cases/upward-links-initializers.dtest/init5.c new file mode 100644 index 0000000..b81a6e4 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init5.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(5); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/init6.c b/testing/test-cases/upward-links-initializers.dtest/init6.c new file mode 100644 index 0000000..2c7eeb2 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/init6.c @@ -0,0 +1,7 @@ +extern void checkInitOrder(int expected); + +__attribute__((constructor)) +static void myInit() +{ + checkInitOrder(6); +} diff --git a/testing/test-cases/upward-links-initializers.dtest/main.c b/testing/test-cases/upward-links-initializers.dtest/main.c new file mode 100644 index 0000000..c771f23 --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/main.c @@ -0,0 +1,42 @@ +// BUILD: $CC value.c -dynamiclib -install_name $RUN_DIR/libvalue.dylib -o $BUILD_DIR/libvalue.dylib +// BUILD: $CC init4.c -dynamiclib -install_name $RUN_DIR/libinit4.dylib -o $BUILD_DIR/libinit4.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC init5.c -dynamiclib -install_name $RUN_DIR/libinit5.dylib -o $BUILD_DIR/libinit5.dylib $BUILD_DIR/libinit4.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC init6.c -dynamiclib -install_name $RUN_DIR/libinit6.dylib -o $BUILD_DIR/libinit6.dylib $BUILD_DIR/libinit5.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC init1.c -dynamiclib -install_name $RUN_DIR/libinit1.dylib -o $BUILD_DIR/libinit1.dylib -Wl,-upward_library,$BUILD_DIR/libinit6.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC init2.c -dynamiclib -install_name $RUN_DIR/libinit2.dylib -o $BUILD_DIR/libinit2.dylib $BUILD_DIR/libinit1.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC init3.c -dynamiclib -install_name $RUN_DIR/libinit3.dylib -o $BUILD_DIR/libinit3.dylib $BUILD_DIR/libinit2.dylib $BUILD_DIR/libvalue.dylib +// BUILD: $CC main.c -o $BUILD_DIR/upward-link-initializers.exe -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libinit3.dylib + + +/* + * main ---> libinit3 + * | + * -------| + * libinit6 libinit2 + * | ^ | + * | | | + * | | | + * libinit5 -----------libinit1 + * | + * | + * libinit4 + * + * libinit1: lowest direct dependency from top level lib (libinit3) + * libinit6: only ever brought via upward link dependency, and should not be initialized before libinit1 + * libinit4: lowest direct dependency from dangling upward (libinit6) + */ + +// RUN: ./upward-link-initializers.exe + + +#include +#include +#include + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[]) { + // Initializers tests passed + PASS("Success"); +} + diff --git a/testing/test-cases/upward-links-initializers.dtest/value.c b/testing/test-cases/upward-links-initializers.dtest/value.c new file mode 100644 index 0000000..eccbb6f --- /dev/null +++ b/testing/test-cases/upward-links-initializers.dtest/value.c @@ -0,0 +1,10 @@ +#include "test_support.h" + +static int initValue = 0; + +void checkInitOrder(int expected) +{ + initValue++; + if ( initValue != expected) + FAIL("Wrong init value in bar: %d, should have been %d.", initValue, expected); +} diff --git a/testing/test-cases/weak-coalesce-dlopen.dtest/foo.cpp b/testing/test-cases/weak-coalesce-dlopen.dtest/foo.cpp new file mode 100644 index 0000000..49d95a2 --- /dev/null +++ b/testing/test-cases/weak-coalesce-dlopen.dtest/foo.cpp @@ -0,0 +1,6 @@ + +#include + +extern "C" void* foo() { + return new int(1); +} diff --git a/testing/test-cases/weak-coalesce-dlopen.dtest/main.cpp b/testing/test-cases/weak-coalesce-dlopen.dtest/main.cpp new file mode 100644 index 0000000..b294931 --- /dev/null +++ b/testing/test-cases/weak-coalesce-dlopen.dtest/main.cpp @@ -0,0 +1,54 @@ + +// BUILD: $CC foo.cpp -lc++ -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.cpp -lc++ -o $BUILD_DIR/weak-coalesce-dlopen.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./weak-coalesce-dlopen.exe + + +#include +#include +#include +#include + +#include "test_support.h" + +extern void* foo(); + +void* lastAllocatedValue = NULL; + +void* operator new(size_t size) { + lastAllocatedValue = malloc(size); + return lastAllocatedValue; +} + +int main() +{ + // The value we allocate should come from our new function + int* value1 = new int(1); + if ( value1 != lastAllocatedValue ) { + FAIL("value1 (%p) != lastAllocatedValue (%p)", value1, lastAllocatedValue); + } + + // dlopen foo which defines "foo" + // In dyld2, for chained fixups, this will run weakBindOld which patches the cache for + // weak defs. That patching will fail if the cache uses __DATA_CONST and was not marked as + // RW prior to patching + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); + } + + const void* symFoo = dlsym(handle, "foo"); + if ( symFoo == NULL ) { + FAIL("dlsym(handle, foo) failed"); + } + + // The value foo allocates should come from our new function + void* value2 = ((__typeof(&foo))symFoo)(); + if ( value2 != lastAllocatedValue ) { + FAIL("value2 (%p) != lastAllocatedValue (%p)", value2, lastAllocatedValue); + } + + PASS("weak-coalesce-dlopen"); +} + diff --git a/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp b/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp index f4fdad1..3f7d1ef 100644 --- a/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp +++ b/testing/test-cases/weak-coalesce-inserted-dylibs.dtest/main.cpp @@ -10,6 +10,8 @@ #include #include +#include "test_support.h" + extern void foo(); // We have our own copy of operator new. Make sure that we call our version, but that foo calls the one from the inserted bar dylib @@ -32,23 +34,18 @@ void operator delete(void* ptr) free(ptr); } -int main() -{ - printf("[BEGIN] weak-coalesce-inserted-dylibs\n"); - +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { // First make sure we do use our versions of new and delete. enableTracking = true; int* v = new int(1); if (!calledMainNew) { - printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call executable operator new\n"); - return 1; + FAIL("Didn't call executable operator new"); } delete v; if (!calledMainDelete) { - printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call executable operator delete\n"); - return 1; + FAIL("Didn't call executable operator delete"); } // Now make foo do the same and make sure we got the new/delete from bar @@ -57,17 +54,13 @@ int main() foo(); if (calledMainNew) { - printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call bar operator new\n"); - return 1; + FAIL("Didn't call bar operator new"); } if (calledMainDelete) { - printf("[FAIL] weak-coalesce-inserted-dylibs, didn't call bar operator delete\n"); - return 1; + FAIL("Didn't call bar operator delete"); } - printf("[PASS] weak-coalesce-inserted-dylibs\n"); - - return 0; + PASS("Success"); } diff --git a/testing/test-cases/weak-coalesce-unload.dtest/main.c b/testing/test-cases/weak-coalesce-unload.dtest/main.c index dc98c47..dc19b9d 100644 --- a/testing/test-cases/weak-coalesce-unload.dtest/main.c +++ b/testing/test-cases/weak-coalesce-unload.dtest/main.c @@ -11,117 +11,99 @@ #include #include +#include "test_support.h" + extern int foo(); extern void* fooPtr(); int main() { - printf("[BEGIN] weak-coalesce-unload\n"); - // dlopen foo1 which defines "foo" void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_FIRST); if ( handle1 == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo1.dylib", dlerror()); - return 0; + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo1.dylib", dlerror()); } const void* symFoo1 = dlsym(handle1, "foo"); if ( symFoo1 == NULL ) { - printf("[FAIL] dlsym(handle1, foo) failed\n"); - return 0; + FAIL("dlsym(handle1, foo) failed"); } const void* symFooPtr1 = dlsym(handle1, "fooPtr"); if ( symFooPtr1 == NULL ) { - printf("[FAIL] dlsym(handle1, fooPtr) failed\n"); - return 0; + FAIL("dlsym(handle1, fooPtr) failed"); } void* fooptr1 = ((__typeof(&fooPtr))symFooPtr1)(); int close1 = dlclose(handle1); if ( close1 != 0 ) { - printf("[FAIL] dlclose(handle1) failed with: %s\n", dlerror()); - return 0; + FAIL("dlclose(handle1) failed with: %s", dlerror()); } // Now dlopen foo2 and get the value it finds for foo void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST); if ( handle2 == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo2.dylib", dlerror()); - return 0; + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo2.dylib", dlerror()); } const void* symFoo2 = dlsym(handle2, "foo"); if ( symFoo2 == NULL ) { - printf("[FAIL] dlsym(handle2, foo) failed\n"); - return 0; + FAIL("dlsym(handle2, foo) failed"); } const void* symFooPtr2 = dlsym(handle2, "fooPtr"); if ( symFooPtr2 == NULL ) { - printf("[FAIL] dlsym(handle2, fooPtr) failed\n"); - return 0; + FAIL("dlsym(handle2, fooPtr) failed"); } void* fooptr2 = ((__typeof(&fooPtr))symFooPtr2)(); // Don't close foo2, but instead open foo3 void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_FIRST); if ( handle3 == NULL ) { - printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo3.dylib", dlerror()); - return 0; + FAIL("dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo3.dylib", dlerror()); } const void* symFoo3 = dlsym(handle3, "foo"); if ( symFoo3 == NULL ) { - printf("[FAIL] dlsym(handle3, foo) failed\n"); - return 0; + FAIL("dlsym(handle3, foo) failed"); } const void* symFooPtr3 = dlsym(handle3, "fooPtr"); if ( symFooPtr3 == NULL ) { - printf("[FAIL] dlsym(handle3, fooPtr) failed\n"); - return 0; + FAIL("dlsym(handle3, fooPtr) failed"); } void* fooptr3 = ((__typeof(&fooPtr))symFooPtr3)(); // No-one should point to libfoo1.dylib if ( symFoo1 == symFoo2 ) { - printf("[FAIL] foo1 == foo2\n"); - return 0; + FAIL("foo1 == foo2"); } if ( symFoo1 == symFoo3 ) { - printf("[FAIL] foo1 == foo3\n"); - return 0; + FAIL("foo1 == foo3"); } // foo2 and foo3 should be different if ( symFoo2 == symFoo3 ) { - printf("[FAIL] foo2 != foo3\n"); - return 0; + FAIL("foo2 != foo3"); } // But their coalesced values should be the same if ( fooptr1 == fooptr2 ) { - printf("[FAIL] fooptr1 == fooptr2\n"); - return 0; + FAIL("fooptr1 == fooptr2"); } if ( fooptr2 != fooptr3 ) { - printf("[FAIL] fooptr2 != fooptr3\n"); - return 0; + FAIL("fooptr2 != fooptr3"); } // foo should return the value from foo2, not the value from foo3 // Also calling this would explode if we somehow pointed at foo1 if ( ((__typeof(&foo))fooptr2)() != 2 ) { - printf("[FAIL] foo2 != 2\n"); - return 0; + FAIL("foo2 != 2"); } if ( ((__typeof(&foo))fooptr3)() != 2 ) { - printf("[FAIL] foo3 != 2\n"); - return 0; + FAIL("foo3 != 2"); } - printf("[PASS] weak-coalesce-unload\n"); - return 0; + PASS("weak-coalesce-unload"); } diff --git a/testing/test-cases/weak-coalesce.dtest/base.c b/testing/test-cases/weak-coalesce.dtest/base.c index b8a04c9..d0618b8 100644 --- a/testing/test-cases/weak-coalesce.dtest/base.c +++ b/testing/test-cases/weak-coalesce.dtest/base.c @@ -1,6 +1,7 @@ #include #include +#include "test_support.h" #include "base.h" static bool wasProblem = false; @@ -10,19 +11,19 @@ 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; - } - } + LOG("baseVerifyCoal1(%s, %p)", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + LOG("coal1 resolved to different locations. %p in %s and %p in %s", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } } @@ -32,32 +33,32 @@ 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; - } - } + LOG("baseVerifyCoal2(%s, %p)", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + LOG("coal2 resolved to different locations. %p in %s and %p in %s", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } } void baseCheck() { - if ( wasProblem ) - printf("[FAIL] weak-coalesce: was problem\n"); + if ( wasProblem ) + FAIL("was problem"); else if ( checkInCountCoal1 != 4 ) - printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n"); + FAIL("checkInCountCoal1 != 4"); else if ( checkInCountCoal2 != 4 ) - printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n"); - else - printf("[PASS] weak-coalesce\n"); + FAIL("checkInCountCoal2 != 2"); + else + PASS("Success"); } diff --git a/testing/test-cases/weak-coalesce.dtest/foo1.c b/testing/test-cases/weak-coalesce.dtest/foo1.c index 0707e0d..10bb3dd 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo1.c +++ b/testing/test-cases/weak-coalesce.dtest/foo1.c @@ -21,6 +21,9 @@ * @APPLE_LICENSE_HEADER_END@ */ #include + +#include "test_support.h" + #include "base.h" @@ -29,11 +32,12 @@ int __attribute__((weak)) coal1 = 1; int __attribute__((weak)) coal2 = 1; -static __attribute__((constructor)) void myinit() +static __attribute__((constructor)) +void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { - //fprintf(stderr, "myinit() in foo1.c\n"); - baseVerifyCoal1("in foo1", &coal1); - baseVerifyCoal2("in foo1", &coal2); + LOG("myinit() in foo1.c"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); } diff --git a/testing/test-cases/weak-coalesce.dtest/foo2.c b/testing/test-cases/weak-coalesce.dtest/foo2.c index 74d884b..37adf6a 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo2.c +++ b/testing/test-cases/weak-coalesce.dtest/foo2.c @@ -1,14 +1,16 @@ #include +#include "test_support.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() +void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { - //fprintf(stderr, "myinit() in foo1.c\n"); - baseVerifyCoal1("in foo2", &coal1); - baseVerifyCoal2("in foo2", &coal2); + LOG("myinit() in foo1.c"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); } diff --git a/testing/test-cases/weak-coalesce.dtest/foo3.c b/testing/test-cases/weak-coalesce.dtest/foo3.c index e086d5c..190e2fe 100644 --- a/testing/test-cases/weak-coalesce.dtest/foo3.c +++ b/testing/test-cases/weak-coalesce.dtest/foo3.c @@ -1,4 +1,4 @@ -#include +#include "test_support.h" #include "base.h" @@ -6,10 +6,10 @@ int __attribute__((weak)) coal1 = 3; int __attribute__((weak)) coal2 = 2; static __attribute__((constructor)) -void myinit() +void myinit(int argc, const char* argv[], const char* envp[], const char* apple[]) { - //fprintf(stderr, "myinit() in foo1.c\n"); - baseVerifyCoal1("in foo3", &coal1); - baseVerifyCoal2("in foo3", &coal2); + LOG("myinit() in foo1.c"); + baseVerifyCoal1("in foo3", &coal1); + baseVerifyCoal2("in foo3", &coal2); } diff --git a/testing/test-cases/weak-coalesce.dtest/main.c b/testing/test-cases/weak-coalesce.dtest/main.c index 4d09091..3e19d5d 100644 --- a/testing/test-cases/weak-coalesce.dtest/main.c +++ b/testing/test-cases/weak-coalesce.dtest/main.c @@ -11,16 +11,13 @@ #include #include +#include "test_support.h" #include "base.h" -int main() -{ - printf("[BEGIN] weak-coalesce\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + baseVerifyCoal1("in main", &coal1); + baseVerifyCoal2("in main", &coal2); - baseVerifyCoal1("in main", &coal1); - baseVerifyCoal2("in main", &coal2); - - baseCheck(); - return 0; + baseCheck(); } diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/bar.c b/testing/test-cases/weak-def-bind-old-format.dtest/bar.c new file mode 100644 index 0000000..82f5a4f --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/bar.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakTestValue = 42; + +int bar() { + return weakTestValue; +} \ No newline at end of file diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/foo.c b/testing/test-cases/weak-def-bind-old-format.dtest/foo.c new file mode 100644 index 0000000..172a9c1 --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/foo.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +int weakTestValue = 1; + +int foo() { + return weakTestValue; +} \ No newline at end of file diff --git a/testing/test-cases/weak-def-bind-old-format.dtest/main.c b/testing/test-cases/weak-def-bind-old-format.dtest/main.c new file mode 100644 index 0000000..e4fbc9d --- /dev/null +++ b/testing/test-cases/weak-def-bind-old-format.dtest/main.c @@ -0,0 +1,29 @@ +// BUILD(macos|x86_64): $CC bar.c -mmacosx-version-min=10.5 -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD(macos|x86_64): $CC foo.c -mmacosx-version-min=10.5 -dynamiclib $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD(macos|x86_64): $CC main.c -mmacosx-version-min=10.5 -o $BUILD_DIR/weak-def-bind-old-format.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar.dylib -L$BUILD_DIR + +// BUILD(ios,tvos,watchos,bridgeos): + +// RUN: ./weak-def-bind-old-format.exe + + +#include + +#include "test_support.h" + +extern int foo(); +extern int bar(); + + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + if ( foo() != 42 ) { + FAIL("weak-def-bind-old-format, wrong value"); + } + if ( bar() != 42 ) { + FAIL("weak-def-bind-old-format, wrong value"); + } + + PASS("weak-def-bind-old-format"); +} + + diff --git a/testing/test-cases/weak-def-unload.dtest/bar.c b/testing/test-cases/weak-def-unload.dtest/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/testing/test-cases/weak-def-unload.dtest/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/testing/test-cases/weak-def-unload.dtest/foo.c b/testing/test-cases/weak-def-unload.dtest/foo.c new file mode 100644 index 0000000..bf054b7 --- /dev/null +++ b/testing/test-cases/weak-def-unload.dtest/foo.c @@ -0,0 +1,7 @@ + +__attribute__((weak)) +void weak1() { } + +__attribute__((weak)) +void weak2() { } + diff --git a/testing/test-cases/weak-def-unload.dtest/main.c b/testing/test-cases/weak-def-unload.dtest/main.c new file mode 100644 index 0000000..a04e288 --- /dev/null +++ b/testing/test-cases/weak-def-unload.dtest/main.c @@ -0,0 +1,34 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libmissing.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/MATLAB -DRUN_DIR="$RUN_DIR" + +// RUN: ./MATLAB + +// libfoo.dylib was weak-def exported symbols. This tests that it can fail to load cleanly. + +#include +#include + +#include "test_support.h" + + + +int main(int argc, const char* argv[]) +{ + // force weak-def table to be generated + void* handle = dlopen("/usr/lib/libc++.dylib", 0); + if ( handle == NULL ) { + FAIL("weak-def-unload, expected dlopen(libc++.dylib) to succeed"); + } + + // try to dlopen something with weak symbols that fails to load + handle = dlopen(RUN_DIR "/libfoo.dylib", 0); + if ( handle != NULL ) { + FAIL("weak-def-unload, expected dlopen to fail"); + } + + PASS("weak-def-unload"); +} + diff --git a/testing/test-cases/weak-dylib-re-export.dtest/main.c b/testing/test-cases/weak-dylib-re-export.dtest/main.c index 7b9b7e6..a7aac22 100644 --- a/testing/test-cases/weak-dylib-re-export.dtest/main.c +++ b/testing/test-cases/weak-dylib-re-export.dtest/main.c @@ -1,24 +1,26 @@ -// BUILD: $CC bar.c -dynamiclib -o $TEMP_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib -// BUILD: $CC foo.c -dynamiclib -L$TEMP_DIR -weak-lbar -Wl,-reexported_symbols_list,symbols.txt -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib -L$BUILD_DIR -weak-lbar -Wl,-reexported_symbols_list,$SRC_DIR/symbols.txt -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: $SKIP_INSTALL $BUILD_DIR/libbar.dylib + // RUN: ./dylib-re-export.exe #include +#include "test_support.h" + __attribute__((weak_import)) extern int bar(); -int main() -{ - printf("[BEGIN] dylib-re-export\n"); +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { if ( &bar == 0 ) - printf("[PASS] dylib-re-export\n"); + PASS("SUCCESS"); else - printf("[FAIL] dylib-re-export, wrong value\n"); + FAIL("wrong value"); return 0; } diff --git a/testing/test-cases/weak-override-shared-cache.dtest/main.cpp b/testing/test-cases/weak-override-shared-cache.dtest/main.cpp new file mode 100644 index 0000000..ceb527a --- /dev/null +++ b/testing/test-cases/weak-override-shared-cache.dtest/main.cpp @@ -0,0 +1,24 @@ + +// BUILD: $CC main.cpp -lc++ -o $BUILD_DIR/weak-override-shared-cache.exe + +// RUN: ./weak-override-shared-cache.exe + + +#include +#include + +// Hack to get a strong definition of this symbol +__attribute__((used)) +void* hack __asm("__ZTISt16nested_exception"); + +#include "test_support.h" + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) { + try { + throw new std::nested_exception(); + } catch (std::nested_exception* e) { + PASS("Success"); + } + FAIL("Expected exception to be thrown"); +} + diff --git a/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile new file mode 100644 index 0000000..44befa7 --- /dev/null +++ b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main10 10 + ./main11 11 + ./main911 9 + ./main911b 9 + ./main1112 11 + ./main1112b 11 + ./main1211 12 + ./main1211b 12 + export DYLD_LIBRARY_PATH=${PWD}/alt11 && ./main10 11 + export DYLD_LIBRARY_PATH=${PWD}/alt9 && ./main11 9 + export DYLD_LIBRARY_PATH=${PWD}/alt20 && ./main11 11 + +all: + mkdir -p alt11 alt9 alt12 + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib" + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/libfoo.dylib" -o alt9/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/libfoo.dylib" -o alt12/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main10 main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main11 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11 + + +clean: + ${RM} -rf libfoo.dylib alt9 alt11 alt12 main10 main11 main9 main911 main911b main1112 main1112b main1211 main1211b + diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c deleted file mode 100644 index 26a1e09..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include // fprintf(), NULL -#include // exit(), EXIT_SUCCESS -#include -#include -#include // for atoi() -#include - -#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - - - -extern int foo(); - -int main(int argc, const char* argv[]) -{ - if ( argc > 2 ) { - bool found = false; - uint32_t count = _dyld_image_count(); - for(uint32_t i=0; i < count; ++i) { - const char* name = _dyld_get_image_name(i); - if ( strstr(name, argv[2]) != NULL ) - found = true; - //fprintf(stderr, "image[%d]=%s\n", i, name); - } - if ( !found ) { - FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic dylib has wrong path"); - return EXIT_SUCCESS; - } - } - - int expectedResult = atoi(argv[1]); - int actualResult = foo(); - //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); - if ( actualResult != expectedResult ) - FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); - else - PASS("DYLD_VERSIONED_FRAMEWORK_PATH-basic"); - - return EXIT_SUCCESS; -} - diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c deleted file mode 100644 index c57608f..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c +++ /dev/null @@ -1,5 +0,0 @@ - -int bar() -{ - return RESULT; -} diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c deleted file mode 100644 index a903b04..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include // fprintf(), NULL -#include // exit(), EXIT_SUCCESS -#include -#include -#include // for atoi() - -#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - - - -extern int foo(); - -int main(int argc, const char* argv[]) -{ - int expectedResult = atoi(argv[1]); - int actualResult = foo(); - //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); - if ( actualResult != expectedResult ) - FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); - else - PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); - - return EXIT_SUCCESS; -} - diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c deleted file mode 100644 index 01c576d..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - -int foo() -{ - return RESULT; -} diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c deleted file mode 100644 index a903b04..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include // fprintf(), NULL -#include // exit(), EXIT_SUCCESS -#include -#include -#include // for atoi() - -#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - - - -extern int foo(); - -int main(int argc, const char* argv[]) -{ - int expectedResult = atoi(argv[1]); - int actualResult = foo(); - //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); - if ( actualResult != expectedResult ) - FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); - else - PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); - - return EXIT_SUCCESS; -} - diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c deleted file mode 100644 index 01c576d..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c +++ /dev/null @@ -1,5 +0,0 @@ - -int foo() -{ - return RESULT; -} diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c deleted file mode 100644 index a903b04..0000000 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 Apple Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#include // fprintf(), NULL -#include // exit(), EXIT_SUCCESS -#include -#include -#include // for atoi() - -#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - - - -extern int foo(); - -int main(int argc, const char* argv[]) -{ - int expectedResult = atoi(argv[1]); - int actualResult = foo(); - //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); - if ( actualResult != expectedResult ) - FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); - else - PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); - - return EXIT_SUCCESS; -} - diff --git a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile new file mode 100644 index 0000000..2a47c2b --- /dev/null +++ b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +all: main + +main : main.c libbar.dylib libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +libfoo.dylib : foo.c libbar.dylib + ${CC} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib libbar.dylib + +libbar.dylib : bar.c + ${CC} -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib -install_name /usr/local/hide/libbar.dylib + +check: + ./main + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib diff --git a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile new file mode 100644 index 0000000..72b6a43 --- /dev/null +++ b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile @@ -0,0 +1,37 @@ +## +# 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 + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/NSAddImage-leafname/Makefile b/unit-tests/test-cases/NSAddImage-leafname/Makefile new file mode 100644 index 0000000..c31f202 --- /dev/null +++ b/unit-tests/test-cases/NSAddImage-leafname/Makefile @@ -0,0 +1,42 @@ +## +# 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: + export DYLD_FALLBACK_LIBRARY_PATH="hide" && ./main + +all: main hide/libzzz.dylib + +main : main.c + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +hide/libzzz.dylib: zzz.c + mkdir -p hide + ${CC} zzz.c -dynamiclib -o hide/libzzz.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main hide diff --git a/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile b/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile new file mode 100644 index 0000000..1590fdc --- /dev/null +++ b/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile @@ -0,0 +1,19 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wno-deprecated-declarations + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/absolute-symbol/Makefile b/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..1c06dda --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/Makefile @@ -0,0 +1,21 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: + + ${CC} ${CCFLAGS} foo.c abs.s -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/addend/Makefile b/unit-tests/test-cases/addend/Makefile new file mode 100644 index 0000000..db2bdbf --- /dev/null +++ b/unit-tests/test-cases/addend/Makefile @@ -0,0 +1,23 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/all_image_infos-cache-slide/Makefile b/unit-tests/test-cases/all_image_infos-cache-slide/Makefile new file mode 100644 index 0000000..0babb7e --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-cache-slide/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/all_image_infos-duplicate/Makefile b/unit-tests/test-cases/all_image_infos-duplicate/Makefile new file mode 100644 index 0000000..b0f89b7 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-duplicate/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib + cp libfoo.dylib libfoodup.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoodup.dylib diff --git a/unit-tests/test-cases/all_image_infos-paths/Makefile b/unit-tests/test-cases/all_image_infos-paths/Makefile new file mode 100644 index 0000000..e384ae6 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-paths/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main `pwd`/test.bundle + ./main `pwd`/test.dylib + +all: + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/all_image_infos/Makefile b/unit-tests/test-cases/all_image_infos/Makefile new file mode 100644 index 0000000..b625c20 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/unit-tests/test-cases/always-libSystem/Makefile b/unit-tests/test-cases/always-libSystem/Makefile new file mode 100644 index 0000000..287a47b --- /dev/null +++ b/unit-tests/test-cases/always-libSystem/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main +ifneq "$(OS_NAME)" "iPhoneOS" + # "avoid" does not work on iPhoneOS because the original dylibs don't exist + export DYLD_SHARED_REGION=avoid && ./main +endif + +all: main + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/big-jump-table/Makefile b/unit-tests/test-cases/big-jump-table/Makefile new file mode 100644 index 0000000..d968239 --- /dev/null +++ b/unit-tests/test-cases/big-jump-table/Makefile @@ -0,0 +1,62 @@ +## +# 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 + +### +### The point of this test is to check an edge case for i386 architecture +### with "fast stubs". We want to verify that a fast stub that ends +### near the page boundary for the __IMPORT segment does not cause an +### accidental read beyond the __IMPORT segment +### rdar://problem/4653725 +### + +main: main.c libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib + +libtest1.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=1 -o libtest1.dylib libfoo.dylib + +libtest2.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=2 -o libtest2.dylib libfoo.dylib + +libtest3.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=3 -o libtest3.dylib libfoo.dylib + +libtest4.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=4 -o libtest4.dylib libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/big-stack/Makefile b/unit-tests/test-cases/big-stack/Makefile new file mode 100644 index 0000000..4f8264a --- /dev/null +++ b/unit-tests/test-cases/big-stack/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# rosetta does not support very large stack sizes +STACK_SIZE = 0x83000000 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + STACK_SIZE = 0x02100000 + endif +endif + + +ifeq "iPhoneOS" "$(OS_NAME)" + STACK_SIZE = 0x20000000 +endif + + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "big stack" "big stack failed" ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-w -Wl,-stack_size -Wl,${STACK_SIZE} -DSTACK_SIZE=${STACK_SIZE} + +clean: + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/branch-islands/Makefile b/unit-tests/test-cases/branch-islands/Makefile new file mode 100644 index 0000000..7b746fd --- /dev/null +++ b/unit-tests/test-cases/branch-islands/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + +main : main.c space.s extra.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c space.s extra.c + + + +clean: + rm ${RMFLAGS} main diff --git a/unit-tests/test-cases/bundle-basic/Makefile b/unit-tests/test-cases/bundle-basic/Makefile new file mode 100644 index 0000000..c05f64e --- /dev/null +++ b/unit-tests/test-cases/bundle-basic/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-dont-gc/Makefile b/unit-tests/test-cases/bundle-dont-gc/Makefile new file mode 100644 index 0000000..5f4f05f --- /dev/null +++ b/unit-tests/test-cases/bundle-dont-gc/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c foo.bundle bar.bundle + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile b/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-bad/Makefile b/unit-tests/test-cases/bundle-memory-load-bad/Makefile new file mode 100644 index 0000000..a6b0db5 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-bad/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## + + +# +# The test case verifies that dyld can cleanly recover from +# a main executable (test.bundle) being used with +# NSCreateObjectFileImageFromMemory() +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-fat/Makefile b/unit-tests/test-cases/bundle-memory-load-fat/Makefile new file mode 100644 index 0000000..00641d0 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-fat/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2005-2011 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +FATFLAGS = $(shell ${LIPO} -detailed_info $(IOSROOT)/usr/lib/libSystem.B.dylib | grep architecture | sed -e 's/architecture/-arch/') + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + echo ${IOSROOT} + echo $(IOSROOT) + ${CC} ${FATFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-malloc/Makefile b/unit-tests/test-cases/bundle-memory-load-malloc/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-malloc/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load/Makefile b/unit-tests/test-cases/bundle-memory-load/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-multi-link/Makefile b/unit-tests/test-cases/bundle-multi-link/Makefile new file mode 100644 index 0000000..02b7dca --- /dev/null +++ b/unit-tests/test-cases/bundle-multi-link/Makefile @@ -0,0 +1,42 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/unit-tests/test-cases/bundle-multi-load/Makefile b/unit-tests/test-cases/bundle-multi-load/Makefile new file mode 100644 index 0000000..02b7dca --- /dev/null +++ b/unit-tests/test-cases/bundle-multi-load/Makefile @@ -0,0 +1,42 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/unit-tests/test-cases/bundle-name-ownership/Makefile b/unit-tests/test-cases/bundle-name-ownership/Makefile new file mode 100644 index 0000000..d5b047f --- /dev/null +++ b/unit-tests/test-cases/bundle-name-ownership/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-private/Makefile b/unit-tests/test-cases/bundle-private/Makefile new file mode 100644 index 0000000..72ca403 --- /dev/null +++ b/unit-tests/test-cases/bundle-private/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-reload/Makefile b/unit-tests/test-cases/bundle-reload/Makefile new file mode 100644 index 0000000..723c2c6 --- /dev/null +++ b/unit-tests/test-cases/bundle-reload/Makefile @@ -0,0 +1,48 @@ +## +# 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 + +ifeq "ppc" "$(ARCH)" + CXX_VERSION = g++-3.3 +else + CXX_VERSION = ${CXX} +endif + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.cxx + ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/unit-tests/test-cases/bundle-terminator/Makefile b/unit-tests/test-cases/bundle-terminator/Makefile new file mode 100644 index 0000000..8e11cff --- /dev/null +++ b/unit-tests/test-cases/bundle-terminator/Makefile @@ -0,0 +1,37 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-unlinkable/Makefile b/unit-tests/test-cases/bundle-unlinkable/Makefile new file mode 100644 index 0000000..b872e6c --- /dev/null +++ b/unit-tests/test-cases/bundle-unlinkable/Makefile @@ -0,0 +1,44 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c libstuff.dylib + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libstuff.dylib + +libstuff.dylib : lib.c + ${CC} ${CCFLAGS} lib.c -dynamiclib -o libstuff.dylib -install_name libcantfind.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle libstuff.dylib + diff --git a/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile new file mode 100644 index 0000000..d5b047f --- /dev/null +++ b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-v-dylib/Makefile b/unit-tests/test-cases/bundle-v-dylib/Makefile new file mode 100644 index 0000000..e706883 --- /dev/null +++ b/unit-tests/test-cases/bundle-v-dylib/Makefile @@ -0,0 +1,50 @@ +## +# 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 bar.dylib foo.bundle foo.dylib foo2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c bar.dylib + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -o foo.bundle foo.c + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo2.dylib foo.c + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -o bar.dylib bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib foo2.dylib bar.dylib + diff --git a/unit-tests/test-cases/bundle-weak/Makefile b/unit-tests/test-cases/bundle-weak/Makefile new file mode 100644 index 0000000..7cdc170 --- /dev/null +++ b/unit-tests/test-cases/bundle-weak/Makefile @@ -0,0 +1,41 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.cxx + ${CXX} ${CCXXFLAGS} -bundle -o test.bundle bundle.cxx + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile b/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile new file mode 100644 index 0000000..4a7020a --- /dev/null +++ b/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo1.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo2.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo3.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo4.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libfoo3.dylib libfoo4.dylib + diff --git a/unit-tests/test-cases/coreSymbolication-notify/Makefile b/unit-tests/test-cases/coreSymbolication-notify/Makefile new file mode 100644 index 0000000..d0b5b87 --- /dev/null +++ b/unit-tests/test-cases/coreSymbolication-notify/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2009 Apple, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +all-check: all check + +check: + export DYLD_PRINT_CS_NOTIFICATIONS=1 && ./main 2> notifications.log + grep "_load" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null + grep "_load" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null + grep "_unload" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null + grep "_unload" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null + echo "PASS coreSymbolication-notify" + +all: main foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib notifications.log + diff --git a/unit-tests/test-cases/crt-apple/Makefile b/unit-tests/test-cases/crt-apple/Makefile new file mode 100644 index 0000000..73b71ac --- /dev/null +++ b/unit-tests/test-cases/crt-apple/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# verify that apple[0] parameter is correct by comparing to argv[1] +# + +all-check: all check + +check: + ./main ./main + ./main.stripped ./main.stripped + `pwd`/main `pwd`/main + `pwd`/main.stripped `pwd`/main.stripped + export DYLD_LIBRARY_PATH=. && export DYLD_FRAMEWORK_PATH=. && ./main-setuid ./main-setuid 2>/dev/null + +all: main main.stripped main-setuid + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -w + +main.stripped: main + ${STRIP} main -o main.stripped + +main-setuid: main + cp main main-setuid + sudo chown root main-setuid + sudo chmod 4755 main-setuid + + +clean: + ${RM} ${RMFLAGS} *~ main main.stripped main-setuid + + diff --git a/unit-tests/test-cases/crt-argv-NULL/Makefile b/unit-tests/test-cases/crt-argv-NULL/Makefile new file mode 100644 index 0000000..f1865bb --- /dev/null +++ b/unit-tests/test-cases/crt-argv-NULL/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that crt glue can handle argv[0] = NULL +# + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main" "crt-argv-NULL main" ./main + + +all: main + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + + diff --git a/unit-tests/test-cases/crt-custom/Makefile b/unit-tests/test-cases/crt-custom/Makefile new file mode 100644 index 0000000..7bb8ddc --- /dev/null +++ b/unit-tests/test-cases/crt-custom/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that 10.4 binaries with a custom entry point +# have the entry point called before initializers are run +# + +all-check: all check + +check: + ./main + + +all: main + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main mystart.s main.c -e _mystart -Wl,-no_new_main + + +clean: + ${RM} ${RMFLAGS} *~ main + + diff --git a/unit-tests/test-cases/crt-libSystem/Makefile b/unit-tests/test-cases/crt-libSystem/Makefile new file mode 100644 index 0000000..367730a --- /dev/null +++ b/unit-tests/test-cases/crt-libSystem/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that _NS* routines in libSystem properly find global variables in main executable. +# the mechanism for 10.4 and 10.5 is different +# + + +all-check: all check + +check: + ./main + ./main.stripped + +all: main main.stripped + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include $(CRTLIB) -o main main.c + +main.stripped: main + ${STRIP} main -o main.stripped + +clean: + ${RM} ${RMFLAGS} *~ main main.stripped + + diff --git a/unit-tests/test-cases/crt-result/Makefile b/unit-tests/test-cases/crt-result/Makefile new file mode 100644 index 0000000..a0cf8ed --- /dev/null +++ b/unit-tests/test-cases/crt-result/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that the return value from main() makes it to shell +# for both crt1.0 and crt1.10.5.o +# + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good" "crt-result good" ./good + ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad" "crt-result bad" ./bad + + +all: good bad + + +good: good.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good good.c + +bad: bad.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad bad.c + +clean: + ${RM} ${RMFLAGS} *~ good bad + + diff --git a/unit-tests/test-cases/cxa_finalize/Makefile b/unit-tests/test-cases/cxa_finalize/Makefile new file mode 100644 index 0000000..44b4c0c --- /dev/null +++ b/unit-tests/test-cases/cxa_finalize/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/deadlock/Makefile b/unit-tests/test-cases/deadlock/Makefile new file mode 100644 index 0000000..c01b46d --- /dev/null +++ b/unit-tests/test-cases/deadlock/Makefile @@ -0,0 +1,44 @@ +## +# 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 bar.dylib foo.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c foo.dylib + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -o bar.dylib bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib bar.dylib + diff --git a/unit-tests/test-cases/dladdr-stripped/Makefile b/unit-tests/test-cases/dladdr-stripped/Makefile new file mode 100644 index 0000000..2e537bd --- /dev/null +++ b/unit-tests/test-cases/dladdr-stripped/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 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: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${STRIP} main + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dladdr/Makefile b/unit-tests/test-cases/dladdr/Makefile new file mode 100644 index 0000000..fb05db1 --- /dev/null +++ b/unit-tests/test-cases/dladdr/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.dSYM + diff --git a/unit-tests/test-cases/dlclose-basic/Makefile b/unit-tests/test-cases/dlclose-basic/Makefile new file mode 100644 index 0000000..8aeb014 --- /dev/null +++ b/unit-tests/test-cases/dlclose-basic/Makefile @@ -0,0 +1,43 @@ +## +# 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 + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -o test.bundle foo.c + +main : main.c test.bundle + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/unit-tests/test-cases/dlclose-bundle-unload/Makefile b/unit-tests/test-cases/dlclose-bundle-unload/Makefile new file mode 100644 index 0000000..c3d1b06 --- /dev/null +++ b/unit-tests/test-cases/dlclose-bundle-unload/Makefile @@ -0,0 +1,44 @@ +## +# 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 test.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile new file mode 100644 index 0000000..ac4fb27 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile @@ -0,0 +1,40 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile b/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile new file mode 100644 index 0000000..63e2f78 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile @@ -0,0 +1,40 @@ +## +# 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: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/Makefile b/unit-tests/test-cases/dlclose-dylib-terminators/Makefile new file mode 100644 index 0000000..e6d0b4c --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/Makefile @@ -0,0 +1,43 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# dylibs need to be unloaded in reverse order of creation + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cpp libbaz.dylib -o libbar.dylib + ${CXX} ${CXXFLAGS} -dynamiclib foo.cpp libbaz.dylib libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-unload/Makefile b/unit-tests/test-cases/dlclose-dylib-unload/Makefile new file mode 100644 index 0000000..bba1b65 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-unload/Makefile @@ -0,0 +1,46 @@ +## +# 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 libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-order/Makefile b/unit-tests/test-cases/dlclose-order/Makefile new file mode 100644 index 0000000..6447f72 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2013 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 + +# foo depends on bar depends on baz +# foo links with baz then bar +# verify that baz terminators run before bar terminators + + +all-check: all check + +check: + ./main libfoo.dylib + ./main libfoo2.dylib + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CXX} ${CXXFLAGS} -dynamiclib baz.cxx libbase.dylib -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cxx libbaz.dylib libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbaz.dylib libbar.dylib -o libfoo2.dylib + ${CC} ${CCFLAGS} main.c libbase.dylib -I${TESTROOT}/include -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo2.dylib libbaz.dylib libbar.dylib libbase.dylib + + diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile new file mode 100644 index 0000000..0c9b579 --- /dev/null +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# Leopard (9a499): dyld crash with recursive calls to dlclose() + + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-unload-c++/Makefile b/unit-tests/test-cases/dlclose-unload-c++/Makefile new file mode 100644 index 0000000..35530b6 --- /dev/null +++ b/unit-tests/test-cases/dlclose-unload-c++/Makefile @@ -0,0 +1,46 @@ +## +# 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 libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-unmap/Makefile b/unit-tests/test-cases/dlclose-unmap/Makefile new file mode 100644 index 0000000..5f24149 --- /dev/null +++ b/unit-tests/test-cases/dlclose-unmap/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlerror-clear/Makefile b/unit-tests/test-cases/dlerror-clear/Makefile new file mode 100644 index 0000000..4871023 --- /dev/null +++ b/unit-tests/test-cases/dlerror-clear/Makefile @@ -0,0 +1,40 @@ +## +# 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 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlerror/Makefile b/unit-tests/test-cases/dlerror/Makefile new file mode 100644 index 0000000..4871023 --- /dev/null +++ b/unit-tests/test-cases/dlerror/Makefile @@ -0,0 +1,40 @@ +## +# 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 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..30d9778 --- /dev/null +++ b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,31 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# Test that DYLD_FALLBACK_LIBRARY_PATH does not apply to dlopen() of a full path +# DYLD_FALLBACK_LIBRARY_PATH man page misleading +# + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH="${PWD}/hide" && ./main + +all: main hide/libfoo.dylib + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/hide/libfoo.dylib" + + + +clean: + ${RM} -rf *~ main hide + diff --git a/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..9ea3667 --- /dev/null +++ b/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,51 @@ +## +# 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 + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main "${PWD}/libfoo.dylib" + export DYLD_LIBRARY_PATH="${PWD}/alt" && ./main "${PWD}/libfoo.dylib" + +all: main alt/libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +alt/libfoo.dylib : foo.c + mkdir -p alt + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt/libfoo.dylib" -DALT + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib alt/libfoo.dylib alt + diff --git a/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..dddef71 --- /dev/null +++ b/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile @@ -0,0 +1,73 @@ +## +# 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 + +PWD = $(shell pwd) + +# +# tests combinations of dlopen() and LD_LIBRARY_PATH +# +# if dlopen() path contains a slash, LD_LIBRARY_PATH is ignored +# +# 1) leaf name found in current directory (On linux this fails because . is usually not in default LD_LIBRARY_PATH path) +# 2) cwd relative path +# 3) full path +# 4) leaf name and LD_LIBRARY_PATH overrides cwd (On 10.3, this fails because cwd was always searched before LD_LIBRARY_PATH??) +# 5) leaf name and LD_LIBRARY_PATH set to alternate directory +# 6) fullpath and LD_LIBRARY_PATH set to alt +# + +all-check: all check + +check: + cd alt1 && ../main "libfoo.dylib" 1 "leafname found in cwd" + ./main "./alt1/libfoo.dylib" 1 "relative path" + ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "libfoo.dylib" 1 "leafname and LD_LIBRARY_PATH overrides cwd" + export LD_LIBRARY_PATH="${PWD}/alt1" && cd alt3 && ../main "libfoo.dylib" 1 "leafname and alt LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath and LD_LIBRARY_PATH" + +all: main alt1/libfoo.dylib alt2/libfoo.dylib alt3 + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +alt1/libfoo.dylib : foo.c + mkdir -p alt1 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt1/libfoo.dylib" -DVALUE=1 + +alt2/libfoo.dylib : foo.c + mkdir -p alt2 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt2/libfoo.dylib" -DVALUE=2 + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" -DVALUE=0 + +alt3 : + mkdir -p alt3 + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib alt1 alt2 alt3 + diff --git a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile new file mode 100644 index 0000000..6d7266a --- /dev/null +++ b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + + diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile new file mode 100644 index 0000000..c1e199b --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.bundle bar.bundle libfoo.dylib libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c libbase.dylib + ${CC} ${CCFLAGS} -bundle foo.c libbase.dylib -o foo.bundle + +bar.bundle : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o bar.bundle + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib -sub_library libbase + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -install_name ${PWD}/libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile b/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile new file mode 100644 index 0000000..e6a5e5a --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile @@ -0,0 +1,49 @@ +## +# 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 foo.bundle bar.bundle + ./main foo.dylib bar.bundle + +all: main foo.bundle foo.dylib bar.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -flat_namespace -bundle bar.c -o bar.bundle -undefined suppress + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib bar.bundle + diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile new file mode 100644 index 0000000..823d5de --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile @@ -0,0 +1,46 @@ +## +# 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 foo.dylib bar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib + + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib bar.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile new file mode 100644 index 0000000..48bf4c4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile @@ -0,0 +1,31 @@ + + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifes that RTLD_LOCAL suppresses weak symbol coalescing +# + + + +all-check: all check + +check: + ./main + +all: main libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile new file mode 100644 index 0000000..e6a5e5a --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile @@ -0,0 +1,49 @@ +## +# 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 foo.bundle bar.bundle + ./main foo.dylib bar.bundle + +all: main foo.bundle foo.dylib bar.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -flat_namespace -bundle bar.c -o bar.bundle -undefined suppress + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib bar.bundle + diff --git a/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile b/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile @@ -0,0 +1,47 @@ +## +# 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 test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile new file mode 100644 index 0000000..b6a476e --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### +### main links with hide/libfoo.dylib +### main is run with DYLD_FALLBACK_LIBRARY_PATH pointing to hide +### main calls dlopen("/foo/bar/libfoo.dylib", RTLD_NOLOAD) +### dlopen should *not* find the already loaded libfoo.dylib because +### it is only supposed to search existing loaded images for one +### with a matching path. +### + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=hide && ./main /foo/bar/libfoo.dylib + +all: main + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c hide/libfoo.dylib -o main + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o hide/libfoo.dylib + + + + +clean: + ${RM} ${RMFLAGS} *~ main hide + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile new file mode 100644 index 0000000..75479e4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +libfoo.dylib : foo.c libbar.dylib libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -I${TESTROOT}/include libbase.dylib -o libbar.dylib + +libbaz.dylib : baz.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c libbase.dylib -o libbaz.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib libbase.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile new file mode 100644 index 0000000..eb6db50 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +### +### Test that RTLD_NOLOAD finds existing image +### even when symlinks are used to obscure it +### + +all-check: all check + +check: + ./main libfoosym.dylib + ./main2 libbar.dylib + + +all: main main2 libfoosym.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoosym.dylib : libfoo.dylib + ln -sf libfoo.dylib libfoosym.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o ${PWD}/libfoo.dylib + +main2 : main.c libbarsym.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbarsym.dylib -o main2 + +libbarsym.dylib : libbar.dylib + ln -sf libbar.dylib libbarsym.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name ${PWD}/libbarsym.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 libfoo.dylib libfoosym.dylib libbar.dylib libbarsym.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile new file mode 100644 index 0000000..f634e7b --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile @@ -0,0 +1,44 @@ +## +# 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 libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile new file mode 100644 index 0000000..3b639ea --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile @@ -0,0 +1,49 @@ +## +# 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 test.bundle foo.dylib foo_foo2.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c foo_foo2.dylib + ${CC} ${CCFLAGS} -bundle bundle.c foo_foo2.dylib -o test.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +foo_foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DFOO2 -o foo_foo2.dylib -install_name foo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle foo.dylib foo_foo2.dylib + diff --git a/unit-tests/test-cases/dlopen-basic/Makefile b/unit-tests/test-cases/dlopen-basic/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/unit-tests/test-cases/dlopen-basic/Makefile @@ -0,0 +1,47 @@ +## +# 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 test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile b/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile new file mode 100644 index 0000000..aabd547 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile @@ -0,0 +1,45 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + ./main-enforce + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -DENFORCE -o main-enforce + codesign -s - main-enforce + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main main-enforce libfoo.dylib diff --git a/unit-tests/test-cases/dlopen-codesign/Makefile b/unit-tests/test-cases/dlopen-codesign/Makefile new file mode 100644 index 0000000..9060b25 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign/Makefile @@ -0,0 +1,43 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + codesign -s - -o enforcement,hard main + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/unit-tests/test-cases/dlopen-dyld-locking/Makefile b/unit-tests/test-cases/dlopen-dyld-locking/Makefile new file mode 100644 index 0000000..dcf5be4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libbar.dylib + +main : main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -I${TESTROOT}/include + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/unit-tests/test-cases/dlopen-error/Makefile b/unit-tests/test-cases/dlopen-error/Makefile new file mode 100644 index 0000000..6515da4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-error/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +all-check: all check + +check: + chmod -r libnoread.dylib + ${RUN_AS_USER} ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libnoread.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libnoread.dylib + diff --git a/unit-tests/test-cases/dlopen-executable/Makefile b/unit-tests/test-cases/dlopen-executable/Makefile new file mode 100644 index 0000000..2fc2da1 --- /dev/null +++ b/unit-tests/test-cases/dlopen-executable/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.exe foo.pie + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.exe : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.exe -Wl,-no_pie + +foo.pie : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.pie -Wl,-pie + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.exe foo.pie + diff --git a/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile b/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile new file mode 100644 index 0000000..fec0176 --- /dev/null +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/dlopen-in-initializer/Makefile b/unit-tests/test-cases/dlopen-in-initializer/Makefile new file mode 100644 index 0000000..8623188 --- /dev/null +++ b/unit-tests/test-cases/dlopen-in-initializer/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-in-initializer" + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile b/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile new file mode 100644 index 0000000..b24fa5f --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile @@ -0,0 +1,62 @@ +all-check: all check + +check:## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### ADOBE: Premiere Pro crashes on quit +### +### libfoo depends on libfoo1 and libfoo2. main dlopens(libfoo). +### libfoo1 has an initializer that calls dlopen(libbar). +### libbar depends on libfoo2 +### + +all-check: all check + +check: + ./main + +all: main + +main : main.cxx libfoo.dylib + ${CXX} ${CCXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +libfoo.dylib : foo.c libfoo1.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libfoo1.dylib libfoo2.dylib + +libfoo1.dylib : foo1.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o libfoo1.dylib + +libfoo2.dylib : foo2.c + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o libfoo2.dylib + +libbar.dylib : bar.c libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib libfoo1.dylib libfoo2.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile b/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile new file mode 100644 index 0000000..8144bce --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen/Makefile b/unit-tests/test-cases/dlopen-init-dlopen/Makefile new file mode 100644 index 0000000..67d3a63 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen" + +all: main + +main : main.c libbar.dylib libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-init-up/Makefile b/unit-tests/test-cases/dlopen-init-up/Makefile new file mode 100644 index 0000000..104e51b --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-up/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-initializer/Makefile b/unit-tests/test-cases/dlopen-initializer/Makefile new file mode 100644 index 0000000..a4bc8a5 --- /dev/null +++ b/unit-tests/test-cases/dlopen-initializer/Makefile @@ -0,0 +1,47 @@ +## +# 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 test1.dylib test2.dylib + +main : main.c test1.dylib test2.dylib test3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DINIT_NAME=myinit -o test1.dylib + +test2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DINIT_NAME=_init -o test2.dylib + +test3.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o test3.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test1.dylib test2.dylib test3.dylib + diff --git a/unit-tests/test-cases/dlopen-leak-threaded/Makefile b/unit-tests/test-cases/dlopen-leak-threaded/Makefile new file mode 100644 index 0000000..83d46ff --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak-threaded/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/dlopen-leak/Makefile b/unit-tests/test-cases/dlopen-leak/Makefile new file mode 100644 index 0000000..a9f14c8 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak/Makefile @@ -0,0 +1,68 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/unit-tests/test-cases/dlopen-local-and-global/Makefile b/unit-tests/test-cases/dlopen-local-and-global/Makefile new file mode 100644 index 0000000..dae1997 --- /dev/null +++ b/unit-tests/test-cases/dlopen-local-and-global/Makefile @@ -0,0 +1,48 @@ +## +# 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-local-first + ./main-global-first + +all: main-local-first main-global-first foo.bundle foo.dylib + +main-local-first : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -DLOCAL_FIRST -o main-local-first + +main-global-first : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main-global-first + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib main-global-first main-local-first + diff --git a/unit-tests/test-cases/dlopen-multi/Makefile b/unit-tests/test-cases/dlopen-multi/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/unit-tests/test-cases/dlopen-multi/Makefile @@ -0,0 +1,47 @@ +## +# 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 test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlopen-non-canonical-path/Makefile b/unit-tests/test-cases/dlopen-non-canonical-path/Makefile new file mode 100644 index 0000000..e933867 --- /dev/null +++ b/unit-tests/test-cases/dlopen-non-canonical-path/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/dlopen-notify-bind/Makefile b/unit-tests/test-cases/dlopen-notify-bind/Makefile new file mode 100644 index 0000000..484ef57 --- /dev/null +++ b/unit-tests/test-cases/dlopen-notify-bind/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### CFSTRs cause crashes in Leopard +### +### main registers for notification and then dlopens(libfoo). +### In the notification callback, main calls NSLookupSymbolInImage(BIND) +### which double bound libfoo +### + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wno-deprecated-declarations -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-sandbox/Makefile b/unit-tests/test-cases/dlopen-sandbox/Makefile new file mode 100644 index 0000000..f52f1b2 --- /dev/null +++ b/unit-tests/test-cases/dlopen-sandbox/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2015 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + sandbox-exec -f sbs/deny-open.sb ./main "file system sandbox blocked open" + sandbox-exec -f sbs/deny-stat.sb ./main "file system sandbox blocked stat" + sandbox-exec -f sbs/deny-mmap.sb ./main "file system sandbox blocked mmap" + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + mkdir -p sbs + sed -e 's/FILTER/file-read-data/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-open.sb + sed -e 's/FILTER/file-read-metadata/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-stat.sb + sed -e 's/FILTER/file-map-executable/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-mmap.sb + +clean: + ${RM} -rf *~ main sbs libfoo.dylib + + diff --git a/unit-tests/test-cases/dlopen-search-leak/Makefile b/unit-tests/test-cases/dlopen-search-leak/Makefile new file mode 100644 index 0000000..e62fe0d --- /dev/null +++ b/unit-tests/test-cases/dlopen-search-leak/Makefile @@ -0,0 +1,71 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide:other:places && ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +libfoo.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo.dylib + +hide/libfoo.dylib : + mkdir hide + touch hide/libfoo.dylib + +other/libfoo.dylib : + mkdir other + touch other/libfoo.dylib + +main : main.c libfoo.dylib hide/libfoo.dylib other/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide other diff --git a/unit-tests/test-cases/dlopen-zero/Makefile b/unit-tests/test-cases/dlopen-zero/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/unit-tests/test-cases/dlopen-zero/Makefile @@ -0,0 +1,39 @@ +## +# 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 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlopen_preflight-basic/Makefile b/unit-tests/test-cases/dlopen_preflight-basic/Makefile new file mode 100644 index 0000000..285779f --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-basic/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle bogus.bundle libbogus.dylib + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +bogus.bundle : + echo "bogus" > bogus.bundle + +libbogus.dylib : + echo "bogus" > libbogus.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle bogus.bundle libbogus.dylib + diff --git a/unit-tests/test-cases/dlopen_preflight-cycle/Makefile b/unit-tests/test-cases/dlopen_preflight-cycle/Makefile new file mode 100644 index 0000000..2123b40 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-cycle/Makefile @@ -0,0 +1,41 @@ +## +# 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, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbaz.dylib baz.c -Wl,-upward_library,libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbaz.dylib libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile new file mode 100644 index 0000000..f5e572f --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen_preflight() if a non-batch image state handler +# denies the load +# Leaked fSegmentsArray and image segments during failed dlopen_preflight +# + + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +libfoo.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/unit-tests/test-cases/dlopen_preflight-leak/Makefile b/unit-tests/test-cases/dlopen_preflight-leak/Makefile new file mode 100644 index 0000000..1752c26 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile b/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile new file mode 100644 index 0000000..d1f6bf0 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile @@ -0,0 +1,47 @@ +## +# 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 test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile new file mode 100644 index 0000000..9e2b6da --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile @@ -0,0 +1,22 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile new file mode 100644 index 0000000..8be6211 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile @@ -0,0 +1,61 @@ +## +# 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 + +PROG = ./main + +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + PROG = /usr/bin/true + endif +endif + + + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ${PROG} + +all: main + +main : main.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo1.dylib + + +foo1.dylib : foo1.c foo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1.dylib foo2.dylib + +foo2.dylib : foo2.c foo3.dylib + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o foo2.dylib foo3.dylib + +foo3.dylib : foo3.c + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1-temp.dylib -install_name foo1.dylib + ${CC} ${CCFLAGS} -dynamiclib foo3.c -o foo3.dylib foo1-temp.dylib + rm foo1-temp.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle foo1.dylib foo2.dylib foo3.dylib + diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile b/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile new file mode 100644 index 0000000..4a148d8 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile @@ -0,0 +1,51 @@ +## +# 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 test.bundle test.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : test.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle test.c foo1.dylib -o test.bundle + +test.dylib : test.c foo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c foo2.dylib -o test.dylib + +foo1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo1.dylib + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo2.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib foo1.dylib foo2.dylib + diff --git a/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile b/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile new file mode 100644 index 0000000..4a148d8 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile @@ -0,0 +1,51 @@ +## +# 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 test.bundle test.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : test.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle test.c foo1.dylib -o test.bundle + +test.dylib : test.c foo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c foo2.dylib -o test.dylib + +foo1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo1.dylib + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo2.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib foo1.dylib foo2.dylib + diff --git a/unit-tests/test-cases/dlsym-error/Makefile b/unit-tests/test-cases/dlsym-error/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/unit-tests/test-cases/dlsym-error/Makefile @@ -0,0 +1,39 @@ +## +# 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 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlsym-indirect/Makefile b/unit-tests/test-cases/dlsym-indirect/Makefile new file mode 100644 index 0000000..f806baa --- /dev/null +++ b/unit-tests/test-cases/dlsym-indirect/Makefile @@ -0,0 +1,55 @@ +## +# 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 foo.dylib + ./main foo.bundle + +all: main + +main : main.c foo.bundle foo.dylib foo3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle foo.c foo1.dylib -o foo.bundle + +foo.dylib : foo.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c foo1.dylib -o foo.dylib + +foo1.dylib : foo1.c foo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1.dylib foo2.dylib + +foo2.dylib : foo2.c + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o foo2.dylib + +foo3.dylib : foo3.c + ${CC} ${CCFLAGS} -dynamiclib foo3.c -o foo3.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib foo1.dylib foo2.dylib foo3.dylib + diff --git a/unit-tests/test-cases/dtrace-static-probes/Makefile b/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..f800ae4 --- /dev/null +++ b/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that dtrace probes fire +# + +ifeq "$(OS_NAME)" "iPhoneOS" + SUDO = +else + SUDO = sudo +endif + +all-check: all check + +check: + if [ `${SUDO} /usr/sbin/dtrace -q -n 'Foo$$target:::count { printf("%u\n", arg0); } ' -c ./main | wc -w` == 5 ]; \ + then \ + echo "PASS dtrace-static-probes"; \ + else \ + echo "FAIL dtrace-static-probes"; \ + fi; \ + + + +all: main + + +main: main.c + dtrace -h -s foo.d + ${CC} ${CCFLAGS} main.c -o main -dead_strip + + +clean: + rm -rf main foo.h + diff --git a/unit-tests/test-cases/dyld-func-lookup/Makefile b/unit-tests/test-cases/dyld-func-lookup/Makefile new file mode 100644 index 0000000..c2a8e2e --- /dev/null +++ b/unit-tests/test-cases/dyld-func-lookup/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dyld-launched-prebound/Makefile b/unit-tests/test-cases/dyld-launched-prebound/Makefile new file mode 100644 index 0000000..61f202b --- /dev/null +++ b/unit-tests/test-cases/dyld-launched-prebound/Makefile @@ -0,0 +1,36 @@ +## +# 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: + ${TESTROOT}/bin/exit-zero-pass.pl "_dyld_launched_prebound() was implemented" "_dyld_launched_prebound() was not implemented" ./main + +all: + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dyld_is_memory_immutable/Makefile b/unit-tests/test-cases/dyld_is_memory_immutable/Makefile new file mode 100644 index 0000000..80fae8e --- /dev/null +++ b/unit-tests/test-cases/dyld_is_memory_immutable/Makefile @@ -0,0 +1,21 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dyld_shared_cache_iterate_text-strings b/unit-tests/test-cases/dyld_shared_cache_iterate_text-strings new file mode 100644 index 0000000..e69de29 diff --git a/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile b/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile new file mode 100644 index 0000000..8f07d7f --- /dev/null +++ b/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2015 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -I../../../launch-cache/ -o main main.c + +clean: + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/dynamic_cast-basic/Makefile b/unit-tests/test-cases/dynamic_cast-basic/Makefile new file mode 100644 index 0000000..79e8be6 --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/Makefile @@ -0,0 +1,24 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.cxx librealmain.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx librealmain.dylib + +librealmain.dylib: realmain.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.cxx + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..ab33f8c --- /dev/null +++ b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/hide && ./main hide/libz.dylib + ./main /usr/lib/libz + export DYLD_FALLBACK_LIBRARY_PATH="" && ${TESTROOT}/bin/exit-non-zero-pass.pl "env-DYLD_FALLBACK_LIBRARY_PATH" "env-DYLD_FALLBACK_LIBRARY_PATH with empty env" ./main2 + + +all: + mkdir -p hide + ${CC} compress.c -dynamiclib -o hide/libz.dylib -install_name /other/libz.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libz.dylib + ${CC} -I${TESTROOT}/include main.c -DSHOULD_FAIL=1 -o main2 hide/libz.dylib + + +clean: + ${RM} -rf *~ main main2 hide diff --git a/unit-tests/test-cases/executable-image-index/Makefile b/unit-tests/test-cases/executable-image-index/Makefile new file mode 100644 index 0000000..b85494d --- /dev/null +++ b/unit-tests/test-cases/executable-image-index/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + #export DYLD_INSERT_LIBRARIES=libfoo.dylib && ./main + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile b/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile new file mode 100644 index 0000000..1992633 --- /dev/null +++ b/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +PWD = $(shell pwd) + + +all-check: all check + + +all: main + +main : main.c libfoo.dylib libbar.dylib + ${CC} -I${TESTROOT}/include main.c libfoo.dylib libbar.dylib -o main + +libfoo.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -DFOO=1 -dynamiclib -install_name ${PWD}/libfoo.dylib -o libfoo.dylib + +libbar.dylib : bar.c other/libfoo.dylib + ${CC} -I${TESTROOT}/include bar.c -dynamiclib other/libfoo.dylib -install_name ${PWD}/libbar.dylib -o libbar.dylib + +other/libfoo.dylib : foo.c + mkdir -p ${PWD}/other + ${CC} -I${TESTROOT}/include foo.c -DFOO=2 -dynamiclib -install_name ${PWD}/other/libfoo.dylib -o other/libfoo.dylib + + + +check: + export DYLD_FALLBACK_LIBRARY_PATH=${PWD} && ./main + + +clean: + rm -rf main libfoo.dylib libbar.dylib other \ No newline at end of file diff --git a/unit-tests/test-cases/fallback-with-suid/Makefile b/unit-tests/test-cases/fallback-with-suid/Makefile new file mode 100644 index 0000000..dccb78c --- /dev/null +++ b/unit-tests/test-cases/fallback-with-suid/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +PWD = $(shell pwd) +TESTROOT = $(PWD)/../.. +include ${TESTROOT}/include/common.makefile + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +all-check: all check + +check: + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "fallback-with-suid" "fallback-with-suid" $(PWD)/main-suid + +all: main-suid + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +main-suid: main + cp main main-suid + sudo chown root main-suid + sudo chmod 4755 main-suid + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /bogus/libz.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main-suid libfoo.dylib + diff --git a/unit-tests/test-cases/flat-data/Makefile b/unit-tests/test-cases/flat-data/Makefile new file mode 100644 index 0000000..392ffaa --- /dev/null +++ b/unit-tests/test-cases/flat-data/Makefile @@ -0,0 +1,44 @@ +## +# 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 libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib + + +libbar.dylib : bar.c getbar.c + ${CC} ${CCFLAGS} -dynamiclib getbar.c bar.c -o libbar.dylib -flat_namespace + + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib + diff --git a/unit-tests/test-cases/flat-insert/Makefile b/unit-tests/test-cases/flat-insert/Makefile new file mode 100644 index 0000000..9ce9b26 --- /dev/null +++ b/unit-tests/test-cases/flat-insert/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### verify inserted libraries override with flat namespace + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -flat_namespace -Wl,-interposable + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/flat-prebound/Makefile b/unit-tests/test-cases/flat-prebound/Makefile new file mode 100644 index 0000000..6fe230b --- /dev/null +++ b/unit-tests/test-cases/flat-prebound/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +libfoo.dylib : foo.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 + + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 + + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/flat-private-extern/Makefile b/unit-tests/test-cases/flat-private-extern/Makefile new file mode 100644 index 0000000..fe7dbc1 --- /dev/null +++ b/unit-tests/test-cases/flat-private-extern/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoobar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoobar.dylib + + +libfoobar.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoobar.dylib -flat_namespace + + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoobar.dylib + diff --git a/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..569d814 --- /dev/null +++ b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,43 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main 10 + export DYLD_LIBRARY_PATH=${PWD}/hide && ./main 10 + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -o "${PWD}/Foo.framework/Foo" + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=0 -o "${PWD}/hide/Foo" + ${CC} ${CCFLAGS} main.c -framework Foo -F. -I../../include -o main + + +clean: + ${RM} -rf Foo.framework hide main \ No newline at end of file diff --git a/unit-tests/test-cases/framework-fallback/Makefile b/unit-tests/test-cases/framework-fallback/Makefile new file mode 100644 index 0000000..f29fb62 --- /dev/null +++ b/unit-tests/test-cases/framework-fallback/Makefile @@ -0,0 +1,39 @@ +## +# 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 + ${CC} ${CCFLAGS} -Wno-deprecated-declarations main.c -o main -I${TESTROOT}/include + +clean: + ${RM} ${RMFLAGS} main + + diff --git a/unit-tests/test-cases/ignore-bad-files/Makefile b/unit-tests/test-cases/ignore-bad-files/Makefile new file mode 100644 index 0000000..b181007 --- /dev/null +++ b/unit-tests/test-cases/ignore-bad-files/Makefile @@ -0,0 +1,73 @@ +## +# 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 + +# +# This test the ability of dyld to continue searching if a file was found but not usable. +# Uses DYLD_LIBRARY_PATH to search through many bogus files +# +# + + +all-check: all check + +check: + export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir && ${TESTROOT}/bin/exit-non-zero-pass.pl "ignore-bad-files intended failure" "ignore-bad-files intended failure" ./main + export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir:locations/real && ${TESTROOT}/bin/exit-zero-pass.pl "ignore-bad-files intended success" "ignore-bad-files intended success" ./main + +all: main locations/real/libfoo.dylib locations/exec/libfoo.dylib locations/datafile/libfoo.dylib locations/dir/libfoo.dylib + +main: main.c locations/real/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c locations/real/libfoo.dylib + + +# real dylib to use +locations/real/libfoo.dylib : foo.c + mkdir -p locations/real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o locations/real/libfoo.dylib foo.c -install_name libfoo.dylib + +# not a dylib - but a mach-o main executable +locations/exec/libfoo.dylib : main.c + mkdir -p locations/exec + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o locations/exec/libfoo.dylib main.c foo.c + +# some random data file +locations/datafile/libfoo.dylib : main.c + mkdir -p locations/datafile + cat main.c > locations/datafile/libfoo.dylib + +# not a file - but a directory +locations/dir/libfoo.dylib : + mkdir -p locations/dir/libfoo.dylib + +# file with wrong architecture +#locations/wrongarch/libfoo.dylib : +# mkdir -p locations/wrongarch +# ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o locations/wrongarch/libfoo.dylib foo.c -install_name libfoo.dylib -arch ppc64 + + + +clean: + ${RM} ${RMFLAGS} *~ main locations + diff --git a/unit-tests/test-cases/image-count/Makefile b/unit-tests/test-cases/image-count/Makefile new file mode 100644 index 0000000..115f7fe --- /dev/null +++ b/unit-tests/test-cases/image-count/Makefile @@ -0,0 +1,17 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -I../../../include + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/unit-tests/test-cases/image-remove-crash/Makefile b/unit-tests/test-cases/image-remove-crash/Makefile new file mode 100644 index 0000000..050a99c --- /dev/null +++ b/unit-tests/test-cases/image-remove-crash/Makefile @@ -0,0 +1,17 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o foo.bundle foo.c -bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/image-remove-notification/Makefile b/unit-tests/test-cases/image-remove-notification/Makefile new file mode 100644 index 0000000..0e1b901 --- /dev/null +++ b/unit-tests/test-cases/image-remove-notification/Makefile @@ -0,0 +1,43 @@ +## +# 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 foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o foo.bundle foo.c -bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/image-slide/Makefile b/unit-tests/test-cases/image-slide/Makefile new file mode 100644 index 0000000..9c791ea --- /dev/null +++ b/unit-tests/test-cases/image-slide/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/unit-tests/test-cases/image-state-change/Makefile b/unit-tests/test-cases/image-state-change/Makefile new file mode 100644 index 0000000..d4e91c8 --- /dev/null +++ b/unit-tests/test-cases/image-state-change/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib + diff --git a/unit-tests/test-cases/image-state-deny-OFI/Makefile b/unit-tests/test-cases/image-state-deny-OFI/Makefile new file mode 100644 index 0000000..a89cbf9 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-OFI/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle foo2.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +foo2.bundle: foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo2.bundle foo.c + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle foo2.bundle + diff --git a/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile b/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile new file mode 100644 index 0000000..56dd654 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile @@ -0,0 +1,41 @@ +## +# 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, +# 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 bar.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main bar.bundle + diff --git a/unit-tests/test-cases/image-state-deny-cache-leak/Makefile b/unit-tests/test-cases/image-state-deny-cache-leak/Makefile new file mode 100644 index 0000000..48aff7d --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-cache-leak/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libfoo1.dylib foo.c -lz + ${CC} ${CCFLAGS} -dynamiclib -o libfoo2.dylib foo.c -weak-lz + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/unit-tests/test-cases/image-state-deny-dlclose/Makefile b/unit-tests/test-cases/image-state-deny-dlclose/Makefile new file mode 100644 index 0000000..a67d741 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-dlclose/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbase.dylib base.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbase.dylib + diff --git a/unit-tests/test-cases/image-state-deny-leak/Makefile b/unit-tests/test-cases/image-state-deny-leak/Makefile new file mode 100644 index 0000000..364f994 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-leak/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libbar1.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libbar2.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c libbar1.dylib -weak_library libbar2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/unit-tests/test-cases/image-state-deny/Makefile b/unit-tests/test-cases/image-state-deny/Makefile new file mode 100644 index 0000000..04973bc --- /dev/null +++ b/unit-tests/test-cases/image-state-deny/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle + diff --git a/unit-tests/test-cases/image-state-dependents-initialized/Makefile b/unit-tests/test-cases/image-state-dependents-initialized/Makefile new file mode 100644 index 0000000..86f01d8 --- /dev/null +++ b/unit-tests/test-cases/image-state-dependents-initialized/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/image-suffix/Makefile b/unit-tests/test-cases/image-suffix/Makefile new file mode 100644 index 0000000..87e7bc8 --- /dev/null +++ b/unit-tests/test-cases/image-suffix/Makefile @@ -0,0 +1,157 @@ +## +# 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 + +PWD = $(shell pwd) + + +all-check: all check + +check: check1 check2 check3 check4 check5 check6 check7 check8 + +all: main1 main2 main3 main4 main5 main6 main7 main8 + +main : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main libfoo.dylib + +libfoo.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $(PWD)/libfoo.dylib + +libfoo_debug.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib + +hide/libfoo.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo.dylib -install_name $(PWD)/libfoo.dylib + +hide/libfoo_debug.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib + +# bar_debug has bar_debug as install name +libbar.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o libbar.dylib + +libbar_debug.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libbar_debug.dylib + +hide/libbar.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar.dylib -install_name $(PWD)/libbar.dylib + +hide/libbar_debug.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar_debug.dylib -install_name $(PWD)/libbar_debug.dylib + +clean: + rm -rf libfoo.dylib libfoo_debug.dylib hide libbar.dylib libbar_debug.dylib main1 main2 main3 main4 main5 main6 main7 main8 + + +# +# check1: main links with libfoo.dylib sets DYLD_IMAGE_SUFFIX=_debug and dynamically loads libfoo.dylib +# (fails on 10.3) +# +main1 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main1 libfoo.dylib + +check1: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 libfoo.dylib + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 $(PWD)/libfoo.dylib + + +# +# check2: main links with libfoo_debug.dylib and dynamically loads libfoo.dylib +# +main2 : main.c libfoo_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main2 libfoo_debug.dylib + +check2: + echo "pwd-1 is ${PWD}" + echo "pwd-2 is $(PWD)" + pwd + ./main2 $(PWD)/libfoo.dylib + + +# +# check3: main links with libfoo.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libfoo.dylib +# +main3 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main3 libfoo.dylib + +check3: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main3 $(PWD)/libfoo.dylib + + +# +# check4: main links with libfoo.dylib sets DYLD_LIBRARY_PATH=hide, DYLD_IMAGE_SUFFIX=_debug and dynamically loads libfoo.dylib +# (fails on 10.3) +# +main4 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main4 libfoo.dylib + +check4: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main4 $(PWD)/libfoo.dylib + + +# +# check5: main links with libbar.dylib sets DYLD_IMAGE_SUFFIX=_debug and dynamically loads libbar.dylib +# (fails on 10.3) +# +main5 : main.c libbar.dylib libbar_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main5 libbar.dylib + +check5: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main5 $(PWD)/libbar.dylib + + +# +# check6: main links with libbar_debug.dylib and dynamically loads libbar.dylib +# (fails on 10.3) +# +main6 : main.c libbar_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main6 libbar_debug.dylib + +check6: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main6 $(PWD)/libbar.dylib + + +# +# check7: main links with libbar.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libbar.dylib +# +main7 : main.c libbar.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main7 libbar.dylib + +check7: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main7 $(PWD)/libbar.dylib + + +# +# check8: main links with libbar.dylib sets DYLD_LIBRARY_PATH=hide, DYLD_IMAGE_SUFFIX=_debug and dynamically loads libbar.dylib +# (fails on 10.3) +# +main8 : main.c libbar.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main8 libbar.dylib + +check8: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main8 $(PWD)/libbar.dylib diff --git a/unit-tests/test-cases/image_header_containing_address/Makefile b/unit-tests/test-cases/image_header_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/unit-tests/test-cases/image_header_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/image_path_containing_address/Makefile b/unit-tests/test-cases/image_path_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/unit-tests/test-cases/image_path_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/init-libSystem-first/Makefile b/unit-tests/test-cases/init-libSystem-first/Makefile new file mode 100644 index 0000000..67424c7 --- /dev/null +++ b/unit-tests/test-cases/init-libSystem-first/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -nostdlib -ldylib1.o -flat_namespace -undefined suppress -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/init-order/Makefile b/unit-tests/test-cases/init-order/Makefile new file mode 100644 index 0000000..6525dc6 --- /dev/null +++ b/unit-tests/test-cases/init-order/Makefile @@ -0,0 +1,43 @@ +## +# 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 libtest.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libtest.dylib + +libtest.dylib: foo1.c foo2.c base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libtest.dylib foo1.c foo2.c base.c -init _myDashInit + + +clean: + ${RM} ${RMFLAGS} *~ main libtest.dylib + diff --git a/unit-tests/test-cases/initializer-args/Makefile b/unit-tests/test-cases/initializer-args/Makefile new file mode 100644 index 0000000..3f4e4d0 --- /dev/null +++ b/unit-tests/test-cases/initializer-args/Makefile @@ -0,0 +1,36 @@ +## +# 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 arg1 arg2 + +all: + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/initializer-bounds-check/Makefile b/unit-tests/test-cases/initializer-bounds-check/Makefile new file mode 100644 index 0000000..42f9e42 --- /dev/null +++ b/unit-tests/test-cases/initializer-bounds-check/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ${PASS_IFF_FAILURE} ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include foo1.c foo2.c libbar.dylib -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib libbar.dylib -o main + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile b/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile new file mode 100644 index 0000000..be551ef --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that weak symbols in inserted libraries work +# + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main + +all: main libinsert.dylib + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libinsert.dylib: insert.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/Makefile b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile new file mode 100644 index 0000000..fc9269f --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile @@ -0,0 +1,59 @@ +## +# 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 + +# +# verifies that initializers for inserted libraries run before the +# main executables initializers +# + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main + +all: main libinsert.dylib + + +main: main.c libfoo1.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib + + +libfoo1.dylib: foo1.c libbase.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib libfoo2.dylib + +libfoo2.dylib: foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + +libinsert.dylib: insert.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c libbase.dylib + +libbase.dylib: base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/unit-tests/test-cases/insert-libraries-with-suid/Makefile b/unit-tests/test-cases/insert-libraries-with-suid/Makefile new file mode 100644 index 0000000..c06cad5 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-suid/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +PWD = `pwd` + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env insert-libraries-with-suid 2>/dev/null + +all: main + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + sudo chown root main + sudo chmod 4755 main + echo "#!/bin/sh" > main-with-env + echo "export DYLD_INSERT_LIBRARIES=/usr/lib/libz.dylib" >> main-with-env + echo "$(PWD)/main" >> main-with-env + chmod +x main-with-env + +clean: + ${RM} ${RMFLAGS} *~ main main-with-env + diff --git a/unit-tests/test-cases/interpose-basic-prebound/Makefile b/unit-tests/test-cases/interpose-basic-prebound/Makefile new file mode 100644 index 0000000..c98a29b --- /dev/null +++ b/unit-tests/test-cases/interpose-basic-prebound/Makefile @@ -0,0 +1,46 @@ +## +# 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: + export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main + +all: main libmystrdup.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + export MACOSX_DEPLOYMENT_TARGET=10.3 && ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -prebind -seg1addr 20000 + +libmystrdup.dylib : mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libfoo.dylib + \ No newline at end of file diff --git a/unit-tests/test-cases/interpose-basic/Makefile b/unit-tests/test-cases/interpose-basic/Makefile new file mode 100644 index 0000000..1ea5385 --- /dev/null +++ b/unit-tests/test-cases/interpose-basic/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main + +all: main libmystrdup.dylib + +main : main.c libwrap.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib main.c + +libwrap.dylib: wrap.c + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib + +libmystrdup.dylib : mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib + diff --git a/unit-tests/test-cases/interpose-chained/Makefile b/unit-tests/test-cases/interpose-chained/Makefile new file mode 100644 index 0000000..2920a5c --- /dev/null +++ b/unit-tests/test-cases/interpose-chained/Makefile @@ -0,0 +1,65 @@ +## +# 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@ +# + +# +# This unit test verifies that multiple interposing libraries can all +# interpose the same function and the result is that they chain together. +# That is, each one calls through to the next. +# +# On Tiger (10.4.0), this test fails with infinite recursion. +# +# The function foo() does string appends. This allows us to check: +# 1) every interposer was called, and 2) they were called in the +# correct order. +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib:libfoo3.dylib" && ./main + +all: main libfoo1.dylib libfoo2.dylib libfoo3.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libfoo1.dylib : foo1.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c libfoo.dylib -o libfoo1.dylib + +libfoo2.dylib : foo2.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo2.c libfoo.dylib -o libfoo2.dylib + +libfoo3.dylib : foo3.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo3.c libfoo.dylib -o libfoo3.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo1.dylib libfoo2.dylib libfoo3.dylib + diff --git a/unit-tests/test-cases/interpose-dlsym/Makefile b/unit-tests/test-cases/interpose-dlsym/Makefile new file mode 100644 index 0000000..33e6b2a --- /dev/null +++ b/unit-tests/test-cases/interpose-dlsym/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmyfree.dylib" && ./main + +all: main libmyfree.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libmyfree.dylib : myfree.c + ${CC} ${CCFLAGS} -dynamiclib myfree.c -o libmyfree.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmyfree.dylib + diff --git a/unit-tests/test-cases/interpose-dynamic-basic/Makefile b/unit-tests/test-cases/interpose-dynamic-basic/Makefile new file mode 100644 index 0000000..b1cf89d --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-basic/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile b/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile new file mode 100644 index 0000000..410d9ae --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/interpose-dynamic-lazy/Makefile b/unit-tests/test-cases/interpose-dynamic-lazy/Makefile new file mode 100644 index 0000000..61586cc --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-lazy/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/interpose-multiple/Makefile b/unit-tests/test-cases/interpose-multiple/Makefile new file mode 100644 index 0000000..99041ca --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/Makefile @@ -0,0 +1,54 @@ +## +# 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@ +# + +# +# This unit test verifies that multiple interposing libraries can all +# interpose the same function and the result is that they chain together. +# That is, each one calls through to the next. +# +# On Tiger (10.4.0), this test fails with infinite recursion. +# +# The function foo() does string appends. This allows us to check: +# 1) every interposer was called, and 2) they were called in the +# correct order. +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib" && ./main + export DYLD_INSERT_LIBRARIES="libfoo2.dylib:libfoo1.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbase.dylib -o main + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo1.c libbase.dylib -o libfoo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo2.c libbase.dylib -o libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbase.dylib libfoo1.dylib libfoo2.dylib + diff --git a/unit-tests/test-cases/interpose-not-inserted/Makefile b/unit-tests/test-cases/interpose-not-inserted/Makefile new file mode 100644 index 0000000..cccf28c --- /dev/null +++ b/unit-tests/test-cases/interpose-not-inserted/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libmystrdup.dylib + +main : main.c wrap.c mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib libmystrdup.dylib main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib + diff --git a/unit-tests/test-cases/interpose-shared-cache/Makefile b/unit-tests/test-cases/interpose-shared-cache/Makefile new file mode 100644 index 0000000..c1cb7dd --- /dev/null +++ b/unit-tests/test-cases/interpose-shared-cache/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main + +all: main libmymalloc.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libmymalloc.dylib : mymalloc.c + ${CC} ${CCFLAGS} -dynamiclib mymalloc.c -o libmymalloc.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmymalloc.dylib + diff --git a/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile b/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile new file mode 100644 index 0000000..e21b784 --- /dev/null +++ b/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +### +### Test that when i386 "fast stubs" are updated early to +### avoid the cache line crossing race conditition, that +### dynamically looked up functions around bound lazily +### + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -undefined dynamic_lookup + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/unit-tests/test-cases/jump-table-race/Makefile b/unit-tests/test-cases/jump-table-race/Makefile new file mode 100644 index 0000000..273748c --- /dev/null +++ b/unit-tests/test-cases/jump-table-race/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +### +### The point of this test is that i386 "fast stubs" are updated in +### a thread safe manner. +### + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/unit-tests/test-cases/lazy-binding-reg-params/Makefile b/unit-tests/test-cases/lazy-binding-reg-params/Makefile new file mode 100644 index 0000000..73ce203 --- /dev/null +++ b/unit-tests/test-cases/lazy-binding-reg-params/Makefile @@ -0,0 +1,65 @@ +## +# 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 + +MACHINE = $(shell machine) + +ifeq "ppc" "$(ARCH)" + EXTRA_FLAG = -maltivec -force_cpusubtype_ALL +else + ifeq "ppc64" "$(ARCH)" + EXTRA_FLAG = -maltivec + else + ifeq "i386" "$(ARCH)" + EXTRA_FLAG = "" + else + ifeq "" "$(ARCH)" + ifeq "ppc970" "$(MACHINE)" + EXTRA_FLAG = -maltivec -force_cpusubtype_ALL + endif + endif + endif + endif +endif + + + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib ${EXTRA_FLAG} + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib ${EXTRA_FLAG} + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/lazy-dylib-init-order/Makefile b/unit-tests/test-cases/lazy-dylib-init-order/Makefile new file mode 100644 index 0000000..f3e0ed5 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-init-order/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main > actual.out + ${PASS_IFF} diff expected.out actual.out + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + + +clean: + rm -f libfoo.dylib main actual.out diff --git a/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile b/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile new file mode 100644 index 0000000..4cddd76 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib that can't be found errors out cleanly +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib.full main main2 diff --git a/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile b/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile new file mode 100644 index 0000000..cd631ce --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name libfoo.dylib + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib libfoo.dylib.full main main2 diff --git a/unit-tests/test-cases/lazy-pointer-binding/Makefile b/unit-tests/test-cases/lazy-pointer-binding/Makefile new file mode 100644 index 0000000..6d42f96 --- /dev/null +++ b/unit-tests/test-cases/lazy-pointer-binding/Makefile @@ -0,0 +1,43 @@ +## +# 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 libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/lib-name-overload/Makefile b/unit-tests/test-cases/lib-name-overload/Makefile new file mode 100644 index 0000000..c5a211f --- /dev/null +++ b/unit-tests/test-cases/lib-name-overload/Makefile @@ -0,0 +1,60 @@ +## +# 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 + + +### +### rdar://problem/3684168 +### +### The process has two different libfoo.dylib. +### When DYLD_LIBRARY_PATH is used, both resolve to the same library. +### It gets worse. One of the libraries re-exports from the other. +### So, without loop detection in dyld, it will infinitely recurse. +### + +PWD = `pwd` + +all-check: all check + +check: + # verify it runs as-is + ./main + # verify dyld doesn't hang on the circularity + DYLD_LIBRARY_PATH=$(PWD) && export DYLD_LIBRARY_PATH && ${TESTROOT}/bin/exit-zero-pass.pl "lib-name-overload" "lib-name-overload" ./main + +all: main + +other/libfoo.dylib : foo2.c + mkdir -p other + ${CC} foo2.c -dynamiclib -o $(PWD)/other/libfoo.dylib + +libfoo.dylib : foo.c other/libfoo.dylib + ${CC} foo.c -dynamiclib $(PWD)/other/libfoo.dylib -sub_library libfoo -o $(PWD)/libfoo.dylib + +main : main.c libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib + + +clean: + rm -rf main other libfoo.dylib \ No newline at end of file diff --git a/unit-tests/test-cases/loader_path-dup/Makefile b/unit-tests/test-cases/loader_path-dup/Makefile new file mode 100644 index 0000000..981e577 --- /dev/null +++ b/unit-tests/test-cases/loader_path-dup/Makefile @@ -0,0 +1,70 @@ +## +# 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 + +PWD = $(shell pwd) + +### +### This test case is to verify that two different dylibs with the same name +### and each loaded via the same @loader_path does not confuse dyld +### into just loading one of them. +### + +## Note, until ld understands @loader_path we have to do a funky make + + +all-check: all check + +check: + ./main + +all: main + +foo/libfoo.dylib : foo.c foo/libbase.dylib + mkdir -p foo + ${CC} foo.c foo/libbase.dylib -dynamiclib -o "${PWD}/foo/libfoo.dylib" + +foo/libbase.dylib : base.c + mkdir -p foo + ${CC} base.c -DFOO -dynamiclib -o foo/libbase.dylib + + +bar/libbar.dylib : bar.c bar/libbase.dylib + mkdir -p bar + ${CC} bar.c bar/libbase.dylib -dynamiclib -o "${PWD}/bar/libbar.dylib" + +bar/libbase.dylib : base.c + mkdir -p bar + ${CC} base.c -Dbar -dynamiclib -o bar/libbase.dylib + + +main : main.c foo/libfoo.dylib bar/libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main foo/libfoo.dylib bar/libbar.dylib + # this breaks partial makes, but ld can't see @loader_path or it freaks + ${INSTALL_NAME_TOOL} -change foo/libbase.dylib '@loader_path/libbase.dylib' foo/libfoo.dylib + ${INSTALL_NAME_TOOL} -change bar/libbase.dylib '@loader_path/libbase.dylib' bar/libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo bar diff --git a/unit-tests/test-cases/loader_path-symlink/Makefile b/unit-tests/test-cases/loader_path-symlink/Makefile new file mode 100644 index 0000000..7fab8fc --- /dev/null +++ b/unit-tests/test-cases/loader_path-symlink/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a dylib is found via symlink and must find another dylib relative to the symlink +# + +all-check: all check + +check: + ./bin/main + +all: + mkdir -p Frameworks/Foo.framework/Versions/A + mkdir -p Frameworks/Bar.framework/Versions/A + ${CC} bar.c -dynamiclib -o Frameworks/Bar.framework/Versions/A/Bar -install_name @loader_path/../Bar.framework/Bar + (cd Frameworks/Foo.framework; ln -fs Versions/A/Foo) + (cd Frameworks/Bar.framework; ln -fs Versions/A/Bar) + ${CC} foo.c -dynamiclib -o Frameworks/Foo.framework/Versions/A/Foo -install_name @loader_path/../Frameworks/Foo.framework/Foo Frameworks/Bar.framework/Versions/A/Bar + mkdir -p bin + ${CC} -I${TESTROOT}/include main.c -o bin/main Frameworks/Foo.framework/Versions/A/Foo + + +clean: + ${RM} -rf *~ Frameworks bin diff --git a/unit-tests/test-cases/loader_path/Makefile b/unit-tests/test-cases/loader_path/Makefile new file mode 100644 index 0000000..06eeb2b --- /dev/null +++ b/unit-tests/test-cases/loader_path/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name libfoo.dylib + +hide/libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib hide/hole/libfoo.dylib + install_name_tool -change libfoo.dylib '@loader_path/hole/libfoo.dylib' hide/libbar.dylib + +hide/libfoo3.dylib : foo.c + ${CC} foo.c -dynamiclib -o hide/libfoo3.dylib -install_name libfoo3.dylib + +libfoo2.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo2.dylib + + +main : main.c libfoo2.dylib hide/libbar.dylib hide/libfoo3.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main hide/libbar.dylib libfoo2.dylib + ${INSTALL_NAME_TOOL} -change libfoo2.dylib '@loader_path/libfoo2.dylib' main + ${INSTALL_NAME_TOOL} -change libbar.dylib '@loader_path/hide/libbar.dylib' main + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide libfoo2.dylib diff --git a/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile b/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile new file mode 100644 index 0000000..217074c --- /dev/null +++ b/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile @@ -0,0 +1,50 @@ +## +# 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: + export DYLD_FALLBACK_FRAMEWORK_PATH="dir" && ${TESTROOT}/bin/exit-zero-pass.pl "pass message" "fail message" ./main + +all: main dir/Foo.framework/Versions/Current/Foo + +Foo.framework/Versions/Current/Foo: + mkdir -p Foo.framework/Versions/A + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o Foo.framework/Versions/A/Foo Foo.c + cd Foo.framework/Versions/ && ln -s A Current + cd Foo.framework/ && ln -s Versions/Current/Foo Foo + +dir/Foo.framework/Versions/Current/Foo: + $(MAKE) Foo.framework/Versions/Current/Foo + rm -f Foo.framework/Foo + mkdir dir + mv Foo.framework dir + +main: + $(MAKE) Foo.framework/Versions/Current/Foo + ${CC} ${CCFLAGS} -I${TESTROOT}/include -F. -framework Foo -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main dir/ Foo.framework/ diff --git a/unit-tests/test-cases/non-lazy-slide/Makefile b/unit-tests/test-cases/non-lazy-slide/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/unit-tests/test-cases/non-lazy-slide/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/non-lazy-weak/Makefile b/unit-tests/test-cases/non-lazy-weak/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/unit-tests/test-cases/non-lazy-weak/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/non-weak-library/Makefile b/unit-tests/test-cases/non-weak-library/Makefile new file mode 100644 index 0000000..540909d --- /dev/null +++ b/unit-tests/test-cases/non-weak-library/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + cd absent/ && ../${TESTROOT}/bin/exit-non-zero-pass.pl "non-weak-library" "non-weak-library" ./main + cd present/ && ./main + +all: foo.c main.c + ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c + mkdir -p absent/ + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_ABSENT -o absent/main main.c + mkdir -p present/ + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_PRESENT -o present/main main.c + mv libfoo.dylib present/ + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib absent present diff --git a/unit-tests/test-cases/operator-new-dylib/Makefile b/unit-tests/test-cases/operator-new-dylib/Makefile new file mode 100644 index 0000000..fbaac19 --- /dev/null +++ b/unit-tests/test-cases/operator-new-dylib/Makefile @@ -0,0 +1,33 @@ + + + + +# +# test that if the main executable overrides operator new +# that a dylib using operator new is redirected to it. +# +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + + +main: main.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/operator-new/Makefile b/unit-tests/test-cases/operator-new/Makefile new file mode 100644 index 0000000..2fbed98 --- /dev/null +++ b/unit-tests/test-cases/operator-new/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + + +main: main.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/partial-library-load/Makefile b/unit-tests/test-cases/partial-library-load/Makefile new file mode 100644 index 0000000..83ef9bd --- /dev/null +++ b/unit-tests/test-cases/partial-library-load/Makefile @@ -0,0 +1,59 @@ +## +# 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 + +### +### rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +### +### We indirectly load libfoo which depends on libbar. The libbar that is loaded does not contain +### what libfoo needs, so libfoo fails to bind. The bug was that libfoo was left marked as bound, +### so the next use of libfoo seemed to succeed, when it should have failed. +### + + +all-check: all check + +check: + export DYLD_IMAGE_SUFFIX="_missing" && ${TESTROOT}/bin/exit-zero-pass.pl "partial-library-load" "partial-library-load" ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c libfoo.dylib + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libfoo.dylib + +libfoo.dylib : foo.c libbar_missing.dylib libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib libbar.dylib + +libbar_missing.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar_missing.dylib -install_name libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -DHAS_BAR2=1 + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle libfoo.dylib libbar.dylib libbar_missing.dylib + diff --git a/unit-tests/test-cases/pie-basic/Makefile b/unit-tests/test-cases/pie-basic/Makefile new file mode 100644 index 0000000..bac7b71 --- /dev/null +++ b/unit-tests/test-cases/pie-basic/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "FAIL pie-basic"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-big/Makefile b/unit-tests/test-cases/pie-big/Makefile new file mode 100644 index 0000000..e8a5fd0 --- /dev/null +++ b/unit-tests/test-cases/pie-big/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "XFAIL pie-basic"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-custom-stack/Makefile b/unit-tests/test-cases/pie-custom-stack/Makefile new file mode 100644 index 0000000..e795c80 --- /dev/null +++ b/unit-tests/test-cases/pie-custom-stack/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-custom-stack"; \ + else \ + echo "FAIL pie-custom-stack"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -Wl,-stack_size,0x10000000 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-dylib/Makefile b/unit-tests/test-cases/pie-dylib/Makefile new file mode 100644 index 0000000..3b709dd --- /dev/null +++ b/unit-tests/test-cases/pie-dylib/Makefile @@ -0,0 +1,41 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# If pie, ignore preferred load address +# +# run a PIE four times and verify libfoo.dylib load address was different every time +# + +FOO_ADDRESS = 0x10000000 + +ifeq "x86_64" "$(ARCH)" + FOO_ADDRESS = 0x300000000 +endif + + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-dylib"; \ + else \ + echo "FAIL pie-dylib"; \ + fi; \ + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -seg1addr ${FOO_ADDRESS} + +clean: + ${RM} ${RMFLAGS} *~ main main.out libfoo.dylib + diff --git a/unit-tests/test-cases/pie-text-reloc/Makefile b/unit-tests/test-cases/pie-text-reloc/Makefile new file mode 100644 index 0000000..31d75d6 --- /dev/null +++ b/unit-tests/test-cases/pie-text-reloc/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +EXTRA_OPTIONS = -mdynamic-no-pic -read_only_relocs suppress + +ifeq "x86_64" "$(ARCH)" + EXTRA_OPTIONS = +endif + +ifeq "iPhoneOS" "$(OS_NAME)" + EXTRA_OPTIONS = +endif + + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-text-reloc"; \ + else \ + echo "FAIL pie-text-reloc"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/prebased-performance/Makefile b/unit-tests/test-cases/prebased-performance/Makefile new file mode 100644 index 0000000..ce0d7d9 --- /dev/null +++ b/unit-tests/test-cases/prebased-performance/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +DYLIB_BASES_ADDRESS = 0x10000000 + +ifeq "ppc64" "$(ARCH)" + DYLIB_BASES_ADDRESS = 0x300000000 +else + ifeq "x86_64" "$(ARCH)" + DYLIB_BASES_ADDRESS = 0x300000000 + endif +endif + + + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib -Wl,-no_pie + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c -seg1addr ${DYLIB_BASES_ADDRESS} + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/progname/Makefile b/unit-tests/test-cases/progname/Makefile new file mode 100644 index 0000000..5caec06 --- /dev/null +++ b/unit-tests/test-cases/progname/Makefile @@ -0,0 +1,36 @@ +## +# 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: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/pthread-keys/Makefile b/unit-tests/test-cases/pthread-keys/Makefile new file mode 100644 index 0000000..a6a3242 --- /dev/null +++ b/unit-tests/test-cases/pthread-keys/Makefile @@ -0,0 +1,38 @@ +## +# 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 + ${CC} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/re-export-dylib/Makefile b/unit-tests/test-cases/re-export-dylib/Makefile new file mode 100644 index 0000000..5cea7a1 --- /dev/null +++ b/unit-tests/test-cases/re-export-dylib/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + + +# +# Test that dylib re-exports work +# + +all-check: all check + +check: + ./main + +all: main + + + +libbar.dylib : bar.c + ${CC} bar.c -dynamiclib -o $(PWD)/libbar.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib libbar.dylib -sub_library libbar -install_name $(PWD)/libfoo.dylib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib + + +clean: + rm -rf main libfoo.dylib libbar.dylib + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-framework/Makefile b/unit-tests/test-cases/re-export-framework/Makefile new file mode 100644 index 0000000..9e5cea6 --- /dev/null +++ b/unit-tests/test-cases/re-export-framework/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + + +# +# Test re-exported frameworks work +# + +all-check: all check + +check: + ./main + +all: main + + + +Bar.framework/Bar : bar.c + mkdir -p Bar.framework + ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar.framework/Bar -o Bar.framework/Bar + +Foo.framework/Foo : foo.c Bar.framework/Bar + mkdir -p Foo.framework + ${CC} foo.c -dynamiclib Bar.framework/Bar -sub_umbrella Bar -install_name $(PWD)/Foo.framework/Foo -o Foo.framework/Foo + +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. + + + + +clean: + rm -rf main Foo.framework Bar.framework + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-sub-framework/Makefile b/unit-tests/test-cases/re-export-sub-framework/Makefile new file mode 100644 index 0000000..d77a607 --- /dev/null +++ b/unit-tests/test-cases/re-export-sub-framework/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + + +# +# Test that the ways to re-export a sub framework work +# + +all-check: all check + +check: + ./main + +all: main + + +Bar.framework/Bar : bar.c + mkdir -p Bar.framework + ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar.framework/Bar -o Bar.framework/Bar -umbrella Foo + +Foo.framework/Foo : foo.c Bar.framework/Bar + mkdir -p Foo.framework + ${CC} foo.c -dynamiclib Bar.framework/Bar -install_name $(PWD)/Foo.framework/Foo -o Foo.framework/Foo + +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. + + + +clean: + rm -rf main Foo.framework Bar.framework + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-symbol-dylib/Makefile b/unit-tests/test-cases/re-export-symbol-dylib/Makefile new file mode 100644 index 0000000..9465192 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol-dylib/Makefile @@ -0,0 +1,59 @@ +## +# 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, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# Test that fine grain re-exports works +# + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_1: + ./main + +all_1: + # build frob + ${CC} ${CCFLAGS} -dynamiclib frob.c -o libfrob.dylib + # build baz to re-export all of frob + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -exported_symbols_list baz.exp libfrob.dylib + # build bar to re-export all of baz + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib + # build foo to re-export all of bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list foo.exp libbar.dylib + # buid main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include libfoo.dylib -o main + +check_: + ${PASS_IFF} true + +all_: + +clean: + rm -rf libbar.dylib libfoo.dylib libbaz.dylib libfrob.dylib main diff --git a/unit-tests/test-cases/re-export-symbol/Makefile b/unit-tests/test-cases/re-export-symbol/Makefile new file mode 100644 index 0000000..c02f912 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +# +# Test that fine grain re-exports works +# + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_1: + ./main1 + ./main2 + +all_1: + # build base library + ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib + + # build library the re-exports _bar from base library + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -exported_symbols_list foo.exp + # link against dylib and verify _bar is marked as coming from libfoo + ${CC} ${CCFLAGS} main1.c -I${TESTROOT}/include libfoo.dylib -o main1 + + # build library the re-exports _bar from base library as _mybar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp + # link against dylib and verify _mybar is marked as coming from libfoo + ${CC} ${CCFLAGS} main2.c -I${TESTROOT}/include libfoo2.dylib -o main2 + +check_: + ${PASS_IFF} true + +all_: + +clean: + rm -rf libbar.dylib libfoo.dylib libfoo2.dylib main1 main2 diff --git a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile new file mode 100644 index 0000000..5364cbd --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -o main main.c + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + diff --git a/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile new file mode 100644 index 0000000..59cf69c --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main + +all: main libmymalloc.dylib + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libmymalloc.dylib : mymalloc.c + ${CC} ${CCFLAGS} -dynamiclib mymalloc.c -o libmymalloc.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmymalloc.dylib + + diff --git a/unit-tests/test-cases/read-only-stubs/Makefile b/unit-tests/test-cases/read-only-stubs/Makefile new file mode 100644 index 0000000..7e70097 --- /dev/null +++ b/unit-tests/test-cases/read-only-stubs/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib foo.c libbar.dylib -o libfoo.dylib + +libbar.dylib: bar.c + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/restrict-environ/Makefile b/unit-tests/test-cases/restrict-environ/Makefile new file mode 100644 index 0000000..0d2aa80 --- /dev/null +++ b/unit-tests/test-cases/restrict-environ/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2009-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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile + RUN_AS_ROOT = +else + RUN_AS_USER = + RUN_AS_ROOT = sudo +endif + +PWD = `pwd` + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env 2>/dev/null + ${RUN_AS_ROOT} $(PWD)/main-with-env 2>/dev/null + +all: main + +main: main.c + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c -sectcreate __RESTRICT __restrict /dev/null + echo "#!/bin/sh" > main-with-env + echo "export DYLD_INSERT_LIBRARIES=/" >> main-with-env + echo "export DYLD_PRINT_LIBRARIES=/" >> main-with-env + echo "$(PWD)/main" >> main-with-env + chmod +x main-with-env + +clean: + ${RM} ${RMFLAGS} *~ main main-with-env + diff --git a/unit-tests/test-cases/restrict-executable_path/Makefile b/unit-tests/test-cases/restrict-executable_path/Makefile new file mode 100644 index 0000000..ceb2e3b --- /dev/null +++ b/unit-tests/test-cases/restrict-executable_path/Makefile @@ -0,0 +1,76 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +PWD = $(shell pwd) +TESTROOT = $(PWD)/../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +# +# Use of @exectuable_path in restricted binaries is not allowed +# Use of @loader_path in restricted binaries is not allowed +# Use of relative paths in restricted binaries is not allowed +# + +all-check: all check + +check: + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" $(PWD)/main_exe + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" $(PWD)/main_loader + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path relative path" "setuid-executable_path relative path" $(PWD)/main_rel + + +all: main_exe main_loader main_rel + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib -sectcreate __RESTRICT __restrict /dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_loader main_rel dir1 dir2 dir3 + diff --git a/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..d85c89b --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# 1) a main executable built with -rpath run with DYLD_FALLBACK_LIBRARY_PATH and linked rpath wins +# 2) a main executable built without -rpath run with DYLD_FALLBACK_LIBRARY_PATH and dylib found +# + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/bad && ./main + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/good && ./main2 + +all: main main2 bad/libfoo.dylib + + +good/libfoo.dylib : foo.c + mkdir -p good + ${CC} foo.c -dynamiclib -o good/libfoo.dylib -install_name @rpath/libfoo.dylib + +bad/libfoo.dylib : foo.c + mkdir -p bad + ${CC} foo.c -DBAD -dynamiclib -o bad/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main good/libfoo.dylib -Wl,-rpath -Wl,@loader_path/good + +main2 : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main2 good/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 good bad diff --git a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..6a7c133 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with DYLD_LIBRARY_PATH will override its rpath +# + +all-check: all check + +check: + export DYLD_LIBRARY_PATH=`pwd`/hide1 && ./main + +all: main hide2/libfoo.dylib hide1/libfoo.dylib + + +hide1/libfoo.dylib : foo.c + mkdir -p hide1 + ${CC} foo.c -dynamiclib -o hide1/libfoo.dylib -install_name @rpath/libfoo.dylib + +hide2/libfoo.dylib : foo.c + mkdir -p hide2 + ${CC} foo.c -DBAD -dynamiclib -o hide2/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide2/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide2/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide2 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 diff --git a/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile new file mode 100644 index 0000000..26692af --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile @@ -0,0 +1,28 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# DYLD_ROOT_PATH should apply to LC_RPATH rpaths +# + +all-check: all check + +check: + export DYLD_ROOT_PATH=`pwd` && ${PASS_IFF} ./main + +all: main + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} foo.c -dynamiclib -o hide/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libfoo.dylib -Wl,-rpath -Wl,/hide + + +clean: + ${RM} ${RMFLAGS} *~ main hide diff --git a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..f235c02 --- /dev/null +++ b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with LD_LIBRARY_PATH can locate a dylib it links against +# + +all-check: all check + +check: + export LD_LIBRARY_PATH=`pwd`/hide/hole && ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-basic/Makefile b/unit-tests/test-cases/rpath-basic/Makefile new file mode 100644 index 0000000..c559f75 --- /dev/null +++ b/unit-tests/test-cases/rpath-basic/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + +all-check: all check + +check: + ./main + +all: main + + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +hide/hole/libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c hide/hole/libbar.dylib -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} -rf *~ main hide diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile new file mode 100644 index 0000000..f5ebbc7 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the dylib should be used to +# find the dlopen path. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include -Wl,-rpath -Wl,${PWD}/hide/hole + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/Makefile b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile new file mode 100644 index 0000000..5856ea7 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the main executable should be used to +# find the dlopen path. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-leak/Makefile b/unit-tests/test-cases/rpath-dlopen-leak/Makefile new file mode 100644 index 0000000..9d7195a --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-leak/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with using @loader_path based rpaths +# + +# leaks does not work on rosetta processes +EMULATED = 0 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + EMULATED = 1 + endif +endif + +all-check: all check + +check: + if [ ${EMULATED} == 0 ]; \ + then \ + ./main ; \ + ./main.bad ; \ + else \ + echo "XFAIL rpath-dlopen-leak"; \ + fi; + +all: main main.bad + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole + +main.bad : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main.bad -Wl,-rpath -Wl,@loader_path/bad + +clean: + ${RM} ${RMFLAGS} *~ main main.bad hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile b/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile new file mode 100644 index 0000000..c166d10 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath. At runtime the exectuable +# deletes itself than calls dlopen(). Test that @executable_path +# does not cause malloc to abort. +# + +all-check: all check + +check: + cp main main.rm + ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-rm-executable" "rpath-dlopen-rm-executable" ./main.rm 2> /dev/null + +all: main + + +main : main.c + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@executable_path/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main main.rm diff --git a/unit-tests/test-cases/rpath-dlopen/Makefile b/unit-tests/test-cases/rpath-dlopen/Makefile new file mode 100644 index 0000000..f08dfc9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath and uses dlopen can find a dylib +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-executable_path/Makefile b/unit-tests/test-cases/rpath-executable_path/Makefile new file mode 100644 index 0000000..de9f672 --- /dev/null +++ b/unit-tests/test-cases/rpath-executable_path/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @executable_path. +# The @executable_path in the rpath should expand at runtime to the directory +# of the main executable. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libfoo.dylib + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,@executable_path/hide/hole + +clean: + ${RM} -rf *~ main hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-indirect-suid/Makefile b/unit-tests/test-cases/rpath-indirect-suid/Makefile new file mode 100644 index 0000000..6f96b7e --- /dev/null +++ b/unit-tests/test-cases/rpath-indirect-suid/Makefile @@ -0,0 +1,78 @@ +## +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +PWD = $(shell pwd) +TESTROOT = $(PWD)/../.. +include ${TESTROOT}/include/common.makefile + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +# +# a setuid main executable linked with -rpath links against a dylib +# that uses rpath to find another dylib. It is an error if +# LC_RPATH uses @loader_path or a relative path, but ok if it is an absolute path +# + +all-check: all check + +check: + ./main || echo "FAIL rpath-indirect-suid absolute path" + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid @loader_path path" "rpath-indirect-suid @loader_path path" $(PWD)/main_bad1 + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid relative path" "rpath-indirect-suid relative path" $(PWD)/main_bad2 + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid @rpath spoof" "rpath-indirect-suid @rpath spoof" $(PWD)/main_bad3 + +all: main main_bad1 main_bad2 main_bad3 + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c -dynamiclib -o "${PWD}/libfoo.dylib" hide/hole/libbar.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + sudo chown root main + sudo chmod 4755 main + +main_bad1 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad1 libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + sudo chown root main_bad1 + sudo chmod 4755 main_bad1 + +main_bad2 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad2 libfoo.dylib -Wl,-rpath -Wl,hide/hole + sudo chown root main_bad2 + sudo chmod 4755 main_bad2 + +main_bad3 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad3 libfoo.dylib + ln -s hide/hole @rpath + sudo chown root main_bad3 + sudo chmod 4755 main_bad3 + +clean: + ${RM} ${RMFLAGS} *~ main main_bad1 main_bad2 main_bad3 hide libfoo.dylib @rpath diff --git a/unit-tests/test-cases/rpath-install-name/Makefile b/unit-tests/test-cases/rpath-install-name/Makefile new file mode 100644 index 0000000..622ade0 --- /dev/null +++ b/unit-tests/test-cases/rpath-install-name/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# check that a loaded dylib with an @rpath install name will +# be found by a client. +# + +all-check: all check + +check: + ./main + +all: main + +libstuff.dylib : stuff.c + ${CC} stuff.c -I${TESTROOT}/include -dynamiclib -o libstuff.dylib -install_name @rpath/libstuff.dylib + +libstuff_better.dylib : stuff.c + ${CC} stuff.c -DBETTER=1 -I${TESTROOT}/include -dynamiclib -o libstuff_better.dylib -install_name @rpath/libstuff.dylib + +libbar.dylib : bar.c libstuff.dylib + ${CC} bar.c -dynamiclib libstuff.dylib -o libbar.dylib + + +main : main.c libbar.dylib libstuff_better.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath,${PWD} + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libstuff.dylib libstuff_better.dylib diff --git a/unit-tests/test-cases/rpath-introspection/Makefile b/unit-tests/test-cases/rpath-introspection/Makefile new file mode 100644 index 0000000..ec5d680 --- /dev/null +++ b/unit-tests/test-cases/rpath-introspection/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# Checks that @rpath that expands to have ../ in it will get realpathed and +# .. will not be seen when introspecting dylibs +# + +all-check: all check + +check: + ./main + +all: + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/../hide/hole + +clean: + ${RM} -rf *~ main hide diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile new file mode 100644 index 0000000..220b1ad --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the main executable during calls to dlopen() +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libbaz.dylib : baz.c + mkdir -p hide/hole + ${CC} baz.c -dynamiclib -o hide/hole/libbaz.dylib -install_name @rpath/libbaz.dylib + +libbar.dylib : bar.c hide/hole/libbaz.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libbaz.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole libfoo.dylib + +clean: + ${RM} -rf *~ main hide libbar.dylib libfoo.dylib \ No newline at end of file diff --git a/unit-tests/test-cases/rpath-loader_path/Makefile b/unit-tests/test-cases/rpath-loader_path/Makefile new file mode 100644 index 0000000..addee06 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the binary. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-nesting/Makefile b/unit-tests/test-cases/rpath-nesting/Makefile new file mode 100644 index 0000000..14e3a99 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# The main executable supplies an rpath. libfoo.dylib supplies an +# rpath. libfoo.dylib needs libbar.dylib and libbaz.dylib. One +# is found from the main executable's rpath and one from libfoo.dylib's +# rpath. +# + +all-check: all check + +check: + ./main + +all: main + +hide1/libbar.dylib : bar.c + mkdir -p hide1 + ${CC} bar.c -dynamiclib -o hide1/libbar.dylib -install_name @rpath/libbar.dylib + +hide2/libbaz.dylib : baz.c + mkdir -p hide2 + ${CC} baz.c -dynamiclib -o hide2/libbaz.dylib -install_name @rpath/libbaz.dylib + +libfoo.dylib : foo.c hide1/libbar.dylib hide2/libbaz.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib hide1/libbar.dylib hide2/libbaz.dylib -Wl,-rpath -Wl,${PWD}/hide2 + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide1 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 libfoo.dylib diff --git a/unit-tests/test-cases/rpath-no-trailing-slash/Makefile b/unit-tests/test-cases/rpath-no-trailing-slash/Makefile new file mode 100644 index 0000000..e10933d --- /dev/null +++ b/unit-tests/test-cases/rpath-no-trailing-slash/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2014 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 + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + +all-check: all check + +check: + ./hole/main + +all: main + + +main: + mkdir -p hole + ${CC} foo.c -dynamiclib -o hole/libfoo.dylib -install_name @rpath/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o hole/main hole/libfoo.dylib -Wl,-rpath,@executable_path + +clean: + ${RM} -rf *~ hole diff --git a/unit-tests/test-cases/shared-cache-symlink/Makefile b/unit-tests/test-cases/shared-cache-symlink/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/unit-tests/test-cases/shared-cache-symlink/Makefile @@ -0,0 +1,39 @@ +## +# 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 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/shared-region-overlap/Makefile b/unit-tests/test-cases/shared-region-overlap/Makefile new file mode 100644 index 0000000..8a194a2 --- /dev/null +++ b/unit-tests/test-cases/shared-region-overlap/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# rosetta does not support very large stack sizes +BASE_ADDRESS = 0x90030000 +ifeq "x86_64" "$(ARCH)" + BASE_ADDRESS = 0x7FFF80100000 +endif + + +ifeq "iPhoneOS" "$(OS_NAME)" + BASE_ADDRESS = 0x2FF80000 +endif + + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "shared-region-overlap" "shared-region-overlap" ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-image_base,${BASE_ADDRESS} + +clean: + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/suid-environ/Makefile b/unit-tests/test-cases/suid-environ/Makefile new file mode 100644 index 0000000..6356a4a --- /dev/null +++ b/unit-tests/test-cases/suid-environ/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +PWD = `pwd` + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env 2>/dev/null + +all: main + +main: main.c + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c + sudo chown root main + sudo chmod 4755 main + echo "#!/bin/sh" > main-with-env + echo "export DYLD_INSERT_LIBRARIES=/" >> main-with-env + echo "export DYLD_PRINT_LIBRARIES=/" >> main-with-env + echo "$(PWD)/main" >> main-with-env + chmod +x main-with-env + +clean: + ${RM} ${RMFLAGS} *~ main main-with-env + diff --git a/unit-tests/test-cases/suid-executable_path/Makefile b/unit-tests/test-cases/suid-executable_path/Makefile new file mode 100644 index 0000000..aab4ca2 --- /dev/null +++ b/unit-tests/test-cases/suid-executable_path/Makefile @@ -0,0 +1,90 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +PWD = $(shell pwd) +TESTROOT = $(PWD)/../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +# +# Use of @exectuable_path in setuid binaries is not allowed +# Use of @loader_path in setuid binaries is not allowed +# Use of relative paths in setuid binaries is not allowed +# + +all-check: all check + +check: + ./main_exe "setuid-executable_path" || echo "FAIL setuid-executable_path @executable_path not setuid" + ./main_loader "setuid-executable_path" || echo "FAIL setuid-executable_path @loader_path not setuid" + ./main_rel "setuid-executable_path" || echo "FAIL setuid-executable_path relative path not setuid" + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" $(PWD)/main_exe-suid + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" $(PWD)/main_loader-suid + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path relative path" "setuid-executable_path relative path" $(PWD)/main_rel-suid + + + +all: main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib + cp main_exe main_exe-suid + sudo chown root main_exe-suid + sudo chmod 4755 main_exe-suid + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib + cp main_loader main_loader-suid + sudo chown root main_loader-suid + sudo chmod 4755 main_loader-suid + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib + cp main_rel main_rel-suid + sudo chown root main_rel-suid + sudo chmod 4755 main_rel-suid + + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid dir1 dir2 dir3 + diff --git a/unit-tests/test-cases/sym-link-load/Makefile b/unit-tests/test-cases/sym-link-load/Makefile new file mode 100644 index 0000000..a0feb3a --- /dev/null +++ b/unit-tests/test-cases/sym-link-load/Makefile @@ -0,0 +1,85 @@ +## +# 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 + +### +### This test case is to verify that dyld cannot be tricked into loading +### the "same" library twice. +### +### The tricky scenario is: +### 1) a library dyld is to load is specified with a symlink path +### 2) the library the that is the target of the symlink is already loaded +### via a different path. In this case becuase of DYLD_LIBRARY_PATH. +### +### +### +### + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main + export DYLD_LIBRARY_PATH="${PWD}/fake" && ./main + + +all: main real/liblink.dylib real/libtest.dylib fake/libtest.dylib + + +main: main.c stub/libtest.dylib stub/liblink.dylib real/libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c stub/libtest.dylib stub/liblink.dylib real/libbase.dylib + +stub/libtest.dylib: test.c + mkdir -p stub + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c -DDO_NOTHING -o stub/libtest.dylib -install_name "${PWD}/real/libtest.dylib" + +stub/liblink.dylib: link.c + mkdir -p stub + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib link.c -o stub/liblink.dylib -install_name "${PWD}/real/liblink.dylib" + + +real/libbase.dylib: base.c + mkdir -p real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib base.c -o "${PWD}/real/libbase.dylib" + +real/libtest.dylib: test.c real/libbase.dylib + mkdir -p real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/real/libtest.dylib" + +real/liblink.dylib: link.c + mkdir -p real + cd real && ln -s libtest.dylib liblink.dylib + + +fake/libtest.dylib: test.c real/libbase.dylib + mkdir -p fake + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/fake/libtest.dylib" -install_name "${PWD}/real/libtest.dylib" + + + + +clean: + ${RM} ${RMFLAGS} *~ main real stub fake + diff --git a/unit-tests/test-cases/symbol-resolver-basic/Makefile b/unit-tests/test-cases/symbol-resolver-basic/Makefile new file mode 100644 index 0000000..f38f287 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of symbol-resolver functions +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + export TEN=1 && ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib foo.c foo2.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/unit-tests/test-cases/symbol-resolver-interposed/Makefile b/unit-tests/test-cases/symbol-resolver-interposed/Makefile new file mode 100644 index 0000000..2d0c380 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-interposed/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Test that resolver based functions can be interposed +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + export DYLD_INSERT_LIBRARIES=myfoo.interposelib && ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + ${CC} ${CCFLAGS} -dynamiclib myfoo.c libfoo.dylib -o myfoo.interposelib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib myfoo.interposelib + diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile b/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile new file mode 100644 index 0000000..dc1021c --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Test that resolver function is not run before initializer +## even when lazy pointers are bound early. +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -I${TESTROOT}/include + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o b/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o new file mode 100644 index 0000000..9257269 Binary files /dev/null and b/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o differ diff --git a/unit-tests/test-cases/symbol-resolver-pointer/Makefile b/unit-tests/test-cases/symbol-resolver-pointer/Makefile new file mode 100644 index 0000000..bf8e689 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-pointer/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of symbol-resolver functions +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/unit-tests/test-cases/template/Makefile b/unit-tests/test-cases/template/Makefile new file mode 100644 index 0000000..ec6d1cb --- /dev/null +++ b/unit-tests/test-cases/template/Makefile @@ -0,0 +1,37 @@ +## +# 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: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/terminator-bounds-check/Makefile b/unit-tests/test-cases/terminator-bounds-check/Makefile new file mode 100644 index 0000000..71d7002 --- /dev/null +++ b/unit-tests/test-cases/terminator-bounds-check/Makefile @@ -0,0 +1,36 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ${PASS_IFF_FAILURE} ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/text-perm-alt-segment/Makefile b/unit-tests/test-cases/text-perm-alt-segment/Makefile new file mode 100644 index 0000000..163719d --- /dev/null +++ b/unit-tests/test-cases/text-perm-alt-segment/Makefile @@ -0,0 +1,54 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +### +### This test case is to verify __TEXT relocations work in dylibs +### +### + + +ifeq "i386" "$(ARCH)" + EXTRA_DEP = libfoo.dylib +else + +endif + +all-check: all check + +check: + ./main + +all: ${EXTRA_DEP} + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c ${EXTRA_DEP} + +libfoo.dylib: + ${CC} ${CCFLAGS} foo.c -c + ${LD} -r -arch ${ARCH} foo.o -rename_section __TEXT __text MYTEXT mytext -o foo2.o + ${CC} ${CCFLAGS} -dynamiclib foo2.o -o libfoo.dylib -Wl,-segprot,MYTEXT,rwx,r-x -read_only_relocs suppress + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib foo.o foo2.o + diff --git a/unit-tests/test-cases/text-relocs-perms/Makefile b/unit-tests/test-cases/text-relocs-perms/Makefile new file mode 100644 index 0000000..2e1ec0f --- /dev/null +++ b/unit-tests/test-cases/text-relocs-perms/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib -seg1addr 0x20000000 + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/unit-tests/test-cases/text-relocs/Makefile b/unit-tests/test-cases/text-relocs/Makefile new file mode 100644 index 0000000..1048e84 --- /dev/null +++ b/unit-tests/test-cases/text-relocs/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +### +### This test case is to verify __TEXT relocations work in dylibs +### +### + + +TEXT_RELOC_FLAGS = -mdynamic-no-pic -read_only_relocs suppress -Wl,-w +TEXT_STAT_FLAGS = -static + +ifeq "iPhoneOS" "$(OS_NAME)" + # iOS does not support text relocs + TEXT_RELOC_FLAGS = + TEXT_STAT_FLAGS = +endif + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib bar.c space.s -Os -o libbar.dylib ${TEXT_RELOC_FLAGS} + ${CC} ${CCFLAGS} bind.c $(TEXT_STAT_FLAGS) -Os -c -o bind.o + ${CC} -dynamiclib bind.o libbar.dylib -o libbind.dylib ${TEXT_RELOC_FLAGS} + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib libbind.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libbind.dylib bind.o + diff --git a/unit-tests/test-cases/threaded-flat-lookup/Makefile b/unit-tests/test-cases/threaded-flat-lookup/Makefile new file mode 100644 index 0000000..1c8f897 --- /dev/null +++ b/unit-tests/test-cases/threaded-flat-lookup/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo1.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo2.dylib + ${CC} ${CCFLAGS} client.c -dynamiclib -o libclient.dylib -flat_namespace -undefined dynamic_lookup + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libclient.dylib + diff --git a/unit-tests/test-cases/threaded-lazy-bind/Makefile b/unit-tests/test-cases/threaded-lazy-bind/Makefile new file mode 100644 index 0000000..7b38cee --- /dev/null +++ b/unit-tests/test-cases/threaded-lazy-bind/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/tlv-basic/Makefile b/unit-tests/test-cases/tlv-basic/Makefile new file mode 100644 index 0000000..cf25175 --- /dev/null +++ b/unit-tests/test-cases/tlv-basic/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of thread-local-variables in a main executable +## + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} main + diff --git a/unit-tests/test-cases/tlv-dylib/Makefile b/unit-tests/test-cases/tlv-dylib/Makefile new file mode 100644 index 0000000..9011041 --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of thread-local-variables in a main executable +## + +all-check: all check + +check: + ./main + +all: + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + + +clean: + ${RM} ${RMFLAGS} libfoo.dylib main + diff --git a/unit-tests/test-cases/tlv-initializer/Makefile b/unit-tests/test-cases/tlv-initializer/Makefile new file mode 100644 index 0000000..6a6d74c --- /dev/null +++ b/unit-tests/test-cases/tlv-initializer/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of thread-local-variables in a main executable +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c get.s -o main + + +clean: + ${RM} ${RMFLAGS} main + diff --git a/unit-tests/test-cases/tlv-terminators/Makefile b/unit-tests/test-cases/tlv-terminators/Makefile new file mode 100644 index 0000000..9094732 --- /dev/null +++ b/unit-tests/test-cases/tlv-terminators/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Basic test of thread-local-variables in a main executable +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + clang ${CCFLAGS} -I${TESTROOT}/include main.c init.s -o main + + +clean: + ${RM} ${RMFLAGS} main + diff --git a/unit-tests/test-cases/trie-symbol-overrun/Makefile b/unit-tests/test-cases/trie-symbol-overrun/Makefile new file mode 100644 index 0000000..41fe1c1 --- /dev/null +++ b/unit-tests/test-cases/trie-symbol-overrun/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib -Wno-deprecated-declarations + + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib + diff --git a/unit-tests/test-cases/unloadable-library-residue/Makefile b/unit-tests/test-cases/unloadable-library-residue/Makefile new file mode 100644 index 0000000..42459e6 --- /dev/null +++ b/unit-tests/test-cases/unloadable-library-residue/Makefile @@ -0,0 +1,55 @@ +## +# 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 + +### +### rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +### +### We indirectly load libfoo which depends on libbar. The libbar that is loaded does not contain +### what libfoo needs, so libfoo fails to bind. The bug was that libfoo was left marked as bound, +### so the next use of libfoo seemed to succeed, when it should have failed. +### + +all-check: all check + +check: + export DYLD_IMAGE_SUFFIX="_missing" && ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c libbar_missing.dylib libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib libbar.dylib + +libbar_missing.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar_missing.dylib -install_name libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -DHAS_BAR2=1 + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbar_missing.dylib + diff --git a/unit-tests/test-cases/upward-dylib-init-order/Makefile b/unit-tests/test-cases/upward-dylib-init-order/Makefile new file mode 100644 index 0000000..bc48b5c --- /dev/null +++ b/unit-tests/test-cases/upward-dylib-init-order/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib common.c -o libcommon.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u2.c libcommon.dylib -o libu2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u.c libcommon.dylib -o libu.dylib -Wl,-upward_library,libu2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib c.c libcommon.dylib -o libc.dylib -Wl,-upward_library,libu.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib b.c libcommon.dylib -o libb.dylib libc.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libb.dylib libcommon.dylib -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libu.dylib libu2.dylib libb.dylib libc.dylib libcommon.dylib + + + diff --git a/unit-tests/test-cases/upward-dylib/Makefile b/unit-tests/test-cases/upward-dylib/Makefile new file mode 100644 index 0000000..b33a842 --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main1 + ./main2 + +all_1: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib up.c -DSTUB -o libup.stub -install_name libup.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib down.c -o libdown.dylib -Wl,-upward_library,libup.stub + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib up.c libdown.dylib -o libup.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libup.dylib libdown.dylib -o main1 + ${CC} ${CCFLAGS} -I${TESTROOT}/include main2.c libdown.dylib -o main2 + + +clean: + ${RM} ${RMFLAGS} *~ main1 main2 libup.dylib libdown.dylib libup.stub + diff --git a/unit-tests/test-cases/weak-coalesce-c++/Makefile b/unit-tests/test-cases/weak-coalesce-c++/Makefile new file mode 100644 index 0000000..253ea75 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-c++/Makefile @@ -0,0 +1,54 @@ +## +# 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: + ./dynamic-test + +all: dynamic-test static-test + +static-test: a1.o a2.o main.o + $(CXX) -I${TESTROOT}/include a1.o a2.o main.o -o static-test + +dynamic-test: main.o a1.o liba2.dylib + $(CXX) -I${TESTROOT}/include main.o a1.o -L. -la2 -o dynamic-test + +liba2.dylib: a2.o + $(CXX) -I${TESTROOT}/include a2.o -dynamiclib -o liba2.dylib + +a1.o: a1.cc a.h + $(CXX) -I${TESTROOT}/include -c a1.cc + +a2.o: a2.cc a.h + $(CXX) -I${TESTROOT}/include -c a2.cc + +main.o: main.cc + $(CXX) -I${TESTROOT}/include -c main.cc + + +clean: + ${RM} ${RMFLAGS} *.o *.dylib static-test dynamic-test diff --git a/unit-tests/test-cases/weak-coalesce-inserted/Makefile b/unit-tests/test-cases/weak-coalesce-inserted/Makefile new file mode 100644 index 0000000..2c3f872 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/Makefile @@ -0,0 +1,42 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + DYLD_INSERT_LIBRARIES=libfoo1.dylib:libfoo2.dylib ./main + +all: main + +main: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libbase.dylib + diff --git a/unit-tests/test-cases/weak-coalesce-stubs/Makefile b/unit-tests/test-cases/weak-coalesce-stubs/Makefile new file mode 100644 index 0000000..d55a5c5 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-stubs/Makefile @@ -0,0 +1,18 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: + $(CC) $(CCFLAGS) -I${TESTROOT}/include main.c -o main + $(CC) $(CCFLAGS) bar.c -dynamiclib -o libbar.dylib + $(CC) $(CCFLAGS) foo.c -dynamiclib -o libfoo.dylib + $(STRIP) -c -x libfoo.dylib -o libstub.dylib + +clean: + ${RM} ${RMFLAGS} libbar.dylib libfoo.dylib libstub.dylib main diff --git a/unit-tests/test-cases/weak-coalesce/Makefile b/unit-tests/test-cases/weak-coalesce/Makefile new file mode 100644 index 0000000..543a5be --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# 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 + diff --git a/unit-tests/test-cases/weak-external-reloc-flat/Makefile b/unit-tests/test-cases/weak-external-reloc-flat/Makefile new file mode 100644 index 0000000..b1e95b2 --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/Makefile @@ -0,0 +1,34 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# flat_namespace and weak binding conflict +# +# Note that libfoo.dylib is built flat-namespace +# + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib -flat_namespace + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/weak-external-reloc/Makefile b/unit-tests/test-cases/weak-external-reloc/Makefile new file mode 100644 index 0000000..b01a66b --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/weak-in-dylib/Makefile b/unit-tests/test-cases/weak-in-dylib/Makefile new file mode 100644 index 0000000..cd54661 --- /dev/null +++ b/unit-tests/test-cases/weak-in-dylib/Makefile @@ -0,0 +1,21 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/weak-lazy-slidable/Makefile b/unit-tests/test-cases/weak-lazy-slidable/Makefile new file mode 100644 index 0000000..274962b --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/Makefile @@ -0,0 +1,31 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib libother.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libother.dylib libfoo.dylib + +libother.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libother.dylib other.c + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbar3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbar3.dylib + +libbar3.dylib: bar3.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar3.dylib bar3.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbar3.dylib libother.dylib + diff --git a/unit-tests/test-cases/weak-library/Makefile b/unit-tests/test-cases/weak-library/Makefile new file mode 100644 index 0000000..1d1863e --- /dev/null +++ b/unit-tests/test-cases/weak-library/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + cd absent/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main + cd present/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main + +all: foo.c main.c + ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c + mkdir -p absent/ + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o absent/main main.c + mkdir -p present/ + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o present/main main.c + mv libfoo.dylib present/ + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib absent present diff --git a/unit-tests/test-cases/weak-non-lazy/Makefile b/unit-tests/test-cases/weak-non-lazy/Makefile new file mode 100644 index 0000000..c3d1b70 --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/weak-override/Makefile b/unit-tests/test-cases/weak-override/Makefile new file mode 100644 index 0000000..8d7799f --- /dev/null +++ b/unit-tests/test-cases/weak-override/Makefile @@ -0,0 +1,42 @@ +## +# 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 libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo.dylib foo.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/weak-symbol-flat/Makefile b/unit-tests/test-cases/weak-symbol-flat/Makefile new file mode 100644 index 0000000..dfd56ce --- /dev/null +++ b/unit-tests/test-cases/weak-symbol-flat/Makefile @@ -0,0 +1,46 @@ +# +# 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: + (export DYLD_IMAGE_SUFFIX=_missing && ./main) || echo "FAIL \"weak-symbol-flat\"" + ./main 1 + +all: main libfoo.dylib libfoo_missing.dylib + +libfoo.dylib : foo.c + ${CC} -dynamiclib -DSYMBOL_PRESENT -o libfoo.dylib foo.c + +libfoo_missing.dylib : foo.c + ${CC} -dynamiclib -o libfoo_missing.dylib foo.c -install_name libfoo.dylib + +main: main.c libfoo.dylib + ${CC} -I${TESTROOT}/include -L. -lfoo -o main main.c -flat_namespace + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main + + diff --git a/unit-tests/test-cases/weak-symbol/Makefile b/unit-tests/test-cases/weak-symbol/Makefile new file mode 100644 index 0000000..f2d1cec --- /dev/null +++ b/unit-tests/test-cases/weak-symbol/Makefile @@ -0,0 +1,38 @@ +## +# 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: + export DYLD_IMAGE_SUFFIX=_missing && ${TESTROOT}/bin/exit-zero-pass.pl "weak-symbol" "weak-symbol" ./main + +all: foo.c main.c + ${CC} foo.c -dynamiclib -o libfoo.dylib -DSYMBOL_PRESENT + ${CC} foo.c -dynamiclib -o libfoo_missing.dylib -install_name libfoo.dylib + ${CC} -I${TESTROOT}/include -L. -lfoo main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main diff --git a/unit-tests/test-cases/weak_import/Makefile b/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..7833962 --- /dev/null +++ b/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + +all-check: all check + +check: + ./main + export DYLD_IMAGE_SUFFIX=_some && ./main missing + +all: main libfoo_some.dylib + + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -DALL_SYMBOLS=1 -dynamiclib -o libfoo.dylib + +libfoo_some.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo_some.dylib -install_name libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo_some.dylib diff --git a/unit-tests/test-cases/zero-fill-segment/Makefile b/unit-tests/test-cases/zero-fill-segment/Makefile new file mode 100644 index 0000000..f0380c7 --- /dev/null +++ b/unit-tests/test-cases/zero-fill-segment/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c foo.bundle + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c zero.s + ${CC} ${CCFLAGS} -bundle foo.c zero.s -o foo.bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/zero-length-segment/Makefile b/unit-tests/test-cases/zero-length-segment/Makefile new file mode 100644 index 0000000..297c760 --- /dev/null +++ b/unit-tests/test-cases/zero-length-segment/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +SHELL = bash # use bash shell so we can redirect just stderr + + +all-check: all check + +check: + export DYLD_PRINT_SEGMENTS=1 && ./main 2> segments.log + grep __FOOBAR segments.log > /dev/null || echo "PASS zero-length-segment" + (grep __FOOBAR segments.log > /dev/null && echo "FAIL zero-length-segment") || /usr/bin/true + + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-sectcreate -Wl,__FOOBAR -Wl,__empty -Wl,/dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib segments.log + diff --git a/version.c b/version.c deleted file mode 100644 index 2fb7075..0000000 --- a/version.c +++ /dev/null @@ -1,3 +0,0 @@ -const char dyldVersionString[] = "@(#)PROGRAM:dyld PROJECT:dyld-733.6\r"; -const unsigned long long dyldVersionNumber = 0x4086ECCCCCCCCCCDull; -