From 412ebb8e3cc35d457058c31310d89ef96b7c416d Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 13 Jul 2011 00:07:10 +0000 Subject: [PATCH] dyld-195.5.tar.gz --- bin/set-alt-dyld | 30 + doc/man/man1/dyld.1 | 60 +- doc/man/man1/update_dyld_shared_cache.1 | 7 + doc/man/man3/dyld.3 | 7 +- dyld.xcodeproj/project.pbxproj | 582 +++++-- include/mach-o/dyld.h | 11 +- include/mach-o/dyld_images.h | 45 +- include/mach-o/dyld_priv.h | 76 +- launch-cache/CacheFileAbstraction.hpp | 75 +- launch-cache/FileAbstraction.hpp | 12 + launch-cache/MachOBinder.hpp | 411 ++++- launch-cache/MachOFileAbstraction.hpp | 34 +- launch-cache/MachOLayout.hpp | 272 ++-- launch-cache/MachORebaser.hpp | 156 +- launch-cache/MachOTrie.hpp | 119 +- launch-cache/ObjCLegacyAbstraction.hpp | 22 +- launch-cache/ObjCModernAbstraction.hpp | 1089 ++++++++++++- launch-cache/dsc_extractor.cpp | 443 ++++++ launch-cache/dsc_extractor.h | 43 + launch-cache/dsc_iterator.cpp | 81 +- launch-cache/dsc_iterator.h | 23 +- launch-cache/dsc_slider.cpp | 308 ++++ launch-cache/dsc_slider.h | 43 + launch-cache/dyld_cache_format.h | 37 +- launch-cache/dyld_shared_cache_util.cpp | 492 ++++++ launch-cache/update_dyld_shared_cache.cpp | 1088 ++++++++++--- src/ImageLoader.cpp | 161 +- src/ImageLoader.h | 92 +- src/ImageLoaderMachO.cpp | 313 ++-- src/ImageLoaderMachO.h | 35 +- src/ImageLoaderMachOClassic.cpp | 214 ++- src/ImageLoaderMachOClassic.h | 15 +- src/ImageLoaderMachOCompressed.cpp | 428 ++++- src/ImageLoaderMachOCompressed.h | 31 +- src/dyld.cpp | 1372 ++++++++++++++--- src/dyld.h | 9 +- src/dyld.order | 98 ++ src/dyldAPIs.cpp | 125 +- src/dyldAPIsInLibSystem.cpp | 72 +- src/dyldExceptions.c | 290 ++-- src/dyldInitialization.cpp | 178 +-- src/dyldLibSystemGlue.c | 31 +- src/dyldLibSystemInterface.h | 8 +- src/dyldStartup.s | 73 +- src/dyld_debug.c | 1 + src/dyld_gdb.cpp | 66 +- src/dyld_stub_binder.s | 28 + src/glue.c | 96 +- src/threadLocalHelpers.s | 126 ++ src/threadLocalVariables.c | 471 ++++++ unit-tests/bin/build-results-filter.pl | 114 ++ unit-tests/bin/exit-non-zero-pass.pl | 1 + unit-tests/bin/exit-zero-pass.pl | 1 + unit-tests/build-and-run-iPhoneOS-unit-tests | 14 + unit-tests/build-iPhoneOS-unit-tests | 52 + unit-tests/include/common.makefile | 63 +- unit-tests/run-all-unit-tests | 47 +- .../DYLD_LIBRARY_PATH-dyld_env/Makefile | 61 + .../DYLD_LIBRARY_PATH-dyld_env/foo.c | 5 + .../DYLD_LIBRARY_PATH-dyld_env/main.c | 47 + .../Makefile | 48 + .../DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c | 5 + .../main.c | 47 + .../Makefile | 48 + .../DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c | 5 + .../DYLD_VERSIONED_LIBRARY_PATH-basic/main.c | 47 + .../Makefile | 58 + .../foo.c | 5 + .../main.c | 47 + .../Makefile | 61 + .../foo.c | 5 + .../main.c | 47 + .../NSAddImage-MATCH_BY_INSTALLNAME/main.c | 14 +- .../NSAddImage-RETURN_ONLY_IF_LOADED/main.c | 12 +- .../test-cases/NSAddImage-leafname/main.c | 12 +- .../test-cases/NSAddressOfSymbol-NULL/main.c | 6 +- unit-tests/test-cases/addend/main.c | 1 + .../all_image_infos-cache-slide/Makefile | 37 + .../all_image_infos-cache-slide/main.c | 82 + .../all_image_infos-duplicate/Makefile | 38 + .../all_image_infos-duplicate/foo.c | 28 + .../all_image_infos-duplicate/main.c | 70 + .../test-cases/all_image_infos-paths/Makefile | 40 + .../test-cases/all_image_infos-paths/foo.c | 28 + .../test-cases/all_image_infos-paths/main.c | 68 + unit-tests/test-cases/all_image_infos/main.c | 51 +- .../test-cases/always-libSystem/Makefile | 5 +- unit-tests/test-cases/big-stack/Makefile | 2 +- unit-tests/test-cases/branch-islands/Makefile | 40 + unit-tests/test-cases/branch-islands/extra.c | 8 + unit-tests/test-cases/branch-islands/main.c | 26 + unit-tests/test-cases/branch-islands/space.s | 77 + unit-tests/test-cases/bundle-basic/main.c | 5 +- unit-tests/test-cases/bundle-dont-gc/main.c | 6 +- .../bundle-memory-load-all-infos/Makefile | 41 + .../bundle.c} | 15 +- .../bundle-memory-load-all-infos/main.c | 126 ++ .../test-cases/bundle-memory-load-bad/main.c | 10 +- .../test-cases/bundle-memory-load-fat/main.c | 4 + .../bundle-memory-load-malloc/main.c | 8 +- .../test-cases/bundle-memory-load/main.c | 4 + .../test-cases/bundle-multi-link/main.c | 8 +- .../test-cases/bundle-multi-load/main.c | 5 +- .../test-cases/bundle-name-ownership/main.c | 4 +- unit-tests/test-cases/bundle-private/main.c | 7 +- unit-tests/test-cases/bundle-reload/main.c | 6 + .../test-cases/bundle-unlinkable/main.c | 7 +- .../bundle-unload-keep-mapped/main.c | 7 +- unit-tests/test-cases/bundle-v-dylib/main.c | 13 +- .../coreSymbolication-notify/Makefile | 52 + .../test-cases/coreSymbolication-notify/bar.c | 2 + .../test-cases/coreSymbolication-notify/foo.c | 3 + .../main.c | 27 +- unit-tests/test-cases/crt-apple/Makefile | 37 +- unit-tests/test-cases/crt-apple/main.c | 38 +- unit-tests/test-cases/crt-argv-NULL/Makefile | 15 +- unit-tests/test-cases/crt-custom/Makefile | 16 +- unit-tests/test-cases/crt-custom/main.c | 14 +- unit-tests/test-cases/crt-custom/mystart.s | 25 +- unit-tests/test-cases/crt-libSystem/Makefile | 34 +- unit-tests/test-cases/crt-result/Makefile | 25 +- unit-tests/test-cases/cxa_finalize/foo.cxx | 3 +- unit-tests/test-cases/deadlock/main.c | 8 +- unit-tests/test-cases/dladdr/Makefile | 4 +- unit-tests/test-cases/dladdr/main.c | 7 + unit-tests/test-cases/dlopen-error/Makefile | 12 +- .../test-cases/dlopen-executable/Makefile | 2 +- .../dlopen-from-anonymous-code/main.c | 22 +- unit-tests/test-cases/dlopen-leak/bar.c | 3 + .../Makefile | 13 +- .../dlopen-non-canonical-path/main.c | 60 + .../test-cases/dlopen-notify-bind/Makefile | 2 +- .../test-cases/dlopen-notify-bind/main.c | 6 +- .../test-cases/dlopen-search-leak/Makefile | 71 + .../test-cases/dlopen-search-leak/foo.c | 3 + .../test-cases/dlopen-search-leak/main.c | 47 + .../dlopen_preflight-shared-cache/bar.c | 1 - .../test-cases/dtrace-static-probes/Makefile | 8 +- .../test-cases/dyld-func-lookup/Makefile | 6 +- unit-tests/test-cases/dyld-func-lookup/foo.c | 9 +- unit-tests/test-cases/dyld-func-lookup/main.c | 11 +- .../test-cases/dyld-launched-prebound/main.c | 5 +- unit-tests/test-cases/dyld-slide/Makefile | 16 +- unit-tests/test-cases/dyld-slide/main.c | 11 +- .../test-cases/fallback-with-suid/Makefile | 13 +- unit-tests/test-cases/flat-prebound/Makefile | 6 +- .../test-cases/framework-fallback/main.c | 8 +- unit-tests/test-cases/image-count/Makefile | 17 + unit-tests/test-cases/image-count/foo.c | 2 + unit-tests/test-cases/image-count/main.c | 83 + .../test-cases/image-state-deny-OFI/main.c | 7 + unit-tests/test-cases/image-suffix/main.c | 4 +- .../initializer-bounds-check/Makefile | 38 + .../test-cases/initializer-bounds-check/bar.c | 7 + .../initializer-bounds-check/foo1.c | 14 + .../initializer-bounds-check/foo2.c | 12 + .../initializer-bounds-check/main.c | 42 + .../insert-libraries-with-suid/Makefile | 18 +- unit-tests/test-cases/loader_path/main.c | 13 +- .../test-cases/partial-library-load/main.c | 4 +- .../test-cases/pie-DYLD_NO_PIE/Makefile | 34 - unit-tests/test-cases/pie-basic/Makefile | 2 +- unit-tests/test-cases/pie-big/Makefile | 2 +- unit-tests/test-cases/pie-big/main.c | 12 +- .../test-cases/pie-custom-stack/Makefile | 2 +- unit-tests/test-cases/pie-text-reloc/Makefile | 8 +- .../test-cases/prebased-performance/Makefile | 4 +- .../test-cases/re-export-dylib/Makefile | 32 +- .../test-cases/re-export-framework/Makefile | 38 +- .../re-export-sub-framework/Makefile | 38 +- .../test-cases/re-export-symbol/Makefile | 62 + unit-tests/test-cases/re-export-symbol/bar.c | 5 + unit-tests/test-cases/re-export-symbol/foo.c | 4 + .../test-cases/re-export-symbol/foo.exp | 2 + .../test-cases/re-export-symbol/foo2.exp | 2 + .../test-cases/re-export-symbol/main1.c | 23 + .../test-cases/re-export-symbol/main2.c | 26 + .../foo.c | 19 - .../main.c | 16 - unit-tests/test-cases/read-only-stubs/foo.c | 5 +- unit-tests/test-cases/read-only-stubs/main.c | 5 +- .../test-cases/restrict-environ/Makefile | 17 +- .../restrict-executable_path/Makefile | 15 +- .../rpath-dlopen-rm-executable/Makefile | 50 + .../rpath-dlopen-rm-executable/foo.c | 25 + .../rpath-dlopen-rm-executable/main.c | 50 + .../test-cases/rpath-indirect-suid/Makefile | 17 +- .../test-cases/rpath-install-name/Makefile | 55 + .../test-cases/rpath-install-name/bar.c | 29 + .../test-cases/rpath-install-name/main.c | 55 + .../test-cases/rpath-install-name/stuff.c | 37 + .../test-cases/shared-cache-symlink/main.c | 21 +- .../test-cases/shared-region-overlap/Makefile | 47 + .../test-cases/shared-region-overlap/main.c | 12 + unit-tests/test-cases/suid-environ/Makefile | 21 +- .../test-cases/suid-executable_path/Makefile | 14 +- .../test-cases/symbol-resolver-basic/Makefile | 55 + .../test-cases/symbol-resolver-basic/foo.c | 47 + .../test-cases/symbol-resolver-basic/foo2.c | 33 + .../test-cases/symbol-resolver-basic/main.c | 53 + .../symbol-resolver-pointer/Makefile | 54 + .../test-cases/symbol-resolver-pointer/foo.c | 64 + .../test-cases/symbol-resolver-pointer/main.c | 35 + .../Makefile | 18 +- unit-tests/test-cases/text-relocs-perms/foo.c | 101 ++ .../test-cases/text-relocs-perms/main.c | 109 ++ unit-tests/test-cases/text-relocs/Makefile | 20 +- unit-tests/test-cases/text-relocs/bar.c | 6 + unit-tests/test-cases/text-relocs/bind.c | 7 + unit-tests/test-cases/text-relocs/space.s | 2 + .../test-cases/threaded-flat-lookup/Makefile | 41 + .../test-cases/threaded-flat-lookup/client.c | 11 + .../test-cases/threaded-flat-lookup/foo.c | 4 + .../test-cases/threaded-flat-lookup/main.c | 95 ++ unit-tests/test-cases/tlv-basic/Makefile | 44 + unit-tests/test-cases/tlv-basic/main.c | 82 + unit-tests/test-cases/tlv-dylib/Makefile | 43 + unit-tests/test-cases/tlv-dylib/foo.c | 8 + unit-tests/test-cases/tlv-dylib/main.c | 82 + .../test-cases/tlv-initializer/Makefile | 50 + unit-tests/test-cases/tlv-initializer/get.s | 99 ++ unit-tests/test-cases/tlv-initializer/main.c | 77 + .../test-cases/tlv-terminators/Makefile | 50 + unit-tests/test-cases/tlv-terminators/init.s | 10 + unit-tests/test-cases/tlv-terminators/main.c | 154 ++ .../test-cases/trie-symbol-overrun/main.c | 7 +- .../unloadable-library-residue/main.c | 13 +- unit-tests/test-cases/upward-dylib/Makefile | 52 + unit-tests/test-cases/upward-dylib/down.c | 22 + unit-tests/test-cases/upward-dylib/down.h | 2 + unit-tests/test-cases/upward-dylib/main.c | 19 + unit-tests/test-cases/upward-dylib/up.c | 21 + unit-tests/test-cases/upward-dylib/up.h | 2 + .../test-cases/weak-coalesce-stubs/Makefile | 18 + .../test-cases/weak-coalesce-stubs/bar.c | 6 + .../test-cases/weak-coalesce-stubs/foo.c | 61 + .../test-cases/weak-coalesce-stubs/main.c | 22 + 237 files changed, 12836 insertions(+), 2223 deletions(-) create mode 100755 bin/set-alt-dyld create mode 100644 launch-cache/dsc_extractor.cpp create mode 100644 launch-cache/dsc_extractor.h create mode 100644 launch-cache/dsc_slider.cpp create mode 100644 launch-cache/dsc_slider.h create mode 100644 launch-cache/dyld_shared_cache_util.cpp create mode 100644 src/dyld.order create mode 100644 src/threadLocalHelpers.s create mode 100644 src/threadLocalVariables.c create mode 100755 unit-tests/bin/build-results-filter.pl create mode 100755 unit-tests/build-and-run-iPhoneOS-unit-tests create mode 100755 unit-tests/build-iPhoneOS-unit-tests create mode 100644 unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile create mode 100644 unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c create mode 100644 unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/Makefile create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c create mode 100644 unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c create mode 100644 unit-tests/test-cases/all_image_infos-cache-slide/Makefile create mode 100644 unit-tests/test-cases/all_image_infos-cache-slide/main.c create mode 100644 unit-tests/test-cases/all_image_infos-duplicate/Makefile create mode 100644 unit-tests/test-cases/all_image_infos-duplicate/foo.c create mode 100644 unit-tests/test-cases/all_image_infos-duplicate/main.c create mode 100644 unit-tests/test-cases/all_image_infos-paths/Makefile create mode 100644 unit-tests/test-cases/all_image_infos-paths/foo.c create mode 100644 unit-tests/test-cases/all_image_infos-paths/main.c create mode 100644 unit-tests/test-cases/branch-islands/Makefile create mode 100644 unit-tests/test-cases/branch-islands/extra.c create mode 100644 unit-tests/test-cases/branch-islands/main.c create mode 100644 unit-tests/test-cases/branch-islands/space.s create mode 100644 unit-tests/test-cases/bundle-memory-load-all-infos/Makefile rename unit-tests/test-cases/{pie-DYLD_NO_PIE/main.c => bundle-memory-load-all-infos/bundle.c} (82%) create mode 100644 unit-tests/test-cases/bundle-memory-load-all-infos/main.c create mode 100644 unit-tests/test-cases/coreSymbolication-notify/Makefile create mode 100644 unit-tests/test-cases/coreSymbolication-notify/bar.c create mode 100644 unit-tests/test-cases/coreSymbolication-notify/foo.c rename unit-tests/test-cases/{dlopen_preflight-shared-cache => coreSymbolication-notify}/main.c (65%) rename unit-tests/test-cases/{dlopen_preflight-shared-cache => dlopen-non-canonical-path}/Makefile (86%) create mode 100644 unit-tests/test-cases/dlopen-non-canonical-path/main.c create mode 100644 unit-tests/test-cases/dlopen-search-leak/Makefile create mode 100644 unit-tests/test-cases/dlopen-search-leak/foo.c create mode 100644 unit-tests/test-cases/dlopen-search-leak/main.c delete mode 100644 unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c create mode 100644 unit-tests/test-cases/image-count/Makefile create mode 100644 unit-tests/test-cases/image-count/foo.c create mode 100644 unit-tests/test-cases/image-count/main.c create mode 100644 unit-tests/test-cases/initializer-bounds-check/Makefile create mode 100644 unit-tests/test-cases/initializer-bounds-check/bar.c create mode 100644 unit-tests/test-cases/initializer-bounds-check/foo1.c create mode 100644 unit-tests/test-cases/initializer-bounds-check/foo2.c create mode 100644 unit-tests/test-cases/initializer-bounds-check/main.c delete mode 100644 unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile create mode 100644 unit-tests/test-cases/re-export-symbol/Makefile create mode 100644 unit-tests/test-cases/re-export-symbol/bar.c create mode 100644 unit-tests/test-cases/re-export-symbol/foo.c create mode 100644 unit-tests/test-cases/re-export-symbol/foo.exp create mode 100644 unit-tests/test-cases/re-export-symbol/foo2.exp create mode 100644 unit-tests/test-cases/re-export-symbol/main1.c create mode 100644 unit-tests/test-cases/re-export-symbol/main2.c delete mode 100644 unit-tests/test-cases/read-only-import-shared-cache-override/foo.c delete mode 100644 unit-tests/test-cases/read-only-import-shared-cache-override/main.c create mode 100644 unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile create mode 100644 unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c create mode 100644 unit-tests/test-cases/rpath-dlopen-rm-executable/main.c create mode 100644 unit-tests/test-cases/rpath-install-name/Makefile create mode 100644 unit-tests/test-cases/rpath-install-name/bar.c create mode 100644 unit-tests/test-cases/rpath-install-name/main.c create mode 100644 unit-tests/test-cases/rpath-install-name/stuff.c create mode 100644 unit-tests/test-cases/shared-region-overlap/Makefile create mode 100644 unit-tests/test-cases/shared-region-overlap/main.c create mode 100644 unit-tests/test-cases/symbol-resolver-basic/Makefile create mode 100644 unit-tests/test-cases/symbol-resolver-basic/foo.c create mode 100644 unit-tests/test-cases/symbol-resolver-basic/foo2.c create mode 100644 unit-tests/test-cases/symbol-resolver-basic/main.c create mode 100644 unit-tests/test-cases/symbol-resolver-pointer/Makefile create mode 100644 unit-tests/test-cases/symbol-resolver-pointer/foo.c create mode 100644 unit-tests/test-cases/symbol-resolver-pointer/main.c rename unit-tests/test-cases/{read-only-import-shared-cache-override => text-relocs-perms}/Makefile (72%) create mode 100644 unit-tests/test-cases/text-relocs-perms/foo.c create mode 100644 unit-tests/test-cases/text-relocs-perms/main.c create mode 100644 unit-tests/test-cases/text-relocs/bind.c create mode 100644 unit-tests/test-cases/text-relocs/space.s create mode 100644 unit-tests/test-cases/threaded-flat-lookup/Makefile create mode 100644 unit-tests/test-cases/threaded-flat-lookup/client.c create mode 100644 unit-tests/test-cases/threaded-flat-lookup/foo.c create mode 100644 unit-tests/test-cases/threaded-flat-lookup/main.c create mode 100644 unit-tests/test-cases/tlv-basic/Makefile create mode 100644 unit-tests/test-cases/tlv-basic/main.c create mode 100644 unit-tests/test-cases/tlv-dylib/Makefile create mode 100644 unit-tests/test-cases/tlv-dylib/foo.c create mode 100644 unit-tests/test-cases/tlv-dylib/main.c create mode 100644 unit-tests/test-cases/tlv-initializer/Makefile create mode 100644 unit-tests/test-cases/tlv-initializer/get.s create mode 100644 unit-tests/test-cases/tlv-initializer/main.c create mode 100644 unit-tests/test-cases/tlv-terminators/Makefile create mode 100644 unit-tests/test-cases/tlv-terminators/init.s create mode 100644 unit-tests/test-cases/tlv-terminators/main.c create mode 100644 unit-tests/test-cases/upward-dylib/Makefile create mode 100644 unit-tests/test-cases/upward-dylib/down.c create mode 100644 unit-tests/test-cases/upward-dylib/down.h create mode 100644 unit-tests/test-cases/upward-dylib/main.c create mode 100644 unit-tests/test-cases/upward-dylib/up.c create mode 100644 unit-tests/test-cases/upward-dylib/up.h create mode 100644 unit-tests/test-cases/weak-coalesce-stubs/Makefile create mode 100644 unit-tests/test-cases/weak-coalesce-stubs/bar.c create mode 100644 unit-tests/test-cases/weak-coalesce-stubs/foo.c create mode 100644 unit-tests/test-cases/weak-coalesce-stubs/main.c diff --git a/bin/set-alt-dyld b/bin/set-alt-dyld new file mode 100755 index 0000000..032bcf9 --- /dev/null +++ b/bin/set-alt-dyld @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w + +use strict; +undef $/; + +if(@ARGV == 0) +{ + print "Usage: $0 [ ...]\n"; + exit 1; +} + +my $arg; +foreach $arg (@ARGV) +{ + open IN, "<$arg" or die $!; + my $in = ; + close IN or die $!; + + if($in =~ s{/usr/lib/dyld}{/usr/lib/dyle}) + { + open OUT, ">$arg" or die $!; + print OUT $in; + close OUT or die $!; + } + else + { + print STDERR "ERROR: $arg\n"; + exit 1; + } +} diff --git a/doc/man/man1/dyld.1 b/doc/man/man1/dyld.1 index 8570d9a..b89239d 100644 --- a/doc/man/man1/dyld.1 +++ b/doc/man/man1/dyld.1 @@ -1,4 +1,4 @@ -.TH DYLD 1 "November 25, 2008" "Apple Inc." +.TH DYLD 1 "December 14, 2009" "Apple Inc." .SH NAME dyld \- the dynamic link editor .SH SYNOPSIS @@ -6,10 +6,14 @@ DYLD_FRAMEWORK_PATH .br DYLD_FALLBACK_FRAMEWORK_PATH .br +DYLD_VERSIONED_FRAMEWORK_PATH +.br DYLD_LIBRARY_PATH .br DYLD_FALLBACK_LIBRARY_PATH .br +DYLD_VERSIONED_LIBRARY_PATH +.br DYLD_ROOT_PATH .br DYLD_SHARED_REGION @@ -48,7 +52,7 @@ DYLD_PRINT_STATISTICS .br DYLD_PRINT_DOFS .br -DYLD_NO_PIE +DYLD_PRINT_RPATHS .br DYLD_SHARED_CACHE_DIR .br @@ -93,6 +97,17 @@ path. By default, it is set to /Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks .TP +.B DYLD_VERSIONED_FRAMEWORK_PATH +This is a colon separated list of directories that contain potential override frameworks. +The dynamic linker searches these directories for frameworks. For +each framework found dyld looks at its LC_ID_DYLIB and gets the current_version +and install name. Dyld then looks for the framework at the install name path. +Whichever has the larger current_version value will be used in the process whenever +a framework with that install name is required. This is similar to DYLD_FRAMEWORK_PATH +except instead of always overriding, it only overrides is the supplied framework is newer. +Note: dyld does not check the framework's Info.plist to find its version. Dyld only +checks the -currrent_version number supplied when the framework was created. +.TP .B DYLD_LIBRARY_PATH This is a colon separated list of directories that contain libraries. The dynamic linker searches these directories before it searches the default @@ -122,6 +137,15 @@ path. By default, it is set to $(HOME)/lib:/usr/local/lib:/lib:/usr/lib. .TP +.B DYLD_VERSIONED_LIBRARY_PATH +This is a colon separated list of directories that contain potential override libraries. +The dynamic linker searches these directories for dynamic libraries. For +each library found dyld looks at its LC_ID_DYLIB and gets the current_version +and install name. Dyld then looks for the library at the install name path. +Whichever has the larger current_version value will be used in the process whenever +a dylib with that install name is required. This is similar to DYLD_LIBRARY_PATH +except instead of always overriding, it only overrides is the supplied library is newer. +.TP .B DYLD_ROOT_PATH This is a colon separated list of directories. The dynamic linker will prepend each of this directory paths to every image access until a file is found. @@ -216,10 +240,9 @@ Causes dyld to print a line each time a symbolic name is bound. .B DYLD_PRINT_DOFS Causes dyld to print out information about dtrace static probes registered with the kernel. .TP -.B DYLD_NO_PIE -Causes dyld to not randomize the load addresses of images in a process where the main -executable was built position independent. This can be helpful when trying to reproduce -and debug a problem in a PIE. +.B DYLD_PRINT_RPATHS +Cause dyld to print a line each time it expands an @rpath variable and whether +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 @@ -230,6 +253,7 @@ to run a process with an alternate shared cache. 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. +.TP .SH DYNAMIC LIBRARY LOADING Unlike many other operating systems, Darwin does not locate dependent dynamic libraries via their leaf file name. Instead the full path to each dylib is used (e.g. /usr/lib/libSystem.B.dylib). @@ -240,17 +264,25 @@ substitutes a dynamically generated path for the @xxx/ prefix. .TP .B @executable_path/ This variable is replaced with the path to the directory containing the main executable for -the process. This is useful for .app directories where the main executable is in a well -known location inside the .app directory. A typical load path for an embedded framework would -look like @executable_path/../Frameworks/Foo.framework/Versions/A/Foo. +the process. This is useful for loading dylibs/frameworks embedded in a .app directory. +If the main executable file is at /some/path/My.app/Contents/MacOS/My and a framework dylib +file is at /some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo, then +the framework load path could be encoded as +@executable_path/../Frameworks/Foo.framework/Versions/A/Foo and the .app directory could be +moved around in the file system and dyld will still be able to load the embedded framework. .TP .B @loader_path/ This variable is replaced with the path to the directory containing the mach-o binary which -contains the load path. This is useful for a plug-in that has an embedded framework. @executable_path/ -is not helpful because you may not know where the plugin-in will be installed relative to the -main executable, or there may be multiple applications that load the plug-in. -A typical load path for an embedded framework for reference a sibling framework would -look like @loader_path/../../../Frameworks/Foo.framework/Versions/A/Foo. +contains the load command using @loader_path. Thus, in every binary, @loader_path resolves to +a different path, whereas @executable_path always resolves to the same path. @loader_path is +useful as the load path for a framework/dylib embedded in a plug-in, if the final file +system location of the plugin-in unknown (so absolute paths cannot be used) or if the plug-in +is used by multiple applications (so @executable_path cannot be used). If the plug-in mach-o +file is at /some/path/Myfilter.plugin/Contents/MacOS/Myfilter and a framework dylib +file is at /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo, then +the framework load path could be encoded as +@loader_path/../Frameworks/Foo.framework/Versions/A/Foo and the Myfilter.plugin directory could +be moved around in the file system and dyld will still be able to load the embedded framework. .TP .B @rpath/ Dyld maintains a current stack of paths called the run path list. When @rpath is encountered diff --git a/doc/man/man1/update_dyld_shared_cache.1 b/doc/man/man1/update_dyld_shared_cache.1 index f5baa1b..9d524ad 100644 --- a/doc/man/man1/update_dyld_shared_cache.1 +++ b/doc/man/man1/update_dyld_shared_cache.1 @@ -15,6 +15,8 @@ .Op Fl universal_boot .Op Fl verify .Op Fl dylib_list Ar file +.Op Fl iPhone +.Op Fl cache_dir Ar dir .Sh DESCRIPTION .Nm update_dyld_shared_cache ensures that dyld's shared cache is up-to-date. This tool is normally @@ -93,6 +95,11 @@ a list of the dylibs to use when building the shared cache file. Will regenerate a shared cache in-memory that matches the randomization of the existing shared cache file. Then instead of writing the cache file, it compares the in-memory cache file to the on disk version and reports any differences. +.It Fl iPhone +indicates that cache is not for the current Mac OS X, but for rather for an iPhone +.It Fl cache_dir Ar directory +This option specifies the directory in which to create the cache file(s). If not specified, +the cache file(s) are created in the standard location (e.g. var/db/dyld/) of the root partition. .El .Sh FILES .Tp diff --git a/doc/man/man3/dyld.3 b/doc/man/man3/dyld.3 index 285a70c..cb9f6e4 100644 --- a/doc/man/man3/dyld.3 +++ b/doc/man/man3/dyld.3 @@ -1,4 +1,4 @@ -.Dd February 12, 2009 +.Dd November 29, 2010 .Dt dyld 3 .Sh NAME .Nm _dyld_image_count, @@ -107,7 +107,10 @@ copies the path of the main executable into the buffer .Fa buf . The .Fa bufsize -parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied. +parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied, +and * +.Fa bufsize +is left unchanged. It returns -1 if the buffer is not large enough, and * .Fa bufsize is set to the size required. diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index 75cd83f..2165020 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -3,10 +3,27 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 45; objects = { /* Begin PBXAggregateTarget section */ + F908134211D3ED0B00626CC1 /* libdyld */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */; + buildPhases = ( + F908135111D3ED9000626CC1 /* usr|include|mach-o */, + F908137011D3FB5000626CC1 /* usr|include */, + F908137111D3FB5000626CC1 /* usr|local|include|mach-o */, + F908137211D3FB5000626CC1 /* usr|share|man|man1 */, + F908137311D3FB5000626CC1 /* usr|share|man|man3 */, + ); + dependencies = ( + F9B4D78012AD9736000605A6 /* PBXTargetDependency */, + F908134811D3ED1A00626CC1 /* PBXTargetDependency */, + ); + name = libdyld; + productName = libdyld; + }; F9ED4C920630A73900DF4E74 /* all */ = { isa = PBXAggregateTarget; buildConfigurationList = F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */; @@ -23,31 +40,38 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - EF79A010070D293E00F78484 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; }; - EF79A011070D295200F78484 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; }; - EF79A012070D295200F78484 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; }; - EF79A013070D295200F78484 /* dlerror.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; }; - EF79A014070D295200F78484 /* dlopen.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; }; - EF79A015070D295200F78484 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; }; - EF79A016070D295200F78484 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; }; F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = F906E2230639E96400B13DB2 /* dyld_debug.c */; }; + F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; }; + F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; }; + F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; }; + F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; }; + F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F93937400A94FC4700070A07 /* dyld_cache_format.h */; }; + F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; }; + F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; }; + F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; }; + F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; }; + F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; }; + F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; }; + F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; }; + F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; }; + F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; }; + F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; - F918691608B16D3500E0F9DB /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; }; F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */; }; - F93AA9A30630AE1E00301D9F /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; }; - F93AA9A40630AE1E00301D9F /* dyld_priv.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; }; - F93AA9A50630AE1E00301D9F /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; }; 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"; }; }; - F9574CB306C95C1B00142BFA /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; }; F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; }; - F98D274D0AA79D7400416316 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; }; - F99EFC0E0EAD60E8001032B8 /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; }; + F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */; }; + F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */; }; - F9AC7E940B7BB67700FEB38B /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = F9AC7E930B7BB67700FEB38B /* version.c */; }; + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; }; + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; }; + F9B0912911F11D3400096D49 /* dsc_slider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B0912811F11D3400096D49 /* dsc_slider.cpp */; }; + F9B0913911F11DD300096D49 /* dsc_slider.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9B0913811F11DAB00096D49 /* dsc_slider.h */; }; F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; }; + 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 */; }; F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; - F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */; }; F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC70630A7F100DF4E74 /* dyld.cpp */; }; F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */; }; @@ -107,6 +131,13 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; + remoteInfo = libdyld.dylib; + }; F93937370A94FB6A00070A07 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; @@ -114,12 +145,33 @@ remoteGlobalIDString = F93937310A94FAF700070A07; remoteInfo = update_dyld_shared_cache; }; - F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */ = { + F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; proxyType = 1; remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; - remoteInfo = dsc; + remoteInfo = libdsc; + }; + F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; + remoteInfo = libdsc; }; F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -138,87 +190,90 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - F90CF2950E71D1FB000BF0F1 /* usr|local|include */ = { + F908135111D3ED9000626CC1 /* usr|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/local/include; + dstPath = "/usr/include/mach-o"; dstSubfolderSpec = 0; files = ( + F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */, + F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */, ); - name = "usr|local|include"; + name = "usr|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9B30630AE8200301D9F /* usr|include|mach-o */ = { + F908137011D3FB5000626CC1 /* usr|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "/usr/include/mach-o"; + dstPath = /usr/include; dstSubfolderSpec = 0; files = ( - F93AA9A50630AE1E00301D9F /* dyld.h in usr|include|mach-o */, - F98D274D0AA79D7400416316 /* dyld_images.h in usr|include|mach-o */, + F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */, ); - name = "usr|include|mach-o"; + name = "usr|include"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9B60630AEB100301D9F /* usr|local|include|mach-o */ = { + F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = "/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( - F918691608B16D3500E0F9DB /* dyld-interposing.h in usr|local|include|mach-o */, - F93AA9A30630AE1E00301D9F /* dyld_gdb.h in usr|local|include|mach-o */, - F93AA9A40630AE1E00301D9F /* dyld_priv.h in usr|local|include|mach-o */, + F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */, + F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */, + F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */, + F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */, ); name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9C20630AF0700301D9F /* usr|share|man|man1 */ = { + F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - EF79A010070D293E00F78484 /* dyld.1 in usr|share|man|man1 */, + F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */, ); name = "usr|share|man|man1"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9C60630AF1F00301D9F /* usr|share|man|man3 */ = { + F908137311D3FB5000626CC1 /* usr|share|man|man3 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man3; dstSubfolderSpec = 0; files = ( - EF79A011070D295200F78484 /* dladdr.3 in usr|share|man|man3 */, - EF79A012070D295200F78484 /* dlclose.3 in usr|share|man|man3 */, - EF79A013070D295200F78484 /* dlerror.3 in usr|share|man|man3 */, - EF79A014070D295200F78484 /* dlopen.3 in usr|share|man|man3 */, - EF79A015070D295200F78484 /* dlsym.3 in usr|share|man|man3 */, - EF79A016070D295200F78484 /* dyld.3 in usr|share|man|man3 */, - F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in usr|share|man|man3 */, + F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */, + F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */, + F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */, + F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */, + F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */, + F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */, + F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */, ); name = "usr|share|man|man3"; runOnlyForDeploymentPostprocessing = 1; }; - F9574CB206C95C0D00142BFA /* usr|include */ = { + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/include; + dstPath = "$(INSTALL_LOCATION)/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( - F9574CB306C95C1B00142BFA /* dlfcn.h in usr|include */, + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, + F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */, ); - name = "usr|include"; + name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { + F9B0913511F11D8B00096D49 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "/usr/local/include/mach-o"; + dstPath = "usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( - F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, + F9B0913911F11DD300096D49 /* dsc_slider.h in usr|local|include|mach-o */, ); name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; @@ -226,7 +281,7 @@ F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man1; + dstPath = "$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; dstSubfolderSpec = 0; files = ( F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, @@ -263,19 +318,29 @@ F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = ""; }; F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; }; F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; + F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = ""; }; F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = ""; }; F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = ""; }; + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 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; }; F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = ""; }; F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = ""; }; + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = ""; }; + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = ""; }; 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; }; + F9B0912311F11D1600096D49 /* libdsc_slider.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc_slider.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9B0912811F11D3400096D49 /* dsc_slider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_slider.cpp; sourceTree = ""; }; + F9B0913811F11DAB00096D49 /* dsc_slider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_slider.h; sourceTree = ""; }; + 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 = ""; }; F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = ""; }; F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = ""; }; F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; }; - F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdyldapis.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; }; F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; }; F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; }; @@ -308,6 +373,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F99B8E540FEC10F600701838 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -370,6 +442,11 @@ F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */, F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, + F9B0912811F11D3400096D49 /* dsc_slider.cpp */, + F9B0913811F11DAB00096D49 /* dsc_slider.h */, + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */, + F9CE30791208F1B50098B590 /* dsc_extractor.h */, + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */, ); path = "launch-cache"; sourceTree = ""; @@ -389,9 +466,11 @@ isa = PBXGroup; children = ( F9ED4C980630A76000DF4E74 /* dyld */, - F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */, + F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */, F93937320A94FAF700070A07 /* update_dyld_shared_cache */, F9F2A5590F7AEE9800B7C9EB /* libdsc.a */, + F99B8E670FEC121100701838 /* dyld_shared_cache_util */, + F9B0912311F11D1600096D49 /* libdsc_slider.a */, ); name = Products; sourceTree = ""; @@ -423,10 +502,13 @@ F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */, F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */, F9B01E3D0739ABDE00CF981B /* dyld.exp */, + F976F548127B90F8004BA2A5 /* dyld.order */, F9AC7E930B7BB67700FEB38B /* version.c */, F918691408B16D2500E0F9DB /* dyld-interposing.h */, F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */, F906E2230639E96400B13DB2 /* dyld_debug.c */, + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */, + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */, ); name = src; sourceTree = ""; @@ -462,22 +544,59 @@ F939372F0A94FAF700070A07 /* Sources */, F93937300A94FAF700070A07 /* Frameworks */, F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */, ); buildRules = ( ); dependencies = ( - F98C78DA0F7C017F006257D2 /* PBXTargetDependency */, + F99B8EA00FEC195800701838 /* PBXTargetDependency */, + F9CE330B120F40EA0098B590 /* PBXTargetDependency */, ); name = update_dyld_shared_cache; productName = update_dyld_shared_cache; productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */; productType = "com.apple.product-type.tool"; }; + F99B8E550FEC10F600701838 /* dyld_shared_cache_util */ = { + isa = PBXNativeTarget; + buildConfigurationList = F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */; + buildPhases = ( + F99B8E530FEC10F600701838 /* Sources */, + F99B8E540FEC10F600701838 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_shared_cache_util; + productName = dscutil; + productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */; + productType = "com.apple.product-type.tool"; + }; + F9B0912211F11D1600096D49 /* libdsc_slider */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9B0912A11F11D3400096D49 /* Build configuration list for PBXNativeTarget "libdsc_slider" */; + buildPhases = ( + F9B0912011F11D1600096D49 /* Sources */, + F9B0913511F11D8B00096D49 /* usr|local|include|mach-o */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdsc_slider; + productName = libdsc_slider; + productReference = F9B0912311F11D1600096D49 /* libdsc_slider.a */; + productType = "com.apple.product-type.library.static"; + }; F9ED4C970630A76000DF4E74 /* dyld */ = { isa = PBXNativeTarget; buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */; buildPhases = ( + F9D050C811DD701A00FB0A29 /* configure archives */, F9ED4C950630A76000DF4E74 /* Sources */, + F907E2490FA6469000BFEDBD /* install iPhone file */, + F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */, ); buildRules = ( F921D318070376B0000D1056 /* PBXBuildRule */, @@ -485,25 +604,18 @@ F921D3160703769A000D1056 /* PBXBuildRule */, ); dependencies = ( + F99B8EB20FEC220C00701838 /* PBXTargetDependency */, ); name = dyld; productName = dyld; productReference = F9ED4C980630A76000DF4E74 /* dyld */; productType = "com.apple.product-type.tool"; }; - F9ED4C9E0630A76B00DF4E74 /* libdyld */ = { + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */ = { isa = PBXNativeTarget; - buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */; + buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */; buildPhases = ( - F9AC7E7E0B7BB3D300FEB38B /* create version.c */, F9ED4C9C0630A76B00DF4E74 /* Sources */, - F93AA9B30630AE8200301D9F /* usr|include|mach-o */, - F9574CB206C95C0D00142BFA /* usr|include */, - F90CF2950E71D1FB000BF0F1 /* usr|local|include */, - F93AA9B60630AEB100301D9F /* usr|local|include|mach-o */, - F93AA9C20630AF0700301D9F /* usr|share|man|man1 */, - F93AA9C60630AF1F00301D9F /* usr|share|man|man3 */, - F918692408B16F6900E0F9DB /* install symlinks */, ); buildRules = ( F921D31E070376F1000D1056 /* PBXBuildRule */, @@ -511,14 +623,14 @@ ); dependencies = ( ); - name = libdyld; + name = libdyld.dylib; productName = libdyld; - productReference = F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */; - productType = "com.apple.product-type.library.static"; + productReference = F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */; + productType = "com.apple.product-type.library.dynamic"; }; - F9F2A5580F7AEE9800B7C9EB /* dsc */ = { + F9F2A5580F7AEE9800B7C9EB /* libdsc */ = { isa = PBXNativeTarget; - buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */; + buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */; buildPhases = ( F9F2A5560F7AEE9800B7C9EB /* Sources */, F9F2A5570F7AEE9800B7C9EB /* Frameworks */, @@ -528,7 +640,7 @@ ); dependencies = ( ); - name = dsc; + name = libdsc; productName = dsc; productReference = F9F2A5590F7AEE9800B7C9EB /* libdsc.a */; productType = "com.apple.product-type.library.static"; @@ -539,8 +651,15 @@ F9ED4C8B0630A72300DF4E74 /* Project object */ = { isa = PBXProject; buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */; - compatibilityVersion = "Xcode 2.4"; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = F9ED4C870630A72200DF4E74; productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; projectDirPath = ""; @@ -548,43 +667,76 @@ targets = ( F9ED4C920630A73900DF4E74 /* all */, F9ED4C970630A76000DF4E74 /* dyld */, - F9ED4C9E0630A76B00DF4E74 /* libdyld */, + F908134211D3ED0B00626CC1 /* libdyld */, + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */, F93937310A94FAF700070A07 /* update_dyld_shared_cache */, - F9F2A5580F7AEE9800B7C9EB /* dsc */, + F9F2A5580F7AEE9800B7C9EB /* libdsc */, + F99B8E550FEC10F600701838 /* dyld_shared_cache_util */, + F9B0912211F11D1600096D49 /* libdsc_slider */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - F918692408B16F6900E0F9DB /* install symlinks */ = { + F907E2490FA6469000BFEDBD /* install iPhone file */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "install symlinks"; + name = "install iPhone file"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "cd ${DSTROOT}/usr/local/lib/system\nln -sf libdyldapis.a libdyldapis_profile.a\nln -sf libdyldapis.a libdyldapis_debug.a\n"; + shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphoneos\" ] \nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"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; }; - F9AC7E7E0B7BB3D300FEB38B /* create version.c */ = { + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + 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\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/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\n\trm -rf ${DSTROOT}/usr/local\nfi\n"; + showEnvVarsInLog = 0; + }; + F9D050C811DD701A00FB0A29 /* configure archives */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; files = ( ); inputPaths = ( ); - name = "create version.c"; + name = "configure archives"; outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/version.c", + "$(DERIVED_SOURCES_DIR)/archives.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/Developer/Makefiles/bin/version.pl ${ProjectName} > ${BUILT_PRODUCTS_DIR}/version.c\n"; + shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -598,6 +750,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F99B8E530FEC10F600701838 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */, + F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9B0912011F11D1600096D49 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9B0912911F11D3400096D49 /* dsc_slider.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9ED4C950630A76000DF4E74 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -613,7 +782,6 @@ F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */, F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */, F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */, - F99EFC0E0EAD60E8001032B8 /* dyld_stub_binder.s in Sources */, F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */, F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */, ); @@ -624,11 +792,12 @@ buildActionMask = 2147483647; files = ( F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */, - F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, - F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */, F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */, - F9AC7E940B7BB67700FEB38B /* version.c in Sources */, F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */, + F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, + F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */, + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */, + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -637,21 +806,42 @@ buildActionMask = 2147483647; files = ( F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */, + F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; + targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */; + }; F93937380A94FB6A00070A07 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */; targetProxy = F93937370A94FB6A00070A07 /* PBXContainerItemProxy */; }; - F98C78DA0F7C017F006257D2 /* PBXTargetDependency */ = { + F99B8EA00FEC195800701838 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */; + }; + F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */; + }; + F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */; + }; + F9CE330B120F40EA0098B590 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F9F2A5580F7AEE9800B7C9EB /* dsc */; - targetProxy = F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */; }; F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -660,12 +850,35 @@ }; F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F9ED4C9E0630A76B00DF4E74 /* libdyld */; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; targetProxy = F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + F908134311D3ED0C00626CC1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALLHDRS_COPY_PHASE = YES; + PRODUCT_NAME = libdyld; + }; + name = Debug; + }; + F908134411D3ED0C00626CC1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + INSTALLHDRS_COPY_PHASE = YES; + PRODUCT_NAME = libdyld; + ZERO_LINK = NO; + }; + name = Release; + }; F93937350A94FB2900070A07 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -685,20 +898,23 @@ GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; PRODUCT_NAME = update_dyld_shared_cache; - VALID_ARCHS = "ppc i386"; + VALID_ARCHS = "x86_64 i386"; }; name = Debug; }; F93937360A94FB2900070A07 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = x86_64; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = s; GCC_THREADSAFE_STATICS = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - INSTALL_PATH = /usr/bin; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/$(LOCAL)/bin"; + LOCAL = "$(LOCAL_$(RC_TARGET_CONFIG))"; + LOCAL_iPhone = local; PREBINDING = NO; PRODUCT_NAME = update_dyld_shared_cache; STRIP_INSTALLED_PRODUCT = YES; @@ -708,12 +924,70 @@ }; name = Release; }; + F99B8E580FEC10F600701838 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PREBINDING = NO; + PRODUCT_NAME = dyld_shared_cache_util; + }; + name = Debug; + }; + F99B8E590FEC10F600701838 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PREBINDING = NO; + PRODUCT_NAME = dyld_shared_cache_util; + SKIP_INSTALL = NO; + }; + name = Release; + }; + F9B0912411F11D1700096D49 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = libdsc_slider; + }; + name = Debug; + }; + F9B0912511F11D1700096D49 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = /usr/local/lib; + PRODUCT_NAME = dsc_slider; + ZERO_LINK = NO; + }; + name = Release; + }; F9D8C7DE087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = ( - ppc, i386, x86_64, ); @@ -727,11 +1001,6 @@ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; - EXCEPTION_LIB_armv6 = "-lgcc_eh"; - EXCEPTION_LIB_armv7 = "-lgcc_eh"; - EXCEPTION_LIB_i386 = /usr/local/lib/dyld/libunwind.a; - EXCEPTION_LIB_ppc = /usr/local/lib/dyld/libunwind.a; - EXCEPTION_LIB_x86_64 = /usr/local/lib/dyld/libunwind.a; EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp"; GCC_C_LANGUAGE_STANDARD = c99; GCC_DYNAMIC_NO_PIC = NO; @@ -749,24 +1018,17 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; - LIBC_OVERRIDES_iphoneos = ""; - LIBC_OVERRIDES_macosx = "/usr/local/lib/system/libc-dyld.a"; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ( "-seg1addr", "$(BASE_ADDRESS_$(CURRENT_ARCH))", - "-lstdc++-static", - "$(LIBC_OVERRIDES_$(PLATFORM_NAME))", + "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", - /usr/local/lib/system/libc.a, - /usr/local/lib/libCoreSymbolicationSharedWithDyld.a, - "$(EXCEPTION_LIB_$(CURRENT_ARCH))", "-lgcc", "-Wl,-e,__dyld_start", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", ); - PER_ARCH_CFLAGS_ppc = ""; PREBINDING = NO; PRODUCT_NAME = dyld; STRIPFLAGS = "-S"; @@ -782,7 +1044,10 @@ F9D8C7E0087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + ARCHS = ( + x86_64, + i386, + ); BASE_ADDRESS_armv4t = 0x2fe00000; BASE_ADDRESS_armv5 = 0x2fe00000; BASE_ADDRESS_armv6 = 0x2fe00000; @@ -793,11 +1058,6 @@ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - EXCEPTION_LIB_armv6 = "-lgcc_eh"; - EXCEPTION_LIB_armv7 = "-lgcc_eh"; - EXCEPTION_LIB_i386 = /usr/local/lib/dyld/libunwind.a; - EXCEPTION_LIB_ppc = /usr/local/lib/dyld/libunwind.a; - EXCEPTION_LIB_x86_64 = /usr/local/lib/dyld/libunwind.a; EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp"; GCC_C_LANGUAGE_STANDARD = c99; GCC_DYNAMIC_NO_PIC = NO; @@ -812,24 +1072,18 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; - LIBC_OVERRIDES_iphoneos = ""; - LIBC_OVERRIDES_macosx = "/usr/local/lib/system/libc-dyld.a"; - OTHER_CFLAGS = ""; + ORDER_FILE = "$(SRCROOT)/src/dyld.order"; + "OTHER_CFLAGS[arch=armv6]" = "-mthumb"; OTHER_LDFLAGS = ( "-seg1addr", "$(BASE_ADDRESS_$(CURRENT_ARCH))", - "-lstdc++-static", - "$(LIBC_OVERRIDES_$(PLATFORM_NAME))", + "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", - /usr/local/lib/system/libc.a, - /usr/local/lib/libCoreSymbolicationSharedWithDyld.a, - "$(EXCEPTION_LIB_$(CURRENT_ARCH))", "-lgcc", "-Wl,-e,__dyld_start", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", ); - PER_ARCH_CFLAGS_ppc = ""; PREBINDING = NO; PRODUCT_NAME = dyld; STRIPFLAGS = "-S"; @@ -846,15 +1100,17 @@ F9D8C7E2087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(NATIVE_ARCH_ACTUAL)"; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/local/lib/system; - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = dyldapis; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALL_PATH = /usr/lib/system; + PREBINDING = NO; + PRODUCT_NAME = dyld; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -866,15 +1122,34 @@ F9D8C7E4087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; + ARCHS = ( + x86_64, + i386, + ); + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/local/lib/system; - LIBRARY_STYLE = STATIC; - PRODUCT_NAME = dyldapis; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = /usr/lib/system; + OTHER_LDFLAGS = ( + "-nodefaultlibs", + "-lSystem", + "-umbrella", + System, + ); + PREBINDING = NO; + PRODUCT_NAME = dyld; + SEPARATE_STRIP = YES; + STRIP_INSTALLED_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -916,8 +1191,10 @@ COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; PREBINDING = NO; PRODUCT_NAME = dsc; @@ -927,25 +1204,23 @@ F9F2A55B0F7AEE9900B7C9EB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + COPY_PHASE_STRIP = NO; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_ENABLE_OBJC_EXCEPTIONS = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_MODEL_TUNING = G5; GCC_SYMBOLS_PRIVATE_EXTERN = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_PEDANTIC = YES; - GCC_WARN_SHADOW = YES; + GCC_WARN_PEDANTIC = NO; + GCC_WARN_SHADOW = NO; GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - INSTALL_PATH = /usr/local/lib; - PREBINDING = NO; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; PRODUCT_NAME = dsc; ZERO_LINK = NO; }; @@ -954,6 +1229,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F908134311D3ED0C00626CC1 /* Debug */, + F908134411D3ED0C00626CC1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -963,6 +1247,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F99B8E580FEC10F600701838 /* Debug */, + F99B8E590FEC10F600701838 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9B0912A11F11D3400096D49 /* Build configuration list for PBXNativeTarget "libdsc_slider" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9B0912411F11D1700096D49 /* Debug */, + F9B0912511F11D1700096D49 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -972,7 +1274,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */ = { + F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */ = { isa = XCConfigurationList; buildConfigurations = ( F9D8C7E2087B087300E93EFB /* Debug */, @@ -999,7 +1301,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */ = { + F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = { isa = XCConfigurationList; buildConfigurations = ( F9F2A55A0F7AEE9900B7C9EB /* Debug */, diff --git a/include/mach-o/dyld.h b/include/mach-o/dyld.h index 0f9828d..3f579ef 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -81,8 +81,9 @@ extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) __O /* * _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter - * should initially be the size of the buffer. The function returns 0 if the path was successfully copied. - * It returns -1 if the buffer is not large enough, and *bufsize is set to the size required. + * should initially be the size of the buffer. The function returns 0 if the path was successfully copied, + * and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set + * to the size required. * * 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. With deep directories the total bufsize @@ -93,11 +94,9 @@ extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) __ /* - * _dyld_moninit() and _dyld_func_lookup() are private interface between - * dyld and libSystem. -*/ + * _dyld_moninit() is a private interface between dyld and libSystem. + */ extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); -extern int _dyld_func_lookup(const char* dyld_func_name, void **address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_6,__IPHONE_NA,__IPHONE_NA); diff --git a/include/mach-o/dyld_images.h b/include/mach-o/dyld_images.h index 8423f2f..ed710ef 100644 --- a/include/mach-o/dyld_images.h +++ b/include/mach-o/dyld_images.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * Copyright (c) 2006-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,6 +24,7 @@ #define _DYLD_IMAGES_ #include +#include #include #ifdef __cplusplus @@ -55,6 +56,11 @@ extern "C" { * be set to point to a C string message buffer containing the reason dyld terminate the process. * The low bit of the terminationFlags will be set if dyld terminated the process before any user * code ran, in which case there is no need for the crash log to contain the backtrace. + * + * When dyld terminates a process because some required dylib or symbol cannot be bound, in + * addition to the errorMessage field, it now sets the errorKind field and the corresponding + * fields: errorClientOfDylibPath, errorTargetDylibPath, errorSymbol. + * */ enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 }; @@ -67,8 +73,22 @@ struct dyld_image_info { /* then file has been modified since dyld loaded it */ }; +struct dyld_uuid_info { + const struct mach_header* imageLoadAddress; /* base address image is mapped into */ + uuid_t imageUUID; /* UUID of image */ +}; + 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 */ +enum { dyld_error_kind_none=0, + dyld_error_kind_dylib_missing=1, + dyld_error_kind_dylib_wrong_arch=2, + dyld_error_kind_dylib_version=3, + dyld_error_kind_symbol_missing=4 + }; + + struct dyld_all_image_infos { uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */ uint32_t infoArrayCount; @@ -78,20 +98,33 @@ struct dyld_all_image_infos { /* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */ bool libSystemInitialized; const struct mach_header* dyldImageLoadAddress; - /* the following field is only in version 3 (Mac OS X 10.6) and later */ + /* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */ void* jitInfo; - /* the following fields are only in version 5 (Mac OS X 10.6) and later */ + /* the following fields are only in version 5 (Mac OS X 10.6, iPhoneOS 3.0) and later */ const char* dyldVersion; const char* errorMessage; uintptr_t terminationFlags; - /* the following field is only in version 6 (Mac OS X 10.6) and later */ + /* the following field is only in version 6 (Mac OS X 10.6, iPhoneOS 3.1) and later */ void* coreSymbolicationShmPage; - /* the following field is only in version 7 (Mac OS X 10.6) and later */ + /* the following field is only in version 7 (Mac OS X 10.6, iPhoneOS 3.1) and later */ uintptr_t systemOrderFlag; + /* the following field is only in version 8 (Mac OS X 10.7, iPhoneOS 3.1) and later */ + uintptr_t uuidArrayCount; + const struct dyld_uuid_info* uuidArray; /* only images not in dyld shared cache */ + /* the following field is only in version 9 (Mac OS X 10.7, iOS 4.0) and later */ + struct dyld_all_image_infos* dyldAllImageInfosAddress; + /* the following field is only in version 10 (Mac OS X 10.7, iOS 4.2) and later */ + uintptr_t initialImageCount; + /* the following field is only in version 11 (Mac OS X 10.7, iOS 4.2) and later */ + uintptr_t errorKind; + const char* errorClientOfDylibPath; + const char* errorTargetDylibPath; + const char* errorSymbol; + /* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */ + uintptr_t sharedCacheSlide; }; extern struct dyld_all_image_infos dyld_all_image_infos; - /* * Beginning in Mac OS X 10.6, rather than looking up the symbol "_dyld_all_image_infos" * in dyld's symbol table, you can add DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET to the mach_header diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 07ef161..b33c231 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2003-2008 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,7 +24,8 @@ #ifndef _MACH_O_DYLD_PRIV_H_ #define _MACH_O_DYLD_PRIV_H_ - +#include +#include #include #include @@ -33,6 +34,24 @@ extern "C" { #endif /* __cplusplus */ + +// +// private interface between libSystem.dylib and dyld +// +extern int _dyld_func_lookup(const char* dyld_func_name, void **address); + + +// +// _dyld_moninit() is a private interface between libSystem.dylib and dyld +// +extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)); + +// +// private interface between libSystem.dylib and dyld +// +extern void _dyld_fork_child(); + + // // Possible state changes for which you can register to be notified // @@ -68,6 +87,51 @@ extern void dyld_register_image_state_change_handler(enum dyld_image_states state, bool batch, dyld_image_state_change_handler handler); +// +// Possible thread-local variable state changes for which you can register to be notified +// +enum dyld_tlv_states { + dyld_tlv_state_allocated = 10, // TLV range newly allocated + dyld_tlv_state_deallocated = 20 // TLV range about to be deallocated +}; + +// +// Info about thread-local variable storage. +// +typedef struct { + size_t info_size; // sizeof(dyld_tlv_info) + void * tlv_addr; // Base address of TLV storage + size_t tlv_size; // Byte size of TLV storage +} dyld_tlv_info; + +#if __BLOCKS__ + +// +// Callback that notes changes to thread-local variable storage. +// +typedef void (^dyld_tlv_state_change_handler)(enum dyld_tlv_states state, const dyld_tlv_info *info); + +// +// Register a handler to be called when a thread adds or removes storage for thread-local variables. +// The registered handler will only be called from and on behalf of the thread that owns the storage. +// The registered handler will NOT be called for any storage that was +// already allocated before dyld_register_tlv_state_change_handler() was +// called. Use dyld_enumerate_tlv_storage() to get that information. +// Exists in Mac OS X 10.7 and later +// +extern void +dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler); + +// +// Enumerate the current thread-local variable storage allocated for the current thread. +// Exists in Mac OS X 10.7 and later +// +extern void +dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler); + +#endif + + // // get slide for a given loaded mach_header // Mac OS X 10.6 and later @@ -114,7 +178,13 @@ extern const char* dyld_image_path_containing_address(const void* addr); - +#if __IPHONE_OS_VERSION_MIN_REQUIRED +// +// Returns if any OS dylib has overridden its copy in the shared cache +// +// Exists in iPhoneOS 3.1 and later +extern bool dyld_shared_cache_some_image_overridden(); +#endif diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp index cb98ac3..c2e536c 100644 --- a/launch-cache/CacheFileAbstraction.hpp +++ b/launch-cache/CacheFileAbstraction.hpp @@ -35,9 +35,6 @@ public: const char* magic() const INLINE { return fields.magic; } void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } - //uint32_t architecture() const INLINE { return E::get32(fields.architecture); } - //void set_architecture(uint32_t value) INLINE { E::set32(fields.architecture, value); } - uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } @@ -53,11 +50,17 @@ public: uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } - //uint32_t dependenciesOffset() const INLINE { return E::get32(fields.dependenciesOffset); } - //void set_dependenciesOffset(uint32_t value) INLINE { E::set32(fields.dependenciesOffset, 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); } - //uint32_t dependenciesCount() const INLINE { return E::get32(fields.dependenciesCount); } - //void set_dependenciesCount(uint32_t value) INLINE { E::set32(fields.dependenciesCount, value); } + uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } + void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } private: dyld_cache_header fields; @@ -67,23 +70,23 @@ private: template class dyldCacheFileMapping { public: - uint64_t address() const INLINE { return E::get64(fields.sfm_address); } - void set_address(uint64_t value) INLINE { E::set64(fields.sfm_address, value); } + 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.sfm_size); } - void set_size(uint64_t value) INLINE { E::set64(fields.sfm_size, 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.sfm_file_offset); } - void set_file_offset(uint64_t value) INLINE { E::set64(fields.sfm_file_offset, 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.sfm_max_prot); } - void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_max_prot, 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.sfm_init_prot); } - void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_init_prot, 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: - shared_file_mapping_np fields; + dyld_cache_mapping_info fields; }; @@ -102,13 +105,43 @@ public: 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; } - //uint32_t dependenciesStartOffset() const INLINE { return E::get32(fields.dependenciesStartOffset); } - //void set_dependenciesStartOffset(uint32_t value) INLINE { E::set32(fields.dependenciesStartOffset, value); } - private: dyld_cache_image_info 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 +}; + #endif // __DYLD_CACHE_ABSTRACTION__ diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp index 8d75311..8786e6a 100644 --- a/launch-cache/FileAbstraction.hpp +++ b/launch-cache/FileAbstraction.hpp @@ -128,6 +128,12 @@ public: 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, 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; } }; @@ -140,6 +146,12 @@ public: 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; } }; diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp index 5b99dbf..ac882dc 100644 --- a/launch-cache/MachOBinder.hpp +++ b/launch-cache/MachOBinder.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,6 +40,8 @@ #include #include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -47,6 +49,9 @@ #include "MachORebaser.hpp" #include "MachOTrie.hpp" +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif @@ -65,33 +70,52 @@ public: const char* getDylibID() const; void setDependentBinders(const Map& map); - void bind(); - + void bind(std::vector&); + void optimize(); + void addResolverClient(Binder* clientDylib, const char* symbolName); + void addResolverLazyPointerMappedAddress(const char* symbolName, + typename A::P::uint_t lpVMAddr); private: typedef typename A::P P; typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; struct BinderAndReExportFlag { Binder* binder; bool reExport; }; + struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; }; typedef __gnu_cxx::hash_map, CStringEquals> NameToAddrMap; + typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + struct ClientAndSymbol { Binder* client; const char* symbolName; }; + struct SymbolAndLazyPointer { const char* symbolName; pint_t lpVMAddr; }; + static bool isPublicLocation(const char* pth); void doBindExternalRelocations(); void doBindIndirectSymbols(); void doSetUpDyldSection(); void doSetPreboundUndefines(); - void doBindDyldInfo(); - void doBindDyldLazyInfo(); + void hoistPrivateRexports(); + int ordinalOfDependentBinder(Binder* dep); + void doBindDyldInfo(std::vector& pointersInData); + void doBindDyldLazyInfo(std::vector& pointersInData); void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, - int libraryOrdinal, int64_t addend, const char* symbolName); + int libraryOrdinal, int64_t addend, + const char* symbolName, bool lazyPointer, + std::vector& pointersInData); pint_t resolveUndefined(const macho_nlist

* undefinedSymbol); - bool findExportedSymbolAddress(const char* name, pint_t* result); + bool findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, bool* isResolverSymbol); void bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value); const char* parentUmbrella(); + pint_t runtimeAddressFromNList(const macho_nlist

* sym); + void optimizeStub(const char* symbolName, pint_t lpVMAddr); + void optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); + pint_t findLazyPointerFor(const char* symbolName); + static uint8_t pointerRelocSize(); static uint8_t pointerRelocType(); std::vector fDependentDylibs; NameToAddrMap fHashTable; + NameSet fSymbolResolvers; + std::vector fReExportedSymbols; uint64_t fDyldBaseAddress; const macho_nlist

* fSymbolTable; const char* fStrings; @@ -101,15 +125,33 @@ private: const macho_dylib_command

* fParentUmbrella; const macho_dyld_info_command

* fDyldInfo; bool fOriginallyPrebound; + bool fReExportedSymbolsResolved; + std::vector fClientAndSymbols; + std::vector fSymbolAndLazyPointers; }; +template <> +uint32_t Binder::runtimeAddressFromNList(const macho_nlist >* sym) +{ + if (sym->n_desc() & N_ARM_THUMB_DEF) + return sym->n_value() + 1; + else + return sym->n_value(); +} + +template +typename A::P::uint_t Binder::runtimeAddressFromNList(const macho_nlist

* sym) +{ + return sym->n_value(); +} + template Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress) : Rebaser(layout), fDyldBaseAddress(dyldBaseAddress), fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL), - fParentUmbrella(NULL) + fParentUmbrella(NULL), fReExportedSymbolsResolved(false) { fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0); // update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit) @@ -137,6 +179,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: ((macho_dylib_command

*)cmd)->set_timestamp(0); break; case LC_SUB_FRAMEWORK: @@ -163,12 +206,33 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress // fprintf(stderr, "exports for %s\n", layout.getFilePath()); if ( fDyldInfo != NULL ) { std::vector exports; - const uint8_t* exportsStart = &this->fLinkEditBase[fDyldInfo->export_off()]; + const uint8_t* exportsStart = layout.getDyldInfoExports(); const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); pint_t baseAddress = layout.getSegments()[0].newAddress(); for(std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { - fHashTable[it->name] = it->address + baseAddress; + if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) { + if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + fSymbolResolvers.insert(it->name); + } + if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID()); + SymbolReExport sym; + sym.exportName = it->name; + sym.dylibOrdinal = it->other; + sym.importName = it->importName; + if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) + sym.importName = sym.exportName; + fReExportedSymbols.push_back(sym); + // fHashTable entry will be added in first call to findExportedSymbolAddress() + } + else { + fHashTable[it->name] = it->address + baseAddress; + } + } + else { + throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); + } //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); } } @@ -179,7 +243,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress fHashTable.resize(fDynamicInfo->nextdefsym()); // set initial bucket count for (const macho_nlist

* sym=start; sym < end; ++sym) { const char* name = &fStrings[sym->n_strx()]; - fHashTable[name] = sym->n_value(); + fHashTable[name] = runtimeAddressFromNList(sym); //fprintf(stderr, " 0x%08llX %s\n", sym->n_value(), name); } } @@ -191,7 +255,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress const uint32_t index = E::get32(toc[i].symbol_index); const macho_nlist

* sym = &fSymbolTable[index]; const char* name = &fStrings[sym->n_strx()]; - fHashTable[name] = sym->n_value(); + fHashTable[name] = runtimeAddressFromNList(sym); //fprintf(stderr, "- 0x%08llX %s\n", sym->n_value(), name); } } @@ -228,6 +292,30 @@ const char* Binder::parentUmbrella() } +template +bool Binder::isPublicLocation(const char* pth) +{ + // /usr/lib is a public location + if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) ) + return true; + + // /System/Library/Frameworks/ is a public location + if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) { + const char* frameworkDot = strchr(&pth[27], '.'); + // but only top level framework + // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true + // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false + // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false + if ( frameworkDot != NULL ) { + int frameworkNameLen = frameworkDot - &pth[27]; + if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 ) + return true; + } + } + + return false; +} template void Binder::setDependentBinders(const Map& map) @@ -241,6 +329,7 @@ void Binder::setDependentBinders(const Map& map) case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: const char* path = ((struct macho_dylib_command

*)cmd)->name(); typename Map::const_iterator pos = map.find(path); if ( pos != map.end() ) { @@ -334,15 +423,77 @@ void Binder::setDependentBinders(const Map& map) } } } + +} + +template +int Binder::ordinalOfDependentBinder(Binder* dep) +{ + for (int i=0; i < fDependentDylibs.size(); ++i) { + if ( fDependentDylibs[i].binder == dep ) + return i+1; + } + throw "dependend dylib not found"; +} + +template +void Binder::hoistPrivateRexports() +{ + std::vector*> privateReExportedDylibs; + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( it->reExport && ! isPublicLocation(it->binder->getDylibID()) ) + privateReExportedDylibs.push_back(it->binder); + } + if ( privateReExportedDylibs.size() != 0 ) { + // parse export info into vector of exports + const uint8_t* exportsStart = this->fLayout.getDyldInfoExports(); + const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()]; + std::vector exports; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + //fprintf(stderr, "%s exports %lu symbols from trie of size %u \n", this->fLayout.getFilePath(), exports.size(), fDyldInfo->export_size()); + + // add re-exports for each export from an re-exported dylib + for(typename std::vector*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) { + Binder* binder = *it; + int ordinal = ordinalOfDependentBinder(binder); + const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports(); + const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()]; + std::vector aDylibsExports; + mach_o::trie::parseTrie(aDylibsExportsStart, aDylibsExportsEnd, aDylibsExports); + //fprintf(stderr, "%s re-exports %lu symbols from %s\n", this->fLayout.getFilePath(), aDylibsExports.size(), binder->getDylibID()); + for(std::vector::iterator eit = aDylibsExports.begin(); eit != aDylibsExports.end(); ++eit) { + mach_o::trie::Entry entry = *eit; + entry.flags |= EXPORT_SYMBOL_FLAGS_REEXPORT; + entry.other = ordinal; + entry.importName = NULL; + exports.push_back(entry); + } + } + // rebuild new combined trie + std::vector newExportTrieBytes; + newExportTrieBytes.reserve(fDyldInfo->export_size()); + mach_o::trie::makeTrie(exports, newExportTrieBytes); + //fprintf(stderr, "%s now exports %lu symbols from trie of size %lu\n", this->fLayout.getFilePath(), exports.size(), newExportTrieBytes.size()); + + // allocate new buffer and set export_off to use new buffer instead + uint32_t newExportsSize = newExportTrieBytes.size(); + uint8_t* sideTrie = new uint8_t[newExportsSize]; + memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); + this->fLayout.setDyldInfoExports(sideTrie); + ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(0); // invalidate old trie + ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); + } } + template -void Binder::bind() +void Binder::bind(std::vector& pointersInData) { this->doSetUpDyldSection(); if ( fDyldInfo != NULL ) { - this->doBindDyldInfo(); - this->doBindDyldLazyInfo(); + this->doBindDyldInfo(pointersInData); + this->doBindDyldLazyInfo(pointersInData); + this->hoistPrivateRexports(); // weak bind info is processed at launch time } else { @@ -382,7 +533,8 @@ void Binder::doSetUpDyldSection() } template -void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, int64_t addend, const char* symbolName) +void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, + int64_t addend, const char* symbolName, bool lazyPointer, std::vector& pointersInData) { //printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); const std::vector& segments = this->fLayout.getSegments(); @@ -390,7 +542,7 @@ void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uin throw "bad segment index in rebase info"; if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) - throw "flat_namespace linkage not allowed in dyld shared cache"; + throw "dynamic lookup linkage not allowed in dyld shared cache"; if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) throw "linkage to main executable not allowed in dyld shared cache"; @@ -407,8 +559,26 @@ void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uin else binder = fDependentDylibs[libraryOrdinal-1].binder; pint_t targetSymbolAddress; - if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress) ) - throwf("could not resolve %s expected in %s", symbolName, binder->getDylibID()); + bool isResolverSymbol; + Binder* foundIn; + if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol) ) + throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); + + // don't bind lazy pointers to resolver stubs in shared cache + if ( lazyPointer && isResolverSymbol ) { + if ( foundIn == this ) { + // record location of lazy pointer for other dylibs to re-use + pint_t lpVMAddr = segments[segmentIndex].newAddress() + segmentOffset; + foundIn->addResolverLazyPointerMappedAddress(symbolName, lpVMAddr); + //fprintf(stderr, "resolver %s in %s has lazy pointer with segmentOffset=0x%08llX\n", symbolName, this->getDylibID(), segmentOffset); + } + else { + // record that this dylib has a lazy pointer to a resolver function + foundIn->addResolverClient(this, symbolName); + // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID()); + } + return; + } // do actual update const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex]; @@ -433,12 +603,13 @@ void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uin default: throw "bad bind type"; } + pointersInData.push_back(mappedAddr); } template -void Binder::doBindDyldLazyInfo() +void Binder::doBindDyldLazyInfo(std::vector& pointersInData) { const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()]; const uint8_t* end = &p[fDyldInfo->lazy_bind_size()]; @@ -486,7 +657,7 @@ void Binder::doBindDyldLazyInfo() segmentOffset = read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, pointersInData); segmentOffset += sizeof(pint_t); break; case BIND_OPCODE_SET_TYPE_IMM: @@ -503,7 +674,7 @@ void Binder::doBindDyldLazyInfo() } template -void Binder::doBindDyldInfo() +void Binder::doBindDyldInfo(std::vector& pointersInData) { const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()]; const uint8_t* end = &p[fDyldInfo->bind_size()]; @@ -560,22 +731,22 @@ void Binder::doBindDyldInfo() segmentOffset += read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData); segmentOffset += sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: - bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData); segmentOffset += read_uleb128(p, end) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: - bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData); segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData); segmentOffset += skip + sizeof(pint_t); } break; @@ -657,7 +828,7 @@ void Binder::doBindExternalRelocations() const macho_nlist

* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum()]; pint_t* location; try { - location = mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress); + location = this->mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress); } catch (const char* msg) { throwf("%s processesing external relocation r_address 0x%08X", msg, reloc->r_address()); @@ -774,11 +945,11 @@ typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* undefine if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) { if ( (undefinedSymbol->n_type() & N_PEXT) != 0 ) { // is a multi-module private_extern internal reference that the linker did not optimize away - return undefinedSymbol->n_value(); + return runtimeAddressFromNList(undefinedSymbol); } if ( (undefinedSymbol->n_desc() & N_WEAK_DEF) != 0 ) { // is a weak definition, we should prebind to this one in the same linkage unit - return undefinedSymbol->n_value(); + return runtimeAddressFromNList(undefinedSymbol); } } const char* symbolName = &fStrings[undefinedSymbol->n_strx()]; @@ -802,26 +973,62 @@ typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* undefine binder = fDependentDylibs[ordinal-1].binder; } pint_t addr; - if ( ! binder->findExportedSymbolAddress(symbolName, &addr) ) - throwf("could not resolve %s expected in %s", symbolName, binder->getDylibID()); + bool isResolver; + Binder* foundIn; + if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver) ) + throwf("could not resolve undefined symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); return addr; } } template -bool Binder::findExportedSymbolAddress(const char* name, pint_t* result) +bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, bool* isResolverSymbol) { + *foundIn = NULL; + // since re-export chains can be any length, re-exports cannot be resolved in setDependencies() + // instead we lazily, recursively update + if ( !fReExportedSymbolsResolved ) { + + // update fHashTable with any individual symbol re-exports + for (typename std::vector::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { + pint_t targetSymbolAddress; + bool isResolver; + + if ( it->dylibOrdinal <= 0 ) + throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; + + Binder* binder = fDependentDylibs[it->dylibOrdinal-1].binder; + + if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver) ) + throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID()); + + if ( isResolver ) + throw "bad mach-o binary, re-export of resolvers symbols not supported in dyld shared cache"; + + fHashTable[it->exportName] = targetSymbolAddress; + } + // mark as done + fReExportedSymbolsResolved = true; + } + + *isResolverSymbol = false; + if ( !fSymbolResolvers.empty() && fSymbolResolvers.count(name) ) { + // lazy pointers should be left unbound, rather than bind to resolver stub + *isResolverSymbol = true; + } + typename NameToAddrMap::iterator pos = fHashTable.find(name); if ( pos != fHashTable.end() ) { *result = pos->second; //fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID()); + *foundIn = this; return true; } // search re-exports for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { if ( it->reExport ) { - if ( it->binder->findExportedSymbolAddress(name, result) ) + if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol) ) return true; } } @@ -829,6 +1036,144 @@ bool Binder::findExportedSymbolAddress(const char* name, pint_t* result) return false; } +// record which dylibs will be using this dylibs lazy pointer +template +void Binder::addResolverClient(Binder* clientDylib, const char* symbolName) +{ + ClientAndSymbol x; + x.client = clientDylib; + x.symbolName = symbolName; + fClientAndSymbols.push_back(x); +} + +// Record that this dylib has an lazy pointer that points within itself for use +// with a resolver function. +template +void Binder::addResolverLazyPointerMappedAddress(const char* symbolName, pint_t lpVMAddr) +{ + SymbolAndLazyPointer x; + x.symbolName = symbolName; + x.lpVMAddr = lpVMAddr; + fSymbolAndLazyPointers.push_back(x); +} + +template +typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) +{ + for (typename std::vector::iterator it = fSymbolAndLazyPointers.begin(); it != fSymbolAndLazyPointers.end(); ++it) { + if ( strcmp(it->symbolName, symbolName) == 0 ) + return it->lpVMAddr; + } + return 0; +} + +// called after all binding is done to optimize lazy pointers +template +void Binder::optimize() +{ + for (typename std::vector::iterator it = fClientAndSymbols.begin(); it != fClientAndSymbols.end(); ++it) { + pint_t lpVMAddr = findLazyPointerFor(it->symbolName); + if ( lpVMAddr != 0 ) { + it->client->optimizeStub(it->symbolName, lpVMAddr); + } + else { + fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", it->symbolName, it->client->getDylibID()); + } + + } +} + +template <> +void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +{ + if ( stubSize != 16 ) { + fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID()); + return; + } + uint32_t* instructions = (uint32_t*)stubMappedAddress; + if ( (E::get32(instructions[0]) != 0xe59fc004) + || (E::get32(instructions[1]) != 0xe08fc00c) + || (E::get32(instructions[2]) != 0xe59cf000) + ) { + fprintf(stderr, "could not optimize ARM stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); + return; + } + // last .long in stub is: lazyPtr - (stub+8) + // alter to point to more optimal lazy pointer + uint32_t betterOffset = lpVMAddr - (stubVMAddress + 12); + E::set32(instructions[3], betterOffset); +} + + +template <> +void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +{ + if ( stubSize != 6 ) { + fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID()); + return; + } + if ( (stubMappedAddress[0] != 0xFF) || (stubMappedAddress[1] != 0x25) ) { + fprintf(stderr, "could not optimize stub to resolver function in %s because instructions are not as expected\n", this->getDylibID()); + return; + } + // last four bytes in stub is RIP relative offset to lazy pointer + // alter to point to more optimal lazy pointer + uint32_t betterOffset = lpVMAddr - (stubVMAddress + 6); + E::set32(*((uint32_t*)(&stubMappedAddress[2])), betterOffset); +} + +template +void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress) +{ + // Remaining architectures are not optimized + //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress); +} + +// search for stub in this image that call target symbol name and then optimize its lazy pointer +template +void Binder::optimizeStub(const char* stubName, pint_t lpVMAddr) +{ + // find named stub + const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

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

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

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { + pint_t stubsVMStart = sect->addr(); + uint8_t* stubsMappingStart = (uint8_t*)this->mappedAddressForNewAddress(stubsVMStart); + const uint32_t indirectTableOffset = sect->reserved1(); + const uint32_t stubSize = sect->reserved2(); + uint32_t elementCount = sect->size() / stubSize; + pint_t stubVMAddr = stubsVMStart; + uint8_t* stubMappedAddr = stubsMappingStart; + for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + { + const macho_nlist

* sym = &this->fSymbolTable[symbolIndex]; + const char* symName = &fStrings[sym->n_strx()]; + if ( strcmp(symName, stubName) == 0 ) + this->optimizeStub(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr); + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} #endif // __MACHO_BINDER__ diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp index cba7f8a..3be3fa2 100644 --- a/launch-cache/MachOFileAbstraction.hpp +++ b/launch-cache/MachOFileAbstraction.hpp @@ -43,6 +43,27 @@ struct uuid_command { #define S_16BYTE_LITERALS 0xE #endif +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + #include "FileAbstraction.hpp" #include "Architectures.hpp" @@ -738,29 +759,28 @@ public: const macho_segment_command

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

* const cmds = (macho_load_command

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

)); - const uint32_t cmd_count = this->ncmds(); + 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; + const macho_segment_command

* segcmd = (macho_segment_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + 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

* const segcmd = getSegment(segname); + const macho_segment_command

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

* sectcmd = (macho_section

*)(segcmd+1); - const uint32_t section_count = segcmd->nsects(); + 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; diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index 945227b..e23cbac 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -124,10 +125,15 @@ public: virtual uint32_t getFileType() const = 0; virtual uint32_t getFlags() const = 0; virtual Library getID() const = 0; + virtual bool isDylib() const = 0; virtual bool isSplitSeg() const = 0; virtual bool hasSplitSegInfo() const = 0; virtual bool isRootOwned() const = 0; virtual bool inSharableLocation() const = 0; + virtual bool hasDynamicLookupLinkage() const = 0; + virtual bool hasMainExecutableLookupLinkage() const = 0; + virtual bool isTwoLevelNamespace() const = 0; + virtual bool hasDyldInfo() const = 0; virtual uint32_t getNameFileOffset() const = 0; virtual time_t getLastModTime() const = 0; virtual ino_t getInode() const = 0; @@ -142,6 +148,9 @@ public: virtual uint64_t getExecutableVMSize() const = 0; virtual uint64_t getWritableVMSize() const = 0; virtual uint64_t getReadOnlyVMSize() const = 0; + // need getDyldInfoExports because export info uses ULEB encoding and size could grow + virtual const uint8_t* getDyldInfoExports() const = 0; + virtual void setDyldInfoExports(const uint8_t* newExports) const = 0; }; @@ -161,10 +170,15 @@ public: virtual uint32_t getFileType() const { return fFileType; } virtual uint32_t getFlags() const { return fFlags; } virtual Library getID() const { return fDylibID; } + virtual bool isDylib() const { return fIsDylib; } virtual bool isSplitSeg() const; virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; } virtual bool isRootOwned() const { return fRootOwned; } virtual bool inSharableLocation() const { return fShareableLocation; } + virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; } + virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; } + virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); } + virtual bool hasDyldInfo() const { return fHasDyldInfo; } virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } virtual time_t getLastModTime() const { return fMTime; } virtual ino_t getInode() const { return fInode; } @@ -179,6 +193,8 @@ public: virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; } virtual uint64_t getWritableVMSize() const { return fVMWritablSize; } virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } + virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; } + virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; } private: typedef typename A::P P; @@ -209,6 +225,11 @@ private: bool fHasSplitSegInfo; bool fRootOwned; bool fShareableLocation; + bool fDynamicLookupLinkage; + bool fMainExecutableLookupLinkage; + bool fIsDylib; + bool fHasDyldInfo; + mutable const uint8_t* fDyldInfoExports; }; @@ -229,9 +250,7 @@ private: }; typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; - static bool compatibleSubtype(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType); - static bool bestSliceForArch(uint32_t sliceCount, const struct fat_arch* slices, ArchPair ap, uint32_t& bestSliceIndex); - static const cpu_subtype_t* getArmSubtypeList(cpu_subtype_t s); + static bool requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType); static PathToNode fgLayoutCache; const char* fPath; @@ -243,80 +262,23 @@ UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache; -// armv7 can run: v7, v6, v5, and v4 -static const cpu_subtype_t kARMV7compatibleSubTypes[] = - { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0 }; - -// armv6 can run: v6, v5, and v4 -static const cpu_subtype_t kARMV6compatibleSubTypes[] = - { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0}; - -// xscale can run: xscale, v5, and v4 -static const cpu_subtype_t kARMXscaleCompatibleSubTypes[] = - { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0}; - -// armv5 can run: v5 and v4 -static const cpu_subtype_t kARMV5compatibleSubTypes[] = - { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0}; - -// armv4 can run: v4 -static const cpu_subtype_t kARMV4compatibleSubTypes[] = - { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0 }; - -const cpu_subtype_t* UniversalMachOLayout::getArmSubtypeList(cpu_subtype_t s) -{ - switch ( s ) { - case CPU_SUBTYPE_ARM_V7: - return kARMV7compatibleSubTypes; - case CPU_SUBTYPE_ARM_V6: - return kARMV6compatibleSubTypes; - case CPU_SUBTYPE_ARM_XSCALE: - return kARMXscaleCompatibleSubTypes; - case CPU_SUBTYPE_ARM_V5TEJ: - return kARMV5compatibleSubTypes; - case CPU_SUBTYPE_ARM_V4T: - return kARMV4compatibleSubTypes; - } - return NULL; -} - - - const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const { - switch ( ap.arch ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - case CPU_TYPE_X86_64: - // use first matching cputype - for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { - const MachOLayoutAbstraction* layout = *it; - if ( layout->getArchPair().arch == ap.arch ) - return layout; - } - break; - case CPU_TYPE_ARM: - const cpu_subtype_t* list = getArmSubtypeList(ap.subtype); - if ( list != NULL ) { - // known subtype, find best match - for(const cpu_subtype_t* s=list; *s != 0; ++s) { - for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { - const MachOLayoutAbstraction* layout = *it; - if ( (layout->getArchPair().arch == ap.arch) && (layout->getArchPair().subtype == *s) ) - return layout; - } - } - } - else { - // unknown arm sub-type, must have exact match - for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { - const MachOLayoutAbstraction* layout = *it; - if ( (layout->getArchPair().arch == ap.arch) && (layout->getArchPair().subtype == ap.subtype) ) - return layout; - } - } + // use matching cputype and cpusubtype + for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { + const MachOLayoutAbstraction* layout = *it; + if ( layout->getArchPair().arch == ap.arch ) { + switch ( ap.arch ) { + case CPU_TYPE_ARM: + if ( layout->getArchPair().subtype == ap.subtype ) + return layout; + break; + default: + return layout; + } + } } - throwf("no compatible slice found in %s", fPath); + return NULL; } @@ -336,74 +298,24 @@ const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const s return *result; } -bool UniversalMachOLayout::bestSliceForArch(uint32_t sliceCount, const struct fat_arch* slices, ArchPair ap, uint32_t& bestSliceIndex) -{ - switch ( ap.arch ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - case CPU_TYPE_X86_64: - // use first matching cputype - for (uint32_t i=0; i < sliceCount; ++i) { - if ( OSSwapBigToHostInt32(slices[i].cputype) == ap.arch ) { - bestSliceIndex = i; - return true; - } - } - return false; - case CPU_TYPE_ARM: - // find best matching arch - const cpu_subtype_t* list = getArmSubtypeList(ap.subtype); - if ( list != NULL ) { - for(const cpu_subtype_t* s=list; *s != 0; ++s) { - for (uint32_t i=0; i < sliceCount; ++i) { - if ( (OSSwapBigToHostInt32(slices[i].cputype) == ap.arch) && (OSSwapBigToHostInt32(slices[i].cpusubtype) == *s) ) { - bestSliceIndex = i; - return true; - } - } - } - return false; - } - // unknown arm sub-type, must have exact match - for (uint32_t i=0; i < sliceCount; ++i) { - if ( (OSSwapBigToHostInt32(slices[i].cputype) == ap.arch) && (OSSwapBigToHostInt32(slices[i].cpusubtype) == ap.subtype) ) { - bestSliceIndex = i; - return true; - } - } - return false; - } - throw "unknown architecture"; -} -bool UniversalMachOLayout::compatibleSubtype(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType) +bool UniversalMachOLayout::requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType) { + if ( onlyArchs == NULL ) + return true; + // must match cputype and cpusubtype for (std::set::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) { - if ( cpuType == it->arch ) { - switch ( it->arch ) { - case CPU_TYPE_POWERPC: - case CPU_TYPE_I386: - case CPU_TYPE_X86_64: - // just match cpu type - return true; - case CPU_TYPE_ARM: - { - const cpu_subtype_t* list = getArmSubtypeList(it->subtype); - if ( list != NULL ) { - // see if mach-o file is supported by this ArchPair - for(const cpu_subtype_t* s=list; *s != 0; ++s) { - if ( *s == cpuSubType ) - return true; - } - } - else { - // unknown arm sub-type, must have exact match - if ( it->subtype == cpuSubType ) - return true; - } - } - } - } + ArchPair anArch = *it; + if ( cpuType == anArch.arch ) { + switch ( cpuType ) { + case CPU_TYPE_ARM: + if ( cpuSubType == anArch.subtype ) + return true; + break; + default: + return true; + } + } } return false; } @@ -414,8 +326,13 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setnfat_arch); - std::set slicesToUse; - if ( onlyArchs == NULL ) { - // no filter, so instantiate all slices - for (uint32_t i=0; i < sliceCount; ++i) - slicesToUse.insert(i); - } - else { - // instantiate only slices that are best for each architecture - for (std::set::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) { - uint32_t bestSliceIndex; - if ( bestSliceForArch(sliceCount, slices, *it, bestSliceIndex) ) - slicesToUse.insert(bestSliceIndex); - } - } for (uint32_t i=0; i < sliceCount; ++i) { - if ( slicesToUse.count(i) ) { + if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) { uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset); if ( fileOffset > stat_buf.st_size ) { throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s", i, OSSwapBigToHostInt32(slices[i].cputype), path); } + if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) { + throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s", + i, OSSwapBigToHostInt32(slices[i].cputype), path); + } try { switch ( OSSwapBigToHostInt32(slices[i].cputype) ) { case CPU_TYPE_POWERPC: @@ -485,19 +392,19 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setmagic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { - if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(mh->cputype), OSSwapBigToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { - if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { - if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { - if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { @@ -522,7 +429,8 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid) : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0), - fShareableLocation(false) + fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), + fHasDyldInfo(false), fDyldInfoExports(NULL) { fDylibID.name = NULL; fDylibID.currentVersion = 0; @@ -533,6 +441,8 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* throw "Layout object is wrong architecture"; switch ( mh->filetype() ) { case MH_DYLIB: + fIsDylib = true; + break; case MH_BUNDLE: case MH_EXECUTE: case MH_DYLIB_STUB: @@ -546,6 +456,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fArchPair.arch = mh->cputype(); fArchPair.subtype = mh->cpusubtype(); + const macho_dyld_info_command

* dyldInfo = NULL; + const macho_symtab_command

* symbolTableCmd = NULL; + const macho_dysymtab_command

* dynamicSymbolTableCmd = NULL; 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; @@ -564,6 +477,7 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: { macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; Library lib; @@ -584,6 +498,17 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* segCmd->filesize(), segCmd->initprot(), segCmd->segname())); } break; + case LC_SYMTAB: + symbolTableCmd = (macho_symtab_command

*)cmd; + break; + case LC_DYSYMTAB: + dynamicSymbolTableCmd = (macho_dysymtab_command

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fHasDyldInfo = true; + dyldInfo = (struct macho_dyld_info_command

*)cmd; + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -617,10 +542,31 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) ) fLowReadOnlySegment = &seg; fVMReadOnlySize += seg.size(); - } + } } if ( (highSegment != NULL) && (fLowSegment != NULL) ) fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); + + // scan undefines looking, for magic ordinals + if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) { + const macho_nlist

* symbolTable = (macho_nlist

*)((uint8_t*)machHeader + symbolTableCmd->symoff()); + const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym(); + const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym(); + for (uint32_t i=startUndefs; i < endUndefs; ++i) { + uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc()); + if ( ordinal == DYNAMIC_LOOKUP_ORDINAL ) + fDynamicLookupLinkage = true; + else if ( ordinal == EXECUTABLE_ORDINAL ) + fMainExecutableLookupLinkage = true; + } + } + + if ( dyldInfo != NULL ) { + if ( dyldInfo->export_off() != 0 ) { + fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off(); + } + } + } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_POWERPC; } diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp index 9dcfc6b..8907aad 100644 --- a/launch-cache/MachORebaser.hpp +++ b/launch-cache/MachORebaser.hpp @@ -57,7 +57,7 @@ public: virtual cpu_type_t getArchitecture() const = 0; virtual uint64_t getBaseAddress() const = 0; virtual uint64_t getVMSize() const = 0; - virtual void rebase() = 0; + virtual void rebase(std::vector&) = 0; }; @@ -71,7 +71,7 @@ public: virtual cpu_type_t getArchitecture() const; virtual uint64_t getBaseAddress() const; virtual uint64_t getVMSize() const; - virtual void rebase(); + virtual void rebase(std::vector&); protected: typedef typename A::P P; @@ -85,11 +85,13 @@ private: void calculateRelocBase(); void adjustLoadCommands(); void adjustSymbolTable(); + void optimzeStubs(); + void makeNoPicStub(uint8_t* stub, pint_t logicalAddress); void adjustDATA(); void adjustCode(); - void applyRebaseInfo(); + void applyRebaseInfo(std::vector& pointersInData); void adjustExportInfo(); - void doRebase(int segIndex, uint64_t segOffset, uint8_t type); + void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData); void adjustSegmentLoadCommand(macho_segment_command

* seg); pint_t getSlideForVMAddress(pint_t vmaddress); pint_t* mappedAddressForVMAddress(pint_t vmaddress); @@ -111,6 +113,8 @@ private: const macho_dyld_info_command

* fDyldInfo; bool fSplittingSegments; bool fOrignalVMRelocBaseAddressValid; + pint_t fSkipSplitSegInfoStart; + pint_t fSkipSplitSegInfoEnd; }; @@ -118,7 +122,8 @@ private: template Rebaser::Rebaser(const MachOLayoutAbstraction& layout) : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), - fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), fOrignalVMRelocBaseAddressValid(false) + fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), + fOrignalVMRelocBaseAddressValid(false), fSkipSplitSegInfoStart(0), fSkipSplitSegInfoEnd(0) { fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); switch ( fHeader->filetype() ) { @@ -205,11 +210,11 @@ uint64_t Rebaser::getVMSize() const template -void Rebaser::rebase() +void Rebaser::rebase(std::vector& pointersInData) { // update writable segments that have internal pointers if ( fDyldInfo != NULL ) - this->applyRebaseInfo(); + this->applyRebaseInfo(pointersInData); else this->adjustDATA(); @@ -225,6 +230,9 @@ void Rebaser::rebase() // update symbol table this->adjustSymbolTable(); + // optimize stubs + this->optimzeStubs(); + // update export info if ( fDyldInfo != NULL ) this->adjustExportInfo(); @@ -262,6 +270,7 @@ void Rebaser::adjustLoadCommands() case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { // clear expected timestamps so that this image will load with invalid prebinding macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; @@ -359,6 +368,66 @@ typename A::P::uint_t* Rebaser::mappedAddressForRelocAddress(pint_t r_address } +template <> +void Rebaser::makeNoPicStub(uint8_t* stub, pint_t logicalAddress) +{ + uint32_t* instructions = (uint32_t*)stub; + if ( (LittleEndian::get32(instructions[0]) == 0xE59FC004) && + (LittleEndian::get32(instructions[1]) == 0xE08FC00C) && + (LittleEndian::get32(instructions[2]) == 0xE59CF000) ) { + uint32_t lazyPtrAddress = instructions[3] + logicalAddress + 12; + LittleEndian::set32(instructions[0], 0xE59FC000); // ldr ip, [pc, #0] + LittleEndian::set32(instructions[1], 0xE59CF000); // ldr pc, [ip] + LittleEndian::set32(instructions[2], lazyPtrAddress); // .long L_foo$lazy_ptr + LittleEndian::set32(instructions[3], 0xE1A00000); // nop + } + else + fprintf(stderr, "unoptimized stub in %s at 0x%08X\n", fLayout.getFilePath(), logicalAddress); +} + + +#if 0 +// disable this optimization do allow cache to slide +template <> +void Rebaser::optimzeStubs() +{ + // convert pic stubs to no-pic stubs in dyld shared cache + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

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

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS ) { + const uint32_t stubSize = sect->reserved2(); + // ARM PIC stubs are 4 32-bit instructions long + if ( stubSize == 16 ) { + uint32_t stubCount = sect->size() / 16; + pint_t stubLogicalAddress = sect->addr(); + uint8_t* stubMappedAddress = (uint8_t*)mappedAddressForNewAddress(stubLogicalAddress); + for(uint32_t s=0; s < stubCount; ++s) { + makeNoPicStub(stubMappedAddress, stubLogicalAddress); + stubLogicalAddress += 16; + stubMappedAddress += 16; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} +#endif + +template +void Rebaser::optimzeStubs() +{ + // other architectures don't need stubs changed in shared cache +} + template void Rebaser::adjustSymbolTable() { @@ -388,7 +457,7 @@ void Rebaser::adjustExportInfo() // since export info addresses are offsets from mach_header, everything in __TEXT is fine // only __DATA addresses need to be updated - const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off()]; + const uint8_t* start = fLayout.getDyldInfoExports(); const uint8_t* end = &start[fDyldInfo->export_size()]; std::vector originalExports; try { @@ -425,29 +494,13 @@ void Rebaser::adjustExportInfo() while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 ) newExportTrieBytes.push_back(0); - // copy into place, zero pad + // allocate new buffer and set export_off to use new buffer instead uint32_t newExportsSize = newExportTrieBytes.size(); - if ( newExportsSize > fDyldInfo->export_size() ) { - // it is possible that the new export trie is larger than the old one - // for those cases will malloc a block on the side and set up - // export_off to point to it. - uint8_t* sideTrie = new uint8_t[newExportsSize]; - memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); - //fprintf(stderr, "set_export_off()=%ld, fLinkEditBase=%p, sideTrie=%p\n", (long)(sideTrie - fLinkEditBase), fLinkEditBase, sideTrie); - // warning, export_off is only 32-bits so if the trie grows it must be allocated with 32-bits of fLinkeditBase - int64_t offset = sideTrie - fLinkEditBase; - int32_t offset32 = (int32_t)offset; - if ( offset != offset32 ) - throw "internal error, new trie allocated to far from fLinkeditBase"; - ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(offset32); - ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); - } - else { - uint8_t* trie = (uint8_t*)&fLinkEditBase[fDyldInfo->export_off()]; - memcpy(trie, &newExportTrieBytes[0], newExportsSize); - bzero(trie+newExportsSize, fDyldInfo->export_size() - newExportsSize); - ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); - } + uint8_t* sideTrie = new uint8_t[newExportsSize]; + memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); + fLayout.setDyldInfoExports(sideTrie); + ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(0); // invalidate old trie + ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); } @@ -455,7 +508,17 @@ void Rebaser::adjustExportInfo() template void Rebaser::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta) { - //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta); + // begin hack for split seg info wrong for x86_64 stub helpers + if ( (fSkipSplitSegInfoStart <= address) && (address < fSkipSplitSegInfoEnd) ) { + uint8_t* p = (uint8_t*)mappedAddressForVMAddress(address); + // only ignore split seg info for "push" instructions + if ( p[-1] == 0x68 ) + return; + } + // end hack for + + //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n", + // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath()); uint32_t* p; uint32_t instruction; uint32_t value; @@ -530,6 +593,7 @@ void Rebaser::adjustCode() // get uleb128 compressed runs of code addresses to update const uint8_t* infoStart = NULL; const uint8_t* infoEnd = NULL; + const macho_segment_command

* seg; const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

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

* cmd = cmds; @@ -542,6 +606,21 @@ void Rebaser::adjustCode() infoEnd = &infoStart[segInfo->datasize()]; } break; + // begin hack for split seg info wrong for x86_64 stub helpers + case macho_segment_command

::CMD: + seg = (macho_segment_command

*)cmd; + if ( (getArchitecture() == CPU_TYPE_X86_64) && (strcmp(seg->segname(), "__TEXT") == 0) ) { + 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->sectname(), "__stub_helper") == 0 ) { + fSkipSplitSegInfoStart = sect->addr(); + fSkipSplitSegInfoEnd = sect->addr() + sect->size() - 16; + } + } + } + break; + // end hack for split seg info wrong for x86_64 stub helpers } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -559,7 +638,7 @@ void Rebaser::adjustCode() codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); } // decompress and call doCodeUpdate() on each address - for(const uint8_t* p = infoStart; *p != 0;) { + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { uint8_t kind = *p++; p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta); } @@ -567,7 +646,7 @@ void Rebaser::adjustCode() } template -void Rebaser::doRebase(int segIndex, uint64_t segOffset, uint8_t type) +void Rebaser::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData) { const std::vector& segments = fLayout.getSegments(); if ( segIndex > segments.size() ) @@ -603,11 +682,12 @@ void Rebaser::doRebase(int segIndex, uint64_t segOffset, uint8_t type) default: throw "bad rebase type"; } + pointersInData.push_back(mappedAddr); } template -void Rebaser::applyRebaseInfo() +void Rebaser::applyRebaseInfo(std::vector& pointersInData) { const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()]; const uint8_t* end = &p[fDyldInfo->rebase_size()]; @@ -641,26 +721,26 @@ void Rebaser::applyRebaseInfo() break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: for (int i=0; i < immediate; ++i) { - doRebase(segIndex, segOffset, type); + doRebase(segIndex, segOffset, type, pointersInData); segOffset += sizeof(pint_t); } break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: count = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - doRebase(segIndex, segOffset, type); + doRebase(segIndex, segOffset, type, pointersInData); segOffset += sizeof(pint_t); } break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: - doRebase(segIndex, segOffset, type); + doRebase(segIndex, segOffset, type, pointersInData); segOffset += read_uleb128(p, end) + sizeof(pint_t); break; case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { - doRebase(segIndex, segOffset, type); + doRebase(segIndex, segOffset, type, pointersInData); segOffset += skip + sizeof(pint_t); } break; diff --git a/launch-cache/MachOTrie.hpp b/launch-cache/MachOTrie.hpp index 7c5d4a7..05a8e07 100644 --- a/launch-cache/MachOTrie.hpp +++ b/launch-cache/MachOTrie.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,9 +24,11 @@ #ifndef __MACH_O_TRIE__ #define __MACH_O_TRIE__ +#include #include "MachOFileAbstraction.hpp" + namespace mach_o { namespace trie { @@ -41,25 +43,28 @@ struct Edge struct Node { - Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + 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; - uint32_t fFlags; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; bool fOrdered; bool fHaveExportInfo; uint32_t fTrieOffset; - void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + 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; int 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); + e.fChild->addSymbol(fullStr, address, flags, other, importName); return; } else { @@ -80,18 +85,24 @@ struct Node abEdge.fChild = bNode; Edge bcEdge(bcEdgeStr, cNode); bNode->fChildren.push_back(bcEdge); - bNode->addSymbol(fullStr, address, flags); + 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; } @@ -114,14 +125,26 @@ struct Node } // byte for terminal node size in bytes, or 0x00 if not terminal node - // teminal node (uleb128 flags, uleb128 addr) + // 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; // byte for length of export info - if ( fHaveExportInfo ) - nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress); - + 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) { @@ -138,13 +161,42 @@ struct Node void appendToStream(std::vector& out) { if ( fHaveExportInfo ) { - // nodes with export info: size, flags, address - out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress)); - append_uleb128(fFlags, out); - append_uleb128(fAddress, out); + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1; + 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 + // no export info uleb128 of zero is one byte of zero out.push_back(0); } // write number of children @@ -215,22 +267,25 @@ struct Entry const char* name; uint64_t address; uint64_t flags; + uint64_t other; + const char* importName; }; -inline void makeTrie(const std::vector& input, std::vector& output) + +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 = input.begin(); it != input.end(); ++it) { - start.addSymbol(it->name, it->address, it->flags); + 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(input.size()*2); - for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { + orderedNodes.reserve(entries.size()*2); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { start.addOrderedNodes(it->name, orderedNodes); } @@ -262,18 +317,31 @@ struct EntryWithOffset 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) + char* cummulativeString, int curStrOffset, + std::vector& output) { if ( p >= end ) - throwf("malformed trie, node %p outside trie (%p -> %p)", p, start, end); - const uint8_t terminalSize = *p++; + throw "malformed trie, node past end"; + 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); - e.entry.address = 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++; @@ -296,7 +364,6 @@ inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector entries; processExportNode(start, start, end, cummulativeString, 0, entries); diff --git a/launch-cache/ObjCLegacyAbstraction.hpp b/launch-cache/ObjCLegacyAbstraction.hpp index ea0329b..43ab45a 100644 --- a/launch-cache/ObjCLegacyAbstraction.hpp +++ b/launch-cache/ObjCLegacyAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -67,8 +67,8 @@ struct objc_class { uint32_t ivar_layout; // const char * uint32_t ext; // struct objc_class_ext * - struct objc_class *getIsa(SharedCache *cache) const INLINE { return (struct objc_class *)cache->mappedCacheAddressForAddress(A::P::E::get32(isa)); } - struct objc_method_list *getMethodList(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedCacheAddressForAddress(A::P::E::get32(methodList)); } + struct objc_class *getIsa(SharedCache *cache) const INLINE { return (struct objc_class *)cache->mappedAddressForVMAddress(A::P::E::get32(isa)); } + struct objc_method_list *getMethodList(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedAddressForVMAddress(A::P::E::get32(methodList)); } }; template @@ -81,8 +81,8 @@ struct objc_category { uint32_t size; // uint32_t uint32_t instance_properties; // struct objc_property_list * - struct objc_method_list *getInstanceMethods(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedCacheAddressForAddress(A::P::E::get32(instance_methods)); } - struct objc_method_list *getClassMethods(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedCacheAddressForAddress(A::P::E::get32(class_methods)); } + struct objc_method_list *getInstanceMethods(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); } + struct objc_method_list *getClassMethods(SharedCache *cache) const INLINE { return (struct objc_method_list *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); } }; template @@ -95,8 +95,8 @@ struct objc_symtab { uint16_t getClassCount(void) const INLINE { return A::P::E::get16(cls_def_cnt); } uint16_t getCategoryCount(void) const INLINE { return A::P::E::get16(cat_def_cnt); } - struct objc_class *getClass(SharedCache *cache, int index) const INLINE { return (struct objc_class *)cache->mappedCacheAddressForAddress(A::P::E::get32(defs[index])); } - struct objc_category *getCategory(SharedCache *cache, int index) const INLINE { return (struct objc_category *)cache->mappedCacheAddressForAddress(A::P::E::get32(defs[getClassCount() + index])); } + struct objc_class *getClass(SharedCache *cache, int index) const INLINE { return (struct objc_class *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[index])); } + struct objc_category *getCategory(SharedCache *cache, int index) const INLINE { return (struct objc_category *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[getClassCount() + index])); } }; template @@ -106,7 +106,7 @@ struct objc_module { uint32_t name; // char* uint32_t symtab; // Symtab - struct objc_symtab *getSymtab(SharedCache *cache) const INLINE { return (struct objc_symtab *)cache->mappedCacheAddressForAddress(A::P::E::get32(symtab)); } + struct objc_symtab *getSymtab(SharedCache *cache) const INLINE { return (struct objc_symtab *)cache->mappedAddressForVMAddress(A::P::E::get32(symtab)); } }; template @@ -134,8 +134,8 @@ struct objc_protocol { uint32_t instance_methods; // struct objc_method_description_list * uint32_t class_methods; // struct objc_method_description_list * - struct objc_method_description_list *getInstanceMethodDescriptions(SharedCache *cache) const INLINE { return (struct objc_method_description_list *)cache->mappedCacheAddressForAddress(A::P::E::get32(instance_methods)); } - struct objc_method_description_list *getClassMethodDescriptions(SharedCache *cache) const INLINE { return (struct objc_method_description_list *)cache->mappedCacheAddressForAddress(A::P::E::get32(class_methods)); } + struct objc_method_description_list *getInstanceMethodDescriptions(SharedCache *cache) const INLINE { return (struct objc_method_description_list *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); } + struct objc_method_description_list *getClassMethodDescriptions(SharedCache *cache) const INLINE { return (struct objc_method_description_list *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); } }; @@ -227,7 +227,7 @@ public: header->getSection("__OBJC", "__image_info"); if (imageInfoSection) { objc_image_info *info = (objc_image_info *) - cache->mappedCacheAddressForAddress(imageInfoSection->addr()); + cache->mappedAddressForVMAddress(imageInfoSection->addr()); info->setSelectorsPrebound(); } } diff --git a/launch-cache/ObjCModernAbstraction.hpp b/launch-cache/ObjCModernAbstraction.hpp index ef200a5..9dc9b90 100644 --- a/launch-cache/ObjCModernAbstraction.hpp +++ b/launch-cache/ObjCModernAbstraction.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,15 +22,114 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include +#include + +// iterate an entsize-based list +// typedef entsize_iterator< A, type_t, type_list_t > type_iterator; +template +struct entsize_iterator { + uint32_t entsize; + uint32_t index; // keeping track of this saves a divide in operator- + T* current; + + typedef std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + + entsize_iterator() { } + + entsize_iterator(const Tlist& list, uint32_t start = 0) + : entsize(list.getEntsize()), index(start), current(&list.get(start)) + { } + + const entsize_iterator& operator += (ptrdiff_t count) { + current = (T*)((uint8_t *)current + count*entsize); + index += count; + return *this; + } + const entsize_iterator& operator -= (ptrdiff_t count) { + current = (T*)((uint8_t *)current - count*entsize); + index -= count; + return *this; + } + const entsize_iterator operator + (ptrdiff_t count) const { + return entsize_iterator(*this) += count; + } + const entsize_iterator operator - (ptrdiff_t count) const { + return entsize_iterator(*this) -= count; + } + + entsize_iterator& operator ++ () { *this += 1; return *this; } + entsize_iterator& operator -- () { *this -= 1; return *this; } + entsize_iterator operator ++ (int) { + entsize_iterator result(*this); *this += 1; return result; + } + entsize_iterator operator -- (int) { + entsize_iterator result(*this); *this -= 1; return result; + } + + ptrdiff_t operator - (const entsize_iterator& rhs) const { + return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index; + } + + T& operator * () { return *current; } + const T& operator * () const { return *current; } + T& operator -> () { return *current; } + const T& operator -> () const { return *current; } + + operator T& () const { return *current; } + + bool operator == (const entsize_iterator& rhs) { + return this->current == rhs.current; + } + bool operator != (const entsize_iterator& rhs) { + return this->current != rhs.current; + } + + bool operator < (const entsize_iterator& rhs) { + return this->current < rhs.current; + } + bool operator > (const entsize_iterator& rhs) { + return this->current > rhs.current; + } + + + static void overwrite(entsize_iterator& dst, const Tlist* srcList) + { + entsize_iterator src; + uint32_t ee = srcList->getEntsize(); + for (src = srcList->begin(); src != srcList->end(); ++src) { + memcpy(&*dst, &*src, ee); + ++dst; + } + } +}; + +template class objc_method_list_t; // forward reference + template class objc_method_t { typename A::P::uint_t name; // SEL typename A::P::uint_t types; // const char * typename A::P::uint_t imp; // IMP - + friend class objc_method_list_t; public: typename A::P::uint_t getName() const { return A::P::getP(name); } void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); } + + struct SortBySELAddress : + public std::binary_function&, + const objc_method_t&, bool> + { + bool operator() (const objc_method_t& lhs, + const objc_method_t& rhs) + { + return lhs.getName() < rhs.getName(); + } + }; }; template @@ -39,14 +138,67 @@ class objc_method_list_t { uint32_t count; objc_method_t first; - uint32_t getEntsize() const { return A::P::E::get32(entsize) & ~(uint32_t)3; } + // use newMethodList instead + void* operator new (size_t) { return NULL; } + void* operator new (size_t, void* buf) { return buf; } public: - typename A::P::uint_t getCount() const { return A::P::E::get32(count); } - objc_method_t& get(typename A::P::uint_t i) const { return *(objc_method_t *)((uint8_t *)&first + i * getEntsize()); } + typedef entsize_iterator< A, objc_method_t, objc_method_list_t > method_iterator; + + uint32_t getCount() const { return A::P::E::get32(count); } + + uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;} + + objc_method_t& get(uint32_t i) const { return *(objc_method_t *)((uint8_t *)&first + i * getEntsize()); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t)) { + return sizeof(objc_method_list_t) - sizeof(objc_method_t) + c*e; + } + + method_iterator begin() { return method_iterator(*this, 0); } + method_iterator end() { return method_iterator(*this, getCount()); } + const method_iterator begin() const { return method_iterator(*this, 0); } + const method_iterator end() const { return method_iterator(*this, getCount()); } void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); } + + void getPointers(std::set& 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)); + } + } + + static void addPointers(uint8_t* methodList, std::vector& pointersToAdd) { + 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; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.types)); + pointersToAdd.push_back(&(entry.imp)); + } + } + + 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); + } + + 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) + { } }; template @@ -64,8 +216,128 @@ class objc_ivar_list_t { uint32_t count; objc_ivar_t first; + // use newIvarList instead + void* operator new (size_t) { return NULL; } + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator< A, objc_ivar_t, objc_ivar_list_t > ivar_iterator; + + uint32_t getCount() const { return A::P::E::get32(count); } + + uint32_t getEntsize() const { return A::P::E::get32(entsize); } + + objc_ivar_t& get(typename A::P::pint_t i) const { return *(objc_ivar_t *)((uint8_t *)&first + i * A::P::E::get32(entsize)); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t)) { + return sizeof(objc_ivar_list_t) - sizeof(objc_ivar_t) + c*e; + } + + ivar_iterator begin() { return ivar_iterator(*this, 0); } + ivar_iterator end() { return ivar_iterator(*this, getCount()); } + const ivar_iterator begin() const { return ivar_iterator(*this, 0); } + const ivar_iterator end() const { return ivar_iterator(*this, getCount()); } + + static objc_ivar_list_t* newIvarList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_ivar_list_t(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_ivar_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_ivar_t)) + : entsize(newEntsize), count(newCount) + { } + +}; + + +template class objc_property_list_t; // forward + +template +class objc_property_t { + typename A::P::uint_t name; + typename A::P::uint_t attributes; + friend class objc_property_list_t; public: - objc_ivar_t& getIvarAtIndex(typename A::P::pint_t i) const { return *(objc_ivar_t *)((uint8_t *)&first + i * A::P::E::get32(entsize)); } + + const char * getName(SharedCache* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); } + + const char * getAttributes(SharedCache* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); } +}; + +template +class objc_property_list_t { + uint32_t entsize; + uint32_t count; + objc_property_t first; + + // use newPropertyList instead + void* operator new (size_t) { return NULL; } + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator< A, objc_property_t, objc_property_list_t > property_iterator; + + uint32_t getCount() const { return A::P::E::get32(count); } + + uint32_t getEntsize() const { return A::P::E::get32(entsize); } + + objc_property_t& get(uint32_t i) const { return *(objc_property_t *)((uint8_t *)&first + i * getEntsize()); } + + uint32_t byteSize() const { + return byteSizeForCount(getCount(), getEntsize()); + } + + static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t)) { + return sizeof(objc_property_list_t) - sizeof(objc_property_t) + c*e; + } + + property_iterator begin() { return property_iterator(*this, 0); } + property_iterator end() { return property_iterator(*this, getCount()); } + const property_iterator begin() const { return property_iterator(*this, 0); } + const property_iterator end() const { return property_iterator(*this, getCount()); } + + void getPointers(std::set& pointersToRemove) { + for(property_iterator it = begin(); it != end(); ++it) { + objc_property_t& entry = *it; + pointersToRemove.insert(&(entry.name)); + pointersToRemove.insert(&(entry.attributes)); + } + } + + static void addPointers(uint8_t* propertyList, std::vector& pointersToAdd) { + objc_property_list_t* plist = (objc_property_list_t*)propertyList; + for(property_iterator it = plist->begin(); it != plist->end(); ++it) { + objc_property_t& entry = *it; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.attributes)); + } + } + + static objc_property_list_t* newPropertyList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_property_list_t(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_property_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_property_t)) + : entsize(newEntsize), count(newCount) + { } + }; template @@ -80,35 +352,85 @@ class objc_protocol_t { typename A::P::uint_t instanceProperties; public: - objc_method_list_t *getInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(instanceMethods)); } + objc_method_list_t *getInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); } - objc_method_list_t *getClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(classMethods)); } + objc_method_list_t *getClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); } - objc_method_list_t *getOptionalInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(optionalInstanceMethods)); } + objc_method_list_t *getOptionalInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); } - objc_method_list_t *getOptionalClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(optionalClassMethods)); } + objc_method_list_t *getOptionalClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); } }; template class objc_protocol_list_t { - typename A::P::uint_t count; - typename A::P::uint_t list[0]; -}; + typedef typename A::P::uint_t pint_t; + pint_t count; + pint_t list[0]; + + // use newProtocolList instead + void* operator new (size_t) { return NULL; } + void* operator new (size_t, void* buf) { return buf; } + +public: + + pint_t getCount() const { return A::P::getP(count); } + + objc_protocol_t* get(SharedCache* cache, pint_t i) { + return (objc_protocol_t*)cache->mappedAddressForVMAddress(A::P::getP(list[i])); + } + + void overwrite(pint_t& index, const objc_protocol_list_t* src) { + pint_t srcCount = src->getCount(); + memcpy(list+index, src->list, srcCount * sizeof(pint_t)); + index += srcCount; + } + + uint32_t byteSize() const { + return byteSizeForCount(getCount()); + } + static uint32_t byteSizeForCount(pint_t c) { + return sizeof(objc_protocol_list_t) + c*sizeof(pint_t); + } -template < typename P, typename E > -struct pad { }; + void getPointers(std::set& pointersToRemove) { + for(int i=0 ; i < count; ++i) { + pointersToRemove.insert(&list[i]); + } + } + + static void addPointers(uint8_t* protocolList, std::vector& pointersToAdd) { + objc_protocol_list_t* plist = (objc_protocol_list_t*)protocolList; + for(int i=0 ; i < plist->count; ++i) { + pointersToAdd.push_back(&plist->list[i]); + } + } + + static objc_protocol_list_t* newProtocolList(pint_t newCount) { + void *buf = ::calloc(byteSizeForCount(newCount), 1); + return new (buf) objc_protocol_list_t(newCount); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_protocol_list_t(uint32_t newCount) : count(newCount) { } + +}; -template < typename E > -struct pad< Pointer64, E > { uint32_t unused; }; template class objc_class_data_t { uint32_t flags; uint32_t instanceStart; - uint32_t instanceSize; - pad reserved; // ILP32=0 bytes, LP64=4 bytes - + // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout + // on 64-bit archs, but no padding on 32-bit archs. + // This union is a way to model that. + union { + uint32_t instanceSize; + typename A::P::uint_t pad; + } instanceSize; typename A::P::uint_t ivarLayout; typename A::P::uint_t name; typename A::P::uint_t baseMethods; @@ -118,7 +440,37 @@ class objc_class_data_t { typename A::P::uint_t baseProperties; public: - objc_method_list_t *getMethodList(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(baseMethods)); } + objc_method_list_t *getMethodList(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); } + + objc_protocol_list_t *getProtocolList(SharedCache* cache) const { return (objc_protocol_list_t *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); } + + objc_property_list_t *getPropertyList(SharedCache* cache) const { return (objc_property_list_t *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); } + + const char * getName(SharedCache* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); } + + void setMethodList(SharedCache* cache, objc_method_list_t* mlist) { + A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist)); + } + + void setProtocolList(SharedCache* cache, objc_protocol_list_t* protolist) { + A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist)); + } + + void setPropertyList(SharedCache* cache, objc_property_list_t* proplist) { + A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist)); + } + + void addMethodListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseMethods); + } + + void addPropertyListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProperties); + } + + void addProtocolListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProtocols); + } }; template @@ -130,11 +482,44 @@ class objc_class_t { typename A::P::uint_t data; public: - objc_class_t *getIsa(SharedCache *cache) const { return (objc_class_t *)cache->mappedCacheAddressForAddress(A::P::getP(isa)); } + objc_class_t *getIsa(SharedCache *cache) const { return (objc_class_t *)cache->mappedAddressForVMAddress(A::P::getP(isa)); } - objc_class_data_t *getData(SharedCache* cache) const { return (objc_class_data_t *)cache->mappedCacheAddressForAddress(A::P::getP(data)); } + objc_class_data_t *getData(SharedCache* cache) const { return (objc_class_data_t *)cache->mappedAddressForVMAddress(A::P::getP(data)); } objc_method_list_t *getMethodList(SharedCache* cache) const { return getData(cache)->getMethodList(cache); } + + objc_protocol_list_t *getProtocolList(SharedCache* cache) const { return getData(cache)->getProtocolList(cache); } + + objc_property_list_t *getPropertyList(SharedCache* cache) const { return getData(cache)->getPropertyList(cache); } + + const char * getName(SharedCache* cache) const { + return getData(cache)->getName(cache); + } + + void setMethodList(SharedCache* cache, objc_method_list_t* mlist) { + getData(cache)->setMethodList(cache, mlist); + } + + void setProtocolList(SharedCache* cache, objc_protocol_list_t* protolist) { + getData(cache)->setProtocolList(cache, protolist); + } + + void setPropertyList(SharedCache* cache, objc_property_list_t* proplist) { + getData(cache)->setPropertyList(cache, proplist); + } + + void addMethodListPointer(SharedCache* cache, std::vector& pointersToAdd) { + getData(cache)->addMethodListPointer(pointersToAdd); + } + + void addPropertyListPointer(SharedCache* cache, std::vector& pointersToAdd) { + getData(cache)->addPropertyListPointer(pointersToAdd); + } + + void addProtocolListPointer(SharedCache* cache, std::vector& pointersToAdd) { + getData(cache)->addProtocolListPointer(pointersToAdd); + } + }; @@ -149,9 +534,29 @@ class objc_category_t { typename A::P::uint_t instanceProperties; public: - objc_method_list_t *getInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(instanceMethods)); } - objc_method_list_t *getClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(classMethods)); } + const char * getName(SharedCache *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); } + + objc_class_t *getClass(SharedCache *cache) const { return (objc_class_t *)cache->mappedAddressForVMAddress(A::P::getP(cls)); } + + objc_method_list_t *getInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); } + + objc_method_list_t *getClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); } + + objc_protocol_list_t *getProtocols(SharedCache* cache) const { return (objc_protocol_list_t *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); } + + objc_property_list_t *getInstanceProperties(SharedCache* cache) const { return (objc_property_list_t *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); } + + void getPointers(std::set& pointersToRemove) { + pointersToRemove.insert(&name); + pointersToRemove.insert(&cls); + pointersToRemove.insert(&instanceMethods); + pointersToRemove.insert(&classMethods); + pointersToRemove.insert(&protocols); + pointersToRemove.insert(&instanceProperties); + } + + }; template @@ -165,38 +570,34 @@ public: void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); } }; + +// Call visitor.visitMethodList(mlist) on every method list in a header. template -class SelectorUpdater { +class MethodListWalker { typedef typename A::P P; typedef typename A::P::uint_t pint_t; - static void visitMethodList(objc_method_list_t *mlist, V& visitor) - { - for (pint_t m = 0; m < mlist->getCount(); m++) { - pint_t oldValue = mlist->get(m).getName(); - pint_t newValue = visitor.visit(oldValue); - mlist->get(m).setName(newValue); - } - mlist->setFixedUp(); - } + V& mVisitor; -public: +public: + + MethodListWalker(V& visitor) : mVisitor(visitor) { } - static void update(SharedCache* cache, const macho_header

* header, - V& visitor) - { + void walk(SharedCache* cache, const macho_header

* header) + { // Method lists in classes PointerSection *> classes(cache, header, "__DATA", "__objc_classlist"); + for (pint_t i = 0; i < classes.count(); i++) { objc_class_t *cls = classes.get(i); objc_method_list_t *mlist; if ((mlist = cls->getMethodList(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } if ((mlist = cls->getIsa(cache)->getMethodList(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } } @@ -207,10 +608,10 @@ public: objc_category_t *cat = cats.get(i); objc_method_list_t *mlist; if ((mlist = cat->getInstanceMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } if ((mlist = cat->getClassMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } } @@ -221,25 +622,59 @@ public: objc_protocol_t *proto = protocols.get(i); objc_method_list_t *mlist; if ((mlist = proto->getInstanceMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } if ((mlist = proto->getClassMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } if ((mlist = proto->getOptionalInstanceMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } if ((mlist = proto->getOptionalClassMethods(cache))) { - visitMethodList(mlist, visitor); + mVisitor.visitMethodList(mlist); } } + } +}; + +// Update selector references. The visitor performs recording and uniquing. +template +class SelectorOptimizer { + + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + V& mVisitor; + + friend class MethodListWalker< A, SelectorOptimizer >; + void visitMethodList(objc_method_list_t *mlist) + { + // Gather selectors. Update method names. + for (pint_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->get(m).getName(); + pint_t newValue = mVisitor.visit(oldValue); + mlist->get(m).setName(newValue); + } + // Do not setFixedUp: the methods are not yet sorted. + } + +public: + + SelectorOptimizer(V& visitor) : mVisitor(visitor) { } + + void optimize(SharedCache* cache, const macho_header

* header) + { + // method lists of all kinds + MethodListWalker< A, SelectorOptimizer > mw(*this); + mw.walk(cache, header); + // @selector references PointerSection selrefs(cache, header, "__DATA", "__objc_selrefs"); for (pint_t i = 0; i < selrefs.count(); i++) { pint_t oldValue = selrefs.getUnmapped(i); - pint_t newValue = visitor.visit(oldValue); + pint_t newValue = mVisitor.visit(oldValue); selrefs.set(i, newValue); } @@ -249,7 +684,7 @@ public: for (pint_t i = 0; i < msgrefs.count(); i++) { objc_message_ref_t& msg = msgrefs.get(i); pint_t oldValue = msg.getName(); - pint_t newValue = visitor.visit(oldValue); + pint_t newValue = mVisitor.visit(oldValue); msg.setName(newValue); } @@ -258,8 +693,562 @@ public: header->getSection("__DATA", "__objc_imageinfo"); if (imageInfoSection) { objc_image_info *info = (objc_image_info *) - cache->mappedCacheAddressForAddress(imageInfoSection->addr()); + cache->mappedAddressForVMAddress(imageInfoSection->addr()); info->setSelectorsPrebound(); } } }; + + +// Sort methods in place by selector. +template +class MethodListSorter { + + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + 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(); + } + +public: + + void optimize(SharedCache* cache, macho_header

* header) + { + MethodListWalker > mw(*this); + mw.walk(cache, header); + } +}; + + +// Attach categories to classes in the same framework. +// Merge method and protocol and property lists. +template +class CategoryAttacher { + + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + + uint8_t *mBytes; + ssize_t mBytesFree; + ssize_t mBytesUsed; + + size_t mCategoriesAttached; + + bool segmentContainsPointer(SharedCache* cache, + const macho_segment_command

* seg, void *ptr) + { + if (!seg) return false; + void *start = (void*) + cache->mappedAddressForVMAddress(seg->vmaddr()); + void *end = (uint8_t *)start + seg->filesize(); + return (ptr >= start && ptr < end); + } + + bool headerContainsPointer(SharedCache* cache, + macho_header

* header, void *ptr) + { + return + segmentContainsPointer(cache, header->getSegment("__DATA"), ptr) || + segmentContainsPointer(cache, header->getSegment("__TEXT"), ptr) || + segmentContainsPointer(cache, header->getSegment("__OBJC"), ptr); + } + + struct pointer_hash { + size_t operator () (void* ptr) const { + return __gnu_cxx::hash()((long)ptr); + } + }; + + typedef std::deque*> CategoryList; + typedef std::vector CategoryRefs; + + struct ClassChanges { + CategoryList categories; + CategoryRefs catrefs; + + objc_method_list_t* instanceMethods; + objc_method_list_t* classMethods; + objc_protocol_list_t* protocols; + objc_property_list_t* instanceProperties; + + ClassChanges() + : instanceMethods(NULL), classMethods(NULL), + protocols(NULL), instanceProperties(NULL) + { } + + ~ClassChanges() { + if (instanceMethods) delete instanceMethods; + if (classMethods) delete classMethods; + if (protocols) delete protocols; + if (instanceProperties) delete instanceProperties; + } + }; + + typedef __gnu_cxx::hash_map*, ClassChanges, pointer_hash> ClassMap; + + class RangeArray { + typedef std::pair Range; + std::deque ranges; + + class SizeFits { + private: + uint32_t mSize; + public: + SizeFits(uint32_t size) : mSize(size) { } + bool operator() (const Range& r) { + return r.second >= mSize; + } + }; + + struct AddressComp { + bool operator() (const Range& lhs, const Range& rhs) { + return (lhs.first < rhs.first); + } + }; + public: + RangeArray() { } + void add(void* p, uint32_t size) { + add(Range((uint8_t *)p, size)); + } + void add(const Range& r) { + // find insertion point + std::deque::iterator iter; + iter = upper_bound(ranges.begin(), ranges.end(), r, AddressComp()); + // coalesce + // fixme doesn't fully coalesce if new range exactly fills a gap + if (iter != ranges.begin()) { + std::deque::iterator prev = iter - 1; + if ((*prev).first + (*prev).second == r.first) { + (*prev).second += r.second; + return; + } + } + if (iter != ranges.end() && iter+1 != ranges.end()) { + std::deque::iterator next = iter + 1; + if (r.first + r.second == (*next).first) { + (*next).second += r.second; + (*next).first = r.first; + return; + } + } + ranges.insert(iter, r); + } + + uint8_t* remove(uint32_t size) { + // first-fit search + // this saves 50-75% of space overhead; + // a better algorithm might do better + + std::deque::iterator iter; + iter = find_if(ranges.begin(), ranges.end(), SizeFits(size)); + if (iter == ranges.end()) { + return NULL; + } + + Range& found = *iter; + uint8_t *result = found.first; + if (found.second > size) { + // keep leftovers + found.first += size; + found.second -= size; + } else { + ranges.erase(iter); + } + + return result; + } + }; + + void copyMethods(typename objc_method_list_t::method_iterator& dst, + const objc_method_list_t* srcList) + { + objc_method_list_t::method_iterator:: + overwrite(dst, srcList); + } + + void copyProperties(typename objc_property_list_t::property_iterator& dst, + const objc_property_list_t* srcList) + { + objc_property_list_t::property_iterator:: + overwrite(dst, srcList); + } + + void copyProtocols(objc_protocol_list_t* dst, pint_t& dstIndex, + const objc_protocol_list_t* src) + { + dst->overwrite(dstIndex, src); + } + + class InSet + { + public: + InSet(std::set& deadPointers) : _deadPointers(deadPointers) {} + + bool operator()(void* ptr) const { + return ( _deadPointers.count(ptr) != 0 ); + } + + private: + std::set& _deadPointers; + }; + +public: + + CategoryAttacher(uint8_t *bytes, ssize_t bytesFree) + : mBytes(bytes), mBytesFree(bytesFree) + , mBytesUsed(0), mCategoriesAttached(0) + { } + + size_t count() const { return mCategoriesAttached; } + + const char *optimize(SharedCache* cache, macho_header

* header, std::vector& pointersInData) + { + // Build class=>cateories mapping. + // Disregard target classes that aren't in this binary. + + ClassMap map; + + PointerSection *> + nlcatsect(cache, header, "__DATA", "__objc_nlcatlist"); + PointerSection *> + catsect(cache, header, "__DATA", "__objc_catlist"); + for (pint_t i = 0; i < catsect.count(); i++) { + objc_category_t *cat = catsect.get(i); + objc_class_t *cls = cat->getClass(cache); + if (!cls) continue; + if (!headerContainsPointer(cache, header, cls)) continue; + if ( nlcatsect.count() !=0 ) { + // don't optimize categories also in __objc_nlcatlist + bool alsoInNlcatlist = false; + for (pint_t nli = 0; nli < nlcatsect.count(); nli++) { + if ( nlcatsect.get(nli) == cat ) { + //fprintf(stderr, "skipping cat in __objc_nlcatlist for mh=%p\n", header); + alsoInNlcatlist = true; + break; + } + } + if ( alsoInNlcatlist ) + continue; + } + + // The LAST category found is the FIRST to be processed later. + map[cls].categories.push_front(cat); + + // We don't care about the category reference order. + map[cls].catrefs.push_back(i); + } + + if (map.size() == 0) { + // No attachable categories. + return NULL; + } + + // Process each class. + // Each class is all-or-nothing: either all of its categories + // are attached successfully, or none of them are. This preserves + // cache validity if we run out of space for more reallocations. + + // unusedMemory stores memory ranges evacuated by now-unused metadata. + // It is available for re-use by other newly-added metadata. + // fixme could packing algorithm be improved? + RangeArray unusedMemory; + + ssize_t reserve = 0; + + // First: build new aggregated lists on the heap. + // Require enough space in mBytes for all of it. + + std::set pointersToRemove; + for (typename ClassMap::iterator i = map.begin(); + i != map.end(); + ++i) + { + objc_class_t* cls = i->first; + objc_class_t* meta = cls->getIsa(cache); + ClassChanges& changes = i->second; + CategoryList& cats = changes.categories; + + // Count memory needed for all categories on this class. + + uint32_t methodEntsize = 0; + uint32_t propertyEntsize = 0; + objc_method_list_t* mlist; + objc_property_list_t* proplist; + objc_protocol_list_t* protolist; + uint32_t instanceMethodsCount = 0; + uint32_t classMethodsCount = 0; + uint32_t instancePropertyCount = 0; + pint_t protocolCount = 0; + bool addedInstanceMethods = false; + bool addedClassMethods = false; + bool addedInstanceProperties = false; + bool addedProtocols = false; + + mlist = cls->getMethodList(cache); + if (mlist) { + instanceMethodsCount = mlist->getCount(); + methodEntsize = + std::max(methodEntsize, mlist->getEntsize()); + } + + mlist = meta->getMethodList(cache); + if (mlist) { + classMethodsCount = mlist->getCount(); + methodEntsize = + std::max(methodEntsize, mlist->getEntsize()); + } + + proplist = cls->getPropertyList(cache); + if (proplist) { + instancePropertyCount = proplist->getCount(); + propertyEntsize = + std::max(propertyEntsize, proplist->getEntsize()); + } + + protolist = cls->getProtocolList(cache); + if (protolist) { + protocolCount = protolist->getCount(); + } + + typename CategoryList::iterator j; + for (j = cats.begin(); j != cats.end(); ++j) { + objc_category_t* cat = *j; + + mlist = cat->getInstanceMethods(cache); + if (mlist && mlist->getCount() > 0) { + addedInstanceMethods = true; + instanceMethodsCount += mlist->getCount(); + methodEntsize = + std::max(methodEntsize, mlist->getEntsize()); + } + + mlist = cat->getClassMethods(cache); + if (mlist && mlist->getCount() > 0) { + addedClassMethods = true; + classMethodsCount += mlist->getCount(); + methodEntsize = + std::max(methodEntsize, mlist->getEntsize()); + } + + proplist = cat->getInstanceProperties(cache); + if (proplist && proplist->getCount() > 0) { + addedInstanceProperties = true; + instancePropertyCount += proplist->getCount(); + propertyEntsize = + std::max(propertyEntsize, proplist->getEntsize()); + } + + protolist = cat->getProtocols(cache); + if (protolist && protolist->getCount() > 0) { + addedProtocols = true; + protocolCount += protolist->getCount(); + } + } + + // Allocate memory for aggregated lists. + // Reserve the same amount of space from mBytes. + + if (addedInstanceMethods) { + changes.instanceMethods = objc_method_list_t::newMethodList(instanceMethodsCount, methodEntsize); + reserve = P::round_up(reserve + changes.instanceMethods->byteSize()); + } + if (addedClassMethods) { + changes.classMethods = objc_method_list_t::newMethodList(classMethodsCount, methodEntsize); + reserve = P::round_up(reserve + changes.classMethods->byteSize()); + } + if (addedInstanceProperties) { + changes.instanceProperties = objc_property_list_t::newPropertyList(instancePropertyCount, propertyEntsize); + reserve = P::round_up(reserve + changes.instanceProperties->byteSize()); + } + if (addedProtocols) { + changes.protocols = objc_protocol_list_t::newProtocolList(protocolCount); + reserve = P::round_up(reserve + changes.protocols->byteSize()); + } + + // Merge. The LAST category's contents ends up FIRST in each list. + // The aggregated lists are not sorted; a future pass does that. + + typename objc_method_list_t::method_iterator newInstanceMethods; + typename objc_method_list_t::method_iterator newClassMethods; + typename objc_property_list_t::property_iterator newInstanceProperties; + pint_t newProtocolIndex; + + if (addedInstanceMethods) { + newInstanceMethods = changes.instanceMethods->begin(); + } + if (addedClassMethods) { + newClassMethods = changes.classMethods->begin(); + } + if (addedInstanceProperties) { + newInstanceProperties = changes.instanceProperties->begin(); + } + if (addedProtocols) { + newProtocolIndex = 0; + } + + for (j = cats.begin(); j != cats.end(); ++j) { + objc_category_t* cat = *j; + + mlist = cat->getInstanceMethods(cache); + if (mlist) { + copyMethods(newInstanceMethods, mlist); + mlist->getPointers(pointersToRemove); + unusedMemory.add(mlist, mlist->byteSize()); + } + + mlist = cat->getClassMethods(cache); + if (mlist) { + copyMethods(newClassMethods, mlist); + mlist->getPointers(pointersToRemove); + unusedMemory.add(mlist, mlist->byteSize()); + } + + proplist = cat->getInstanceProperties(cache); + if (proplist) { + copyProperties(newInstanceProperties, proplist); + proplist->getPointers(pointersToRemove); + unusedMemory.add(proplist, proplist->byteSize()); + } + + protolist = cat->getProtocols(cache); + if (protolist) { + copyProtocols(changes.protocols, newProtocolIndex, protolist); + protolist->getPointers(pointersToRemove); + unusedMemory.add(protolist, protolist->byteSize()); + } + + cat->getPointers(pointersToRemove); + unusedMemory.add(cat, sizeof(*cat)); + } + + if (addedInstanceMethods && (mlist = cls->getMethodList(cache))) { + copyMethods(newInstanceMethods, mlist); + mlist->getPointers(pointersToRemove); + unusedMemory.add(mlist, mlist->byteSize()); + } + if (addedClassMethods && (mlist = meta->getMethodList(cache))) { + copyMethods(newClassMethods, mlist); + mlist->getPointers(pointersToRemove); + unusedMemory.add(mlist, mlist->byteSize()); + } + if (addedInstanceProperties && (proplist = cls->getPropertyList(cache))) { + copyProperties(newInstanceProperties, proplist); + proplist->getPointers(pointersToRemove); + unusedMemory.add(proplist, proplist->byteSize()); + } + if (addedProtocols && (protolist = cls->getProtocolList(cache))) { + copyProtocols(changes.protocols, newProtocolIndex, protolist); + protolist->getPointers(pointersToRemove); + unusedMemory.add(protolist, protolist->byteSize()); + } + } + + if (reserve > mBytesFree) { + return "insufficient space for category data (metadata not optimized)"; + } + + // update cache slide info and remove areas now longer containing pointers + //fprintf(stderr, "found %lu pointers in objc structures being moved\n", pointersToRemove.size()); + pointersInData.erase(std::remove_if(pointersInData.begin(), pointersInData.end(), InSet(pointersToRemove)), pointersInData.end()); + + + // All lists are now built. + // mBytes is big enough to hold everything if necessary. + // Everything in unusedMemory is now available for re-use. + // The original metadata is still untouched. + + // Second: write lists into mBytes and unusedMemory, + // then disconnect categories. + + for (typename ClassMap::iterator i = map.begin(); + i != map.end(); + ++i) + { + objc_class_t* cls = i->first; + objc_class_t* meta = cls->getIsa(cache); + ClassChanges& changes = i->second; + + // Write lists. + + if (changes.instanceMethods) { + uint8_t *bytes; + uint32_t size = changes.instanceMethods->byteSize(); + if (! (bytes = unusedMemory.remove(size))) { + bytes = mBytes + mBytesUsed; + mBytesFree -= size; + mBytesUsed += size; + } + memcpy(bytes, changes.instanceMethods, size); + objc_method_list_t::addPointers(bytes, pointersInData); + cls->setMethodList(cache, (objc_method_list_t *)bytes); + cls->addMethodListPointer(cache, pointersInData); + } + + if (changes.classMethods) { + uint8_t *bytes; + uint32_t size = changes.classMethods->byteSize(); + if (! (bytes = unusedMemory.remove(size))) { + bytes = mBytes + mBytesUsed; + mBytesFree -= size; + mBytesUsed += size; + } + memcpy(bytes, changes.classMethods, size); + objc_method_list_t::addPointers(bytes, pointersInData); + meta->setMethodList(cache, (objc_method_list_t *)bytes); + meta->addMethodListPointer(cache, pointersInData); + } + + if (changes.instanceProperties) { + uint8_t *bytes; + uint32_t size = changes.instanceProperties->byteSize(); + if (! (bytes = unusedMemory.remove(size))) { + bytes = mBytes + mBytesUsed; + mBytesFree -= size; + mBytesUsed += size; + } + memcpy(bytes, changes.instanceProperties, size); + objc_property_list_t::addPointers(bytes, pointersInData); + cls->setPropertyList(cache, (objc_property_list_t *)bytes); + cls->addPropertyListPointer(cache, pointersInData); + } + + if (changes.protocols) { + uint8_t *bytes; + uint32_t size = changes.protocols->byteSize(); + if (! (bytes = unusedMemory.remove(size))) { + bytes = mBytes + mBytesUsed; + mBytesFree -= size; + mBytesUsed += size; + } + memcpy(bytes, changes.protocols, size); + cls->setProtocolList(cache, (objc_protocol_list_t *)bytes); + objc_protocol_list_t::addPointers(bytes, pointersInData); + cls->addProtocolListPointer(cache, pointersInData); + meta->setProtocolList(cache, (objc_protocol_list_t *)bytes); + meta->addProtocolListPointer(cache, pointersInData); + } + + // Disavow all knowledge of the categories. + + for (typename CategoryRefs::iterator j = changes.catrefs.begin(); + j != changes.catrefs.end(); + ++j) + { + catsect.set(*j, 0); + } + + mCategoriesAttached += changes.categories.size(); + } + + catsect.removeNulls(); + + return NULL; + } + + ssize_t bytesUsed() { return mBytesUsed; } +}; diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp new file mode 100644 index 0000000..b86b366 --- /dev/null +++ b/launch-cache/dsc_extractor.cpp @@ -0,0 +1,443 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" + +#include "dsc_iterator.h" +#include "dsc_extractor.h" + +#include +#include +#include +#include +#include + +struct seg_info +{ + seg_info(const char* n, uint64_t o, uint64_t s) + : segName(n), offset(o), sizem(s) { } + const char* segName; + uint64_t offset; + uint64_t sizem; +}; + +class CStringEquals { +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; +typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> NameToSegments; + + +template +int optimize_linkedit(macho_header* mh, const void* mapped_cache, uint64_t* newSize) +{ + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + // update header flags + mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit + + // update load commands + uint64_t cumulativeFileSize = 0; + const 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; + macho_segment_command

* linkEditSegCmd = NULL; + macho_symtab_command

* symtab = NULL; + macho_dysymtab_command

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

::CMD ) { + // update segment/section file offsets + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + segCmd->set_fileoff(cumulativeFileSize); + 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(cumulativeFileSize+sect->addr()-segCmd->vmaddr()); + } + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + linkEditSegCmd = segCmd; + } + cumulativeFileSize += segCmd->filesize(); + } + else if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + // zero out all dyld info + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + 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); + } + else if ( cmd->cmd() == LC_SYMTAB ) { + symtab = (macho_symtab_command

*)cmd; + } + else if ( cmd->cmd() == LC_DYSYMTAB ) { + dynamicSymTab = (macho_dysymtab_command

*)cmd; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // rebuild symbol table + if ( linkEditSegCmd == NULL ) { + fprintf(stderr, "__LINKEDIT not found\n"); + return -1; + } + if ( symtab == NULL ) { + fprintf(stderr, "LC_SYMTAB not found\n"); + return -1; + } + if ( dynamicSymTab == NULL ) { + fprintf(stderr, "LC_DYSYMTAB not found\n"); + return -1; + } + // copy symbol entries and strings from original cache file to new mapped dylib file + const uint32_t newSymTabOffset = linkEditSegCmd->fileoff(); + const uint32_t newIndSymTabOffset = newSymTabOffset + symtab->nsyms()*sizeof(macho_nlist

); + const uint32_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t); + macho_nlist

* const newSymTabStart = (macho_nlist

*)(((uint8_t*)mh) + newSymTabOffset); + char* const newStringPoolStart = (char*)mh + newStringPoolOffset; + uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset); + const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff()); + const macho_nlist

* const mergedSymTabStart = (macho_nlist

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

* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()]; + const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff(); + macho_nlist

* t = newSymTabStart; + int poolOffset = 0; + newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string + for (const macho_nlist

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + *t = *s; + t->set_n_strx(poolOffset); + strcpy(&newStringPoolStart[poolOffset], &mergedStringPoolStart[s->n_strx()]); + poolOffset += (strlen(&newStringPoolStart[poolOffset]) + 1); + ++t; + } + // pointer align string pool size + while ( (poolOffset % sizeof(pint_t)) != 0 ) + ++poolOffset; + // copy indirect symbol table + memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t)); + + // update load commands + symtab->set_symoff(newSymTabOffset); + symtab->set_stroff(newStringPoolOffset); + symtab->set_strsize(poolOffset); + dynamicSymTab->set_extreloff(0); + dynamicSymTab->set_nextrel(0); + dynamicSymTab->set_locreloff(0); + dynamicSymTab->set_nlocrel(0); + dynamicSymTab->set_indirectsymoff(newIndSymTabOffset); + linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff()); + linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) ); + + // return new size + *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096); + + return 0; +} + + + +static void make_dirs(const char* file_path) +{ + //printf("make_dirs(%s)\n", file_path); + char dirs[strlen(file_path)+1]; + strcpy(dirs, file_path); + char* lastSlash = strrchr(dirs, '/'); + if ( lastSlash == NULL ) + return; + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(dirs, &stat_buf) != 0 ) { + const char* afterSlash = &dirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + //printf("mkdir(%s)\n", dirs); + *slash = '/'; + afterSlash = slash+1; + } + } +} + + + +template +size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, const std::vector& segments) { + typedef typename A::P P; + + size_t additionalSize = 0; + for(std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + additionalSize += it->sizem; + } + + dylib_data.reserve(dylib_data.size() + additionalSize); + + uint32_t nfat_archs = 0; + uint32_t offsetInFatFile = 4096; + uint8_t *base_ptr = &dylib_data.front(); + +#define FH reinterpret_cast(base_ptr) +#define FA reinterpret_cast(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch))) + + if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) { + // have fat header, append new arch to end + nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch); + offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size); + } + + dylib_data.resize(offsetInFatFile); + base_ptr = &dylib_data.front(); + + FH->magic = OSSwapHostToBigInt32(FAT_MAGIC); + FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs); + + FA->cputype = 0; // filled in later + FA->cpusubtype = 0; // filled in later + FA->offset = OSSwapHostToBigInt32(offsetInFatFile); + FA->size = 0; // filled in later + FA->align = OSSwapHostToBigInt32(12); + + // Write regular segments into the buffer + uint32_t totalSize = 0; + + for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + + if(strcmp(it->segName, "__TEXT") == 0 ) { + const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+it->offset); + FA->cputype = OSSwapHostToBigInt32(textMH->cputype()); + FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype()); + + // if this cputype/subtype already exist in fat header, then return immediately + for(uint32_t i=0; i < nfat_archs-1; ++i) { + fat_arch *afa = reinterpret_cast(base_ptr+8)+i; + + if( afa->cputype == FA->cputype + && afa->cpusubtype == FA->cpusubtype) { + fprintf(stderr, "arch already exists in fat dylib\n"); + dylib_data.resize(offsetInFatFile); + return offsetInFatFile; + } + } + } + + //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem); + std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data)); + base_ptr = &dylib_data.front(); + totalSize += it->sizem; + } + + FA->size = OSSwapHostToBigInt32(totalSize); + + // optimize linkedit + uint64_t newSize = dylib_data.size(); + optimize_linkedit(((macho_header

*)(base_ptr+offsetInFatFile)), mapped_cache, &newSize); + + // update fat header with new file size + dylib_data.resize(offsetInFatFile+newSize); + base_ptr = &dylib_data.front(); + FA->size = OSSwapHostToBigInt32(newSize); +#undef FH +#undef FA + return offsetInFatFile; +} + + +extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)) +{ + struct stat statbuf; + if (stat(shared_cache_file_path, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path); + return -1; + } + + int cache_fd = open(shared_cache_file_path, O_RDONLY); + if (cache_fd < 0) { + fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path); + return -1; + } + + void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (mapped_cache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno); + return -1; + } + + close(cache_fd); + + // instantiate arch specific dylib maker + size_t (*dylib_create_func)(const void*, std::vector&, const std::vector&) = NULL; + if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 ppc") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) + dylib_create_func = dylib_maker; + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + munmap(mapped_cache, statbuf.st_size); + return -1; + } + + // iterate through all images in cache and build map of dylibs and segments + __block NameToSegments map; + dyld_shared_cache_iterate_segments_with_slide(mapped_cache, + ^(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem, + uint64_t mappedddress, uint64_t slide) { + map[dylib].push_back(seg_info(segName, offset, sizem)); + }); + + // for each dylib instantiate a dylib file + dispatch_group_t group = dispatch_group_create(); + dispatch_semaphore_t sema = dispatch_semaphore_create(4); + dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); + dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0); + + __block int cumulativeResult = 0; + __block unsigned count = 0; + + for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) { + dispatch_group_async(group, process_queue, ^{ + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + char dylib_path[PATH_MAX]; + strcpy(dylib_path, extraction_root_path); + strcat(dylib_path, "/"); + strcat(dylib_path, it->first); + + //printf("%s with %lu segments\n", dylib_path, segments.size()); + // make sure all directories in this path exist + make_dirs(dylib_path); + + // open file, create if does not already exist + int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644); + if ( fd == -1 ) { + fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno); + cumulativeResult = -1; + return; + } + + struct stat statbuf; + if (fstat(fd, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno); + close(fd); + cumulativeResult = -1; + return; + } + + std::vector *vec = new std::vector(statbuf.st_size); + if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) { + fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno); + close(fd); + cumulativeResult = -1; + return; + } + + const size_t offset = dylib_create_func(mapped_cache, *vec, it->second); + + dispatch_group_async(group, writer_queue, ^{ + progress(count++, map.size()); + + if(offset != vec->size()) { + //Write out the first page, and everything after offset + if( pwrite(fd, &vec->front(), 4096, 0) == -1 + || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) { + fprintf(stderr, "error writing, errnor=%d\n", errno); + cumulativeResult = -1; + } + } + + delete vec; + close(fd); + dispatch_semaphore_signal(sema); + }); + }); + } + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + dispatch_release(group); + dispatch_release(writer_queue); + + munmap(mapped_cache, statbuf.st_size); + return cumulativeResult; +} + + + +int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path) +{ + return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path, + ^(unsigned , unsigned) {} ); +} + + +#if 0 +int main(int argc, const char* argv[]) +{ + if ( argc != 3 ) { + fprintf(stderr, "usage: dsc_extractor \n"); + return 1; + } + + int result = dyld_shared_cache_extract_dylibs_progress(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } ); + fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result); + return 0; +} +#endif + + + diff --git a/launch-cache/dsc_extractor.h b/launch-cache/dsc_extractor.h new file mode 100644 index 0000000..a8620d0 --- /dev/null +++ b/launch-cache/dsc_extractor.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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@ + */ + +#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_ +#define _DYLD_SHARED_CACHE_EXTRACTOR_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path); +extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +#ifdef __cplusplus +} +#endif + +#endif // _DYLD_SHARED_CACHE_EXTRACTOR_ + diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp index 5110cbc..9b5cac4 100644 --- a/launch-cache/dsc_iterator.cpp +++ b/launch-cache/dsc_iterator.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,9 @@ */ #include +#include +#include + #include "dsc_iterator.h" #include "dyld_cache_format.h" @@ -32,8 +35,10 @@ #include "CacheFileAbstraction.hpp" + 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, uint64_t addr) @@ -50,7 +55,7 @@ namespace dyld { // call the callback block on each segment in this image template - void walkSegments(const uint8_t* cache, const char* dylibPath, const uint8_t* machHeader, dyld_shared_cache_iterator_t callback) + void walkSegments(const uint8_t* cache, const char* dylibPath, const uint8_t* machHeader, uint64_t slide, dyld_shared_cache_iterator_slide_t callback) { typedef typename A::P P; typedef typename A::P::E E; @@ -61,26 +66,47 @@ namespace dyld { for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd() == macho_segment_command

::CMD ) { macho_segment_command

* segCmd = (macho_segment_command

*)cmd; - const uint8_t* segStartInCache = mappedAddress(cache, segCmd->vmaddr()); - uint64_t fileOffset = segStartInCache - cache; - callback(dylibPath, segCmd->segname(), fileOffset, segCmd->vmsize()); + 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 + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + if ( mappings[2].file_offset() <= fileOffset ) { + if ( sizem > mappings[2].size() ) + sizem = mappings[2].file_offset() + mappings[2].size() - fileOffset; + } + } + callback(dylibPath, segCmd->segname(), fileOffset, sizem, segCmd->vmaddr()+slide, slide); } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } } - + // call walkSegments on each image in the cache template - int walkImages(const uint8_t* cache, dyld_shared_cache_iterator_t callback) + int walkImages(const uint8_t* cache, dyld_shared_cache_iterator_slide_t callback) { - typedef typename A::P::E E; + typedef typename A::P::E E; + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; const dyldCacheHeader* header = (dyldCacheHeader*)cache; + uint64_t slide = 0; + if ( header->mappingOffset() >= 0x48 ) { + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + uint64_t storedPointerToHeader = P::getP(*((pint_t*)&cache[mappings[1].file_offset()])); + slide = storedPointerToHeader - mappings[0].address(); + } const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; for (uint32_t i=0; i < header->imagesCount(); ++i) { const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); const uint8_t* machHeader = mappedAddress(cache, dylibs[i].address()); - walkSegments(cache, dylibPath, machHeader, callback); + walkSegments(cache, dylibPath, machHeader, slide, callback); } return 0; } @@ -92,7 +118,7 @@ namespace dyld { // 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. -int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) +int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) { const uint8_t* cache = (uint8_t*)shared_cache_file; if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) @@ -101,7 +127,42 @@ int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_share return dyld::walkImages(cache, callback); else if ( strcmp((char*)cache, "dyld_v1 ppc") == 0 ) return dyld::walkImages(cache, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) + return dyld::walkImages(cache, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) + return dyld::walkImages(cache, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) + return dyld::walkImages(cache, callback); else return -1; } +// 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/dsc_iterator.h b/launch-cache/dsc_iterator.h index 418eece..981386f 100644 --- a/launch-cache/dsc_iterator.h +++ b/launch-cache/dsc_iterator.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,15 +28,22 @@ extern "C" { #endif - typedef void (^dyld_shared_cache_iterator_t)(const char* dylib, const char* segName, uint64_t offset, uint64_t size); - // 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_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback); +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); + +// 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_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback); +extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback); +extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData); +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); + - #ifdef __cplusplus } #endif diff --git a/launch-cache/dsc_slider.cpp b/launch-cache/dsc_slider.cpp new file mode 100644 index 0000000..d11f46a --- /dev/null +++ b/launch-cache/dsc_slider.cpp @@ -0,0 +1,308 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 +#include +#include +#include +#include +#include +#include + +#define NO_ULEB 1 +#include "dyld_cache_format.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + +#include "dsc_slider.h" + +int update_dyld_shared_cache_load_address(const char* path, void (*logProc)(const char* format, ...) ) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + (*logProc)("open(%s) failed, errno=%d\n", path, errno); + return -1; + } + (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content + + uint8_t buffer[4096]; + if ( read(fd, buffer, 4096) != 4096 ) { + (*logProc)("read(%s) failed, errno=%d\n", path, errno); + close(fd); + return -1; + } + bool has64BitPointers = false; + bool isBigEndian = false; + uint64_t startReadOnlySharedRegion = 0; + uint64_t endReadOnlySharedRegion = 0; + uint64_t startReadWriteSharedRegion = 0; + uint64_t endReadWriteSharedRegion = 0; + if ( strcmp((char*)buffer, "dyld_v1 i386") == 0 ) { + has64BitPointers = false; + isBigEndian = false; + } + else if ( strcmp((char*)buffer, "dyld_v1 x86_64") == 0 ) { + has64BitPointers = true; + isBigEndian = false; + startReadOnlySharedRegion = 0x7FFF80000000LL; + endReadOnlySharedRegion = 0x7FFFC0000000LL; + startReadWriteSharedRegion = 0x7FFF70000000LL; + endReadWriteSharedRegion = 0x7FFF80000000LL; + } + else if ( strcmp((char*)buffer, "dyld_v1 ppc") == 0 ) { + has64BitPointers = false; + isBigEndian = true; + } + else if ( strcmp((char*)buffer, "dyld_v1 armv5") == 0 ) { + has64BitPointers = false; + isBigEndian = false; + startReadOnlySharedRegion = 0x30000000LL; + endReadOnlySharedRegion = 0x3E000000LL; + startReadWriteSharedRegion = 0x3E000000LL; + endReadWriteSharedRegion = 0x40000000LL; + } + else if ( strcmp((char*)buffer, "dyld_v1 armv6") == 0 ) { + has64BitPointers = false; + isBigEndian = false; + startReadOnlySharedRegion = 0x30000000LL; + endReadOnlySharedRegion = 0x3E000000LL; + startReadWriteSharedRegion = 0x3E000000LL; + endReadWriteSharedRegion = 0x40000000LL; + } + else if ( strcmp((char*)buffer, "dyld_v1 armv7") == 0 ) { + has64BitPointers = false; + isBigEndian = false; + startReadOnlySharedRegion = 0x30000000LL; + endReadOnlySharedRegion = 0x3E000000LL; + startReadWriteSharedRegion = 0x3E000000LL; + endReadWriteSharedRegion = 0x40000000LL; + } + else { + (*logProc)("file %s is not a known dyld shared cache file\n", path); + close(fd); + return -1; + } +#if __BIG_ENDIAN__ + bool swap = !isBigEndian; +#else + bool swap = isBigEndian; +#endif + + const dyld_cache_header* header = (dyld_cache_header*)buffer; + uint32_t mappingOffset = swap ? OSSwapInt32(header->mappingOffset) : header->mappingOffset; + if ( mappingOffset < 0x48 ) { + (*logProc)("dyld shared cache file %s is old format\n", path); + close(fd); + return -1; + } + + uint32_t mappingCount = swap ? OSSwapInt32(header->mappingCount) : header->mappingCount; + if ( mappingCount != 3 ) { + (*logProc)("dyld shared cache file %s has wrong mapping count\n", path); + close(fd); + return -1; + } + + uint64_t slidePointersOffset = swap ? OSSwapInt64(header->slidePointersOffset) : header->slidePointersOffset; + uint64_t slidePointersSize = swap ? OSSwapInt64(header->slidePointersSize) : header->slidePointersSize; + if ( (slidePointersOffset == 0) || (slidePointersSize == 0) ) { + (*logProc)("dyld shared cache file %s is missing slide information\n", path); + close(fd); + return -1; + } + + // read slide info + void* slideInfo = malloc(slidePointersSize); + if ( slideInfo == NULL ) { + (*logProc)("malloc(%llu) failed\n", slidePointersSize); + close(fd); + return -1; + } + int64_t amountRead = pread(fd, slideInfo, slidePointersSize, slidePointersOffset); + if ( amountRead != (int64_t)slidePointersSize ) { + (*logProc)("slide info pread(fd, buf, %llu, %llu) failed\n", slidePointersSize, slidePointersOffset); + close(fd); + return -1; + } + + // read all DATA + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)&buffer[mappingOffset]; + uint64_t dataFileOffset = swap ? OSSwapInt64(mappings[1].fileOffset) : mappings[1].fileOffset; + uint64_t dataSize = swap ? OSSwapInt64(mappings[1].size) : mappings[1].size; + uint8_t* data = (uint8_t*)malloc(dataSize); + amountRead = pread(fd, data, dataSize, dataFileOffset); + if ( amountRead != (int64_t)dataSize ) { + (*logProc)("data pread(fd, buf, %llu, %llu) failed\n", data, dataSize); + close(fd); + return -1; + } + + // close read-only file + close(fd); + + // extract current slide + uint64_t headerDataUses; + if ( has64BitPointers ) { + headerDataUses = *((uint64_t*)data); + if ( swap ) + headerDataUses = OSSwapInt64(headerDataUses); + } + else { + uint32_t temp = *((uint32_t*)data); + if ( swap ) + temp = OSSwapInt32(temp); + headerDataUses = temp; + } + uint64_t textAddress = swap ? OSSwapInt64(mappings[0].address) : mappings[0].address; + uint32_t currentSlide = headerDataUses - textAddress; + (*logProc)("currentSlide=0x%08X\n", currentSlide); + + // find read-only space + uint64_t linkeditAddress = swap ? OSSwapInt64(mappings[2].address) : mappings[2].address; + uint64_t linkeditSize = swap ? OSSwapInt64(mappings[2].size) : mappings[2].size; + uint64_t linkeditEnd = linkeditAddress + linkeditSize; + uint64_t roSpace = endReadOnlySharedRegion - linkeditEnd; + (*logProc)("ro space=0x%08llX\n", roSpace); + + // find read-write space + uint64_t dataAddress = swap ? OSSwapInt64(mappings[1].address) : mappings[1].address; + uint64_t dataEnd = dataAddress + dataSize; + uint64_t rwSpace = endReadWriteSharedRegion - dataEnd; + (*logProc)("rw space=0x%08llX\n", rwSpace); + + // choose new random slide + uint32_t slideSpace = rwSpace; + if ( roSpace < rwSpace ) + slideSpace = roSpace; + uint32_t newSlide = (arc4random() % slideSpace) & (-4096); + (*logProc)("newSlide=0x%08X\n", newSlide); + int32_t slideAdjustment = newSlide - currentSlide; + + // update DATA with slide info + uint64_t offsetInCacheFile = 0; + const uint8_t* infoStart = (uint8_t*)slideInfo; + const uint8_t* infoEnd = infoStart + slidePointersSize; + for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd); ) { + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + offsetInCacheFile += delta; + // verify in DATA range + if ( (offsetInCacheFile < dataFileOffset) || (offsetInCacheFile > (dataFileOffset+dataSize)) ) { + (*logProc)("pointer offset 0x%llX outside DATA range\n", offsetInCacheFile); + return -1; + } + uint32_t offsetInData = offsetInCacheFile - dataFileOffset; + if ( has64BitPointers ) { + uint64_t value64 = *((uint64_t*)(&data[offsetInData])); + if ( swap ) + value64 = OSSwapInt64(value64); + value64 += slideAdjustment; + if ( swap ) + value64 = OSSwapInt64(value64); + *((uint64_t*)(&data[offsetInData])) = value64; + } + else { + uint64_t value32 = *((uint32_t*)(&data[offsetInData])); + if ( swap ) + value32 = OSSwapInt32(value32); + value32 += slideAdjustment; + if ( swap ) + value32 = OSSwapInt32(value32); + *((uint32_t*)(&data[offsetInData])) = value32; + } + //(*logProc)("update pointer at offset 0x%08X", offsetInData); + more = false; + } + } while (more); + } + free(slideInfo); + slideInfo = NULL; + + // re-open cache file and overwrite DATA range + fd = open(path, O_RDWR, 0); + if ( fd == -1 ) { + (*logProc)("open(%s) failed, errno=%d\n", path, errno); + return -1; + } + (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content + uint64_t amountWrote = pwrite(fd, data, dataSize, dataFileOffset); + if ( amountWrote != dataSize ) { + (*logProc)("data pwrite(fd, buf, %llu, %llu) failed\n", data, dataSize); + close(fd); + return -1; + } + close(fd); + + // re-open and re-read to verify write + fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + (*logProc)("verify open(%s) failed, errno=%d\n", path, errno); + return -1; + } + (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content + uint8_t* dataVerify = (uint8_t*)malloc(dataSize); + amountRead = pread(fd, dataVerify, dataSize, dataFileOffset); + if ( amountRead != (int64_t)dataSize ) { + (*logProc)("verify data pread(fd, buf, %llu, %llu) failed\n", data, dataSize); + close(fd); + return -1; + } + close(fd); + if ( memcmp(data, dataVerify, dataSize) != 0 ) { + (*logProc)("data update verification failed\n"); + return -1; + } + free(data); + free(dataVerify); + + + // success + return 0; +} + + +#if 0 +static void logger(const char* format, ...) +{ + va_list list; + va_start(list, format); + fprintf(stderr, "error: "); + vfprintf(stderr, format, list); +} + + +int main(int argc, const char* argv[]) +{ + return update_dyld_shared_cache_load_address(argv[1], logger); +} +#endif + + diff --git a/launch-cache/dsc_slider.h b/launch-cache/dsc_slider.h new file mode 100644 index 0000000..4979562 --- /dev/null +++ b/launch-cache/dsc_slider.h @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +// +// This function is called with a path to a dyld shared cache file. It will update the shared cache file +// in place. The update randomizes the load address when the shared cache file is later used by dyld. +// +// On success, the return value is zero. +// On failure the return value is non-zero and an explanation error was written to the logProc callback. +// +extern int update_dyld_shared_cache_load_address(const char* path, void (*logProc)(const char* format, ...) ); + + +#ifdef __cplusplus +} +#endif diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h index 748fff5..42a1a13 100644 --- a/launch-cache/dyld_cache_format.h +++ b/launch-cache/dyld_cache_format.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2008 Apple Inc. All rights reserved. + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,14 +29,26 @@ #include - struct dyld_cache_header +struct dyld_cache_header { char magic[16]; // e.g. "dyld_v0 ppc" - uint32_t mappingOffset; // file offset to first shared_file_mapping_np - uint32_t mappingCount; // number of shared_file_mapping_np entries + 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 +}; + +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 @@ -48,7 +60,22 @@ struct dyld_cache_image_info uint32_t pad; }; -#define DYLD_SHARED_CACHE_DIR "/var/db/dyld/" +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]; +}; + + + +#define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/" +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" #define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp new file mode 100644 index 0000000..d82674a --- /dev/null +++ b/launch-cache/dyld_shared_cache_util.cpp @@ -0,0 +1,492 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dsc_iterator.h" +#include "dyld_cache_format.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + + +#define OP_NULL 0 +#define OP_LIST_DEPENDENCIES 1 +#define OP_LIST_DYLIBS 2 +#define OP_LIST_LINKEDIT 3 + +#define UUID_BYTES 16 + +// Define this here so we can work with or without block support +typedef void (*segment_callback_t)(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem, + uint64_t mappedddress, uint64_t slide, void* userData); + +struct seg_callback_args { + char *target_path; + uint32_t target_found; + void *mapped_cache; + uint32_t op; + uint8_t print_uuids; + uint8_t print_vmaddrs; + uint8_t print_dylib_versions; +}; + +void usage() { + fprintf(stderr, "Usage: dscutil -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit [ shared-cache-file ]\n"); +} + +/* + * Get the path to the native shared cache for this host + */ +static const char* default_shared_cache_path() { +#if __i386__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386"; +#elif __x86_64__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; +#elif __ppc__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "rosetta"; +#elif __ARM_ARCH_5TEJ__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5"; +#elif __ARM_ARCH_6K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6"; +#elif __ARM_ARCH_7A__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7"; +#else + #error unsupported architecture +#endif +} + +/* + * Get a vector of all the load commands from the header pointed to by headerAddr + */ +template +std::vector* > get_load_cmds(void *headerAddr) { + typedef typename A::P P; + typedef typename A::P::E E; + + std::vector* > cmd_vector; + + const macho_header

* mh = (const macho_header

*)headerAddr; + uint32_t ncmds = mh->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + + for (uint32_t i = 0; i < ncmds; i++) { + cmd_vector.push_back((macho_load_command

*)cmd); + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return cmd_vector; +} + +/* + * List dependencies from the mach-o header at headerAddr + * in the same format as 'otool -L' + */ +template +void list_dependencies(const char *dylib, void *headerAddr, uint8_t print_dylib_versions) { + typedef typename A::P P; + typedef typename A::P::E E; + + std::vector< macho_load_command

* > cmds; + cmds = get_load_cmds(headerAddr); + for(typename std::vector*>::iterator it = cmds.begin(); it != cmds.end(); ++it) { + uint32_t cmdType = (*it)->cmd(); + if (cmdType == LC_LOAD_DYLIB || + cmdType == LC_ID_DYLIB || + cmdType == LC_LOAD_WEAK_DYLIB || + cmdType == LC_REEXPORT_DYLIB || + cmdType == LC_LOAD_UPWARD_DYLIB) { + macho_dylib_command

* dylib_cmd = (macho_dylib_command

*)*it; + const char *name = dylib_cmd->name(); + uint32_t compat_vers = dylib_cmd->compatibility_version(); + uint32_t current_vers = dylib_cmd->current_version(); + + if (print_dylib_versions) { + printf("\t%s (compatibility version %u.%u.%u, current version %u.%u.%u)\n", + name, + (compat_vers >> 16), + (compat_vers >> 8) & 0xff, + (compat_vers) & 0xff, + (current_vers >> 16), + (current_vers >> 8) & 0xff, + (current_vers) & 0xff); + } else { + printf("\t%s\n", name); + } + } + } +} + +/* + * Print out a dylib from the shared cache, optionally including the UUID + */ +template +void print_dylib(const char *dylib, void *headerAddr, uint64_t slide, struct seg_callback_args *args) { + typedef typename A::P P; + typedef typename A::P::E E; + char uuid_str[UUID_BYTES*3]; + uint8_t got_uuid = 0; + uint64_t vmaddr = 0; + + std::vector< macho_load_command

* > cmds; + cmds = get_load_cmds(headerAddr); + for(typename std::vector*>::iterator it = cmds.begin(); it != cmds.end(); ++it) { + uint32_t cmdType = (*it)->cmd(); + if (cmdType == LC_UUID) { + macho_uuid_command

* uuid_cmd = (macho_uuid_command

*)*it; + const uint8_t *uuid = uuid_cmd->uuid(); + sprintf(uuid_str, "<%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]); + got_uuid = 1; + } + else if (cmdType == LC_SEGMENT) { + macho_segment_command

* seg_cmd = (macho_segment_command

*)*it; + if (strcmp(seg_cmd->segname(), "__TEXT") == 0) { + vmaddr = seg_cmd->vmaddr(); + } + } + } + + if (args->print_vmaddrs) + printf("0x%08llX ", vmaddr+slide); + if (args->print_uuids) { + if (got_uuid) + printf("%s ", uuid_str); + else + printf("< no uuid in dylib > "); + } + printf("%s\n", dylib); +} + + +uint64_t sLinkeditBase = 0; +std::map sPageToContent; + + + + +static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, char* message) +{ + for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { + std::map::iterator pos = sPageToContent.find(p); + if ( pos == sPageToContent.end() ) { + sPageToContent[p] = strdup(message); + } + else { + char* oldMessage = pos->second; + char* newMesssage; + asprintf(&newMesssage, "%s, %s", oldMessage, message); + sPageToContent[p] = newMesssage; + free(oldMessage); + } + } +} + + +/* + * get LINKEDIT info for dylib + */ +template +void process_linkedit(const char* dylib, void* headerAddr) { + typedef typename A::P P; + typedef typename A::P::E E; + // filter out symlinks by only handling first path found for each mach header + static std::set seenImages; + if ( seenImages.count(headerAddr) != 0 ) + return; + seenImages.insert(headerAddr); + const macho_header

* mh = (const macho_header

*)headerAddr; + uint32_t ncmds = mh->ncmds(); + const macho_load_command

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < ncmds; i++) { + if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + char message[1000]; + const char* shortName = strrchr(dylib, '/') + 1; + // add export trie info + if ( dyldInfo->export_size() != 0 ) { + //printf("export_off=0x%X\n", dyldInfo->export_off()); + uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096); + uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096); + sprintf(message, "exports from %s", shortName); + add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message); + } + // add binding info + if ( dyldInfo->bind_size() != 0 ) { + uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096); + uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096); + sprintf(message, "bindings from %s", shortName); + add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message); + } + // add lazy binding info + if ( dyldInfo->lazy_bind_size() != 0 ) { + uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096); + uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096); + sprintf(message, "lazy bindings from %s", shortName); + add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message); + } + // add weak binding info + if ( dyldInfo->weak_bind_size() != 0 ) { + uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096); + uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096); + sprintf(message, "weak bindings from %s", shortName); + add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + + +/* + * This callback is used with dsc_iterator, and called once for each segment in the target shared cache + */ +template +void segment_callback(const char *dylib, const char *segName, uint64_t offset, uint64_t sizem, + uint64_t mappedAddress, uint64_t slide, void *userData) { + typedef typename A::P P; + typedef typename A::P::E E; + struct seg_callback_args *args = (struct seg_callback_args *)userData; + if (strncmp(segName, "__TEXT", 6) == 0) { + int target_match = args->target_path ? (strcmp(args->target_path, dylib) == 0) : 0; + if (!args->target_path || target_match) { + if (target_match) { + args->target_found = 1; + } + void *headerAddr = (void*)((long)args->mapped_cache + (long)offset); + switch (args->op) { + case OP_LIST_DEPENDENCIES: + list_dependencies(dylib, headerAddr, args->print_dylib_versions); + break; + case OP_LIST_DYLIBS: + print_dylib(dylib, headerAddr, slide, args); + break; + case OP_LIST_LINKEDIT: + process_linkedit(dylib, headerAddr); + default: + break; + } + } + } + else if (strncmp(segName, "__LINKEDIT", 6) == 0) { + sLinkeditBase = mappedAddress - offset; + } +} + + + + +int main (int argc, char **argv) { + struct seg_callback_args args; + const char *shared_cache_path = NULL; + void *mapped_cache; + struct stat statbuf; + int cache_fd; + char c; + bool print_slide_info = false; + + args.target_path = NULL; + args.op = OP_NULL; + args.print_uuids = 0; + args.print_vmaddrs = 0; + args.print_dylib_versions = 0; + args.target_found = 0; + + for (uint32_t optind = 1; optind < argc; optind++) { + char *opt = argv[optind]; + if (opt[0] == '-') { + if (strcmp(opt, "-list") == 0) { + if (args.op) { + fprintf(stderr, "Error: select one of -list or -dependents\n"); + usage(); + exit(1); + } + args.op = OP_LIST_DYLIBS; + } else if (strcmp(opt, "-dependents") == 0) { + if (args.op) { + fprintf(stderr, "Error: select one of -list or -dependents\n"); + usage(); + exit(1); + } + if (!(++optind < argc)) { + fprintf(stderr, "Error: option -depdendents requires an argument\n"); + usage(); + exit(1); + } + args.op = OP_LIST_DEPENDENCIES; + args.target_path = argv[optind]; + } else if (strcmp(opt, "-uuid") == 0) { + args.print_uuids = 1; + } else if (strcmp(opt, "-versions") == 0) { + args.print_dylib_versions = 1; + } else if (strcmp(opt, "-vmaddr") == 0) { + args.print_vmaddrs = 1; + } else if (strcmp(opt, "-linkedit") == 0) { + args.op = OP_LIST_LINKEDIT; + } else if (strcmp(opt, "-slide_info") == 0) { + print_slide_info = true; + } else { + fprintf(stderr, "Error: unrecognized option %s\n", opt); + usage(); + exit(1); + } + } else { + shared_cache_path = opt; + } + } + + if ( !print_slide_info ) { + if (args.op == OP_NULL) { + fprintf(stderr, "Error: select one of -list or -dependents\n"); + usage(); + exit(1); + } + + if (args.print_uuids && args.op != OP_LIST_DYLIBS) + fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); + if (args.print_vmaddrs && args.op != OP_LIST_DYLIBS) + fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); + if (args.print_dylib_versions && args.op != OP_LIST_DEPENDENCIES) + fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); + + if (args.op == OP_LIST_DEPENDENCIES && !args.target_path) { + fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); + usage(); + exit(1); + } + } + + if (!shared_cache_path) + shared_cache_path = default_shared_cache_path(); + + if (stat(shared_cache_path, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_path); + exit(1); + } + + cache_fd = open(shared_cache_path, O_RDONLY); + if (cache_fd < 0) { + fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_path); + exit(1); + } + mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (mapped_cache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_path, errno); + exit(1); + } + + if ( print_slide_info ) { + const dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; + if ( (strcmp(header->magic(), "dyld_v1 x86_64") != 0) + && (strcmp(header->magic(), "dyld_v1 armv6") != 0) + && (strcmp(header->magic(), "dyld_v1 armv7") != 0) ) { + fprintf(stderr, "Error: unrecognized dyld shared cache magic or arch does not support sliding\n"); + exit(1); + } + if ( header->slideInfoOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); + exit(1); + } + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)mapped_cache + header->mappingOffset()); + const dyldCacheFileMapping* dataMapping = &mappings[1]; + uint64_t dataStartAddress = dataMapping->address(); + uint64_t dataSize = dataMapping->size(); + const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)mapped_cache+header->slideInfoOffset()); + printf("slide info version=%d\n", slideInfoHeader->version()); + printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096); + const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset()); + for(int i=0; i < slideInfoHeader->toc_count(); ++i) { + printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i)); + const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)]; + for(int j=0; j < slideInfoHeader->entries_size(); ++j) + printf("%02X", entry->bits[j]); + printf("\n"); + } + + + } + else { + segment_callback_t callback; + if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) + callback = segment_callback; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) + callback = segment_callback; + else if ( strcmp((char*)mapped_cache, "dyld_v1 ppc") == 0 ) + callback = segment_callback; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) + callback = segment_callback; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) + callback = segment_callback; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) + callback = segment_callback; + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + exit(1); + } + + args.mapped_cache = mapped_cache; + + #if __BLOCKS__ + // Shim to allow building for the host + void *argsPtr = &args; + dyld_shared_cache_iterate_segments_with_slide(mapped_cache, + ^(const char* dylib, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide ) { + (callback)(dylib, segName, offset, size, mappedddress, slide, argsPtr); + }); + #else + dyld_shared_cache_iterate_segments_with_slide_nb(mapped_cache, callback, &args); + #endif + + if (args.op == OP_LIST_LINKEDIT) { + // dump -linkedit information + for (std::map::iterator it = sPageToContent.begin(); it != sPageToContent.end(); ++it) { + printf("0x%0llX %s\n", sLinkeditBase+it->first, it->second); + } + } + + + if (args.target_path && !args.target_found) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", args.target_path, shared_cache_path); + exit(1); + } + } + return 0; +} diff --git a/launch-cache/update_dyld_shared_cache.cpp b/launch-cache/update_dyld_shared_cache.cpp index 704aa81..c7a3874 100644 --- a/launch-cache/update_dyld_shared_cache.cpp +++ b/launch-cache/update_dyld_shared_cache.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * Copyright (c) 2006-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -44,8 +44,6 @@ #include #include #include -#include -#include #include "dyld_cache_format.h" @@ -63,9 +61,16 @@ #define SELOPT_WRITE #include +#define FIRST_DYLIB_TEXT_OFFSET 0x5000 +#define FIRST_DYLIB_DATA_OFFSET 0x1000 + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif static bool verbose = false; static bool progress = false; +static bool iPhoneOS = false; static std::vector warnings; @@ -95,6 +100,11 @@ static uint64_t pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } class ArchGraph { public: + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> StringToString; + static void addArchPair(ArchPair ap); static void addRoot(const char* vpath, const std::set& archs); static void findSharedDylibs(ArchPair ap); @@ -104,6 +114,7 @@ public: ArchPair getArchPair() { return fArchPair; } std::set& getSharedDylibs() { return fSharedDylibs; } + StringToString& getDylibAliases() { return fAliasesMap; } const char* archName() { return archName(fArchPair); } private: @@ -128,9 +139,6 @@ private: std::set fRootsDependentOnThis; }; - struct CStringEquals { - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; @@ -139,6 +147,7 @@ private: DependencyNode* getNode(const char* path); DependencyNode* getNodeForVirtualPath(const char* vpath); static bool canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set& possibleLibs, std::map& shareableMap); + static bool sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg); static std::map fgPerArchGraph; static const char* fgFileSystemRoot; @@ -148,6 +157,7 @@ private: std::set fRoots; PathToNode fNodes; std::set fSharedDylibs; // use set to avoid duplicates when installname!=realpath + StringToString fAliasesMap; }; std::map ArchGraph::fgPerArchGraph; const char* ArchGraph::fgFileSystemRoot = ""; @@ -187,17 +197,18 @@ void ArchGraph::addRoot(const char* vpath, const std::set& onlyArchs) for(std::set::iterator ait = onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { try { const MachOLayoutAbstraction* layout = uni.getSlice(*ait); - fgPerArchGraph[*ait]->addRoot(path, layout); + if ( layout != NULL ) + fgPerArchGraph[*ait]->addRoot(path, layout); } catch (const char* msg) { if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: warning for %s can't use root %s: %s\n", fgPerArchGraph[*ait]->archName(), path, msg); + fprintf(stderr, "update_dyld_shared_cache: warning for %s can't use root '%s': %s\n", fgPerArchGraph[*ait]->archName(), path, msg); } } } catch (const char* msg) { - fprintf(stderr, "update_dyld_shared_cache: warning can't use root %s: %s\n", path, msg); + fprintf(stderr, "update_dyld_shared_cache: warning can't use root '%s': %s\n", path, msg); } } @@ -218,6 +229,7 @@ void ArchGraph::addRoot(const char* path, const MachOLayoutAbstraction* layout) node->markNeededByRoot(NULL); } +// a virtual path does not have the fgFileSystemRoot prefix // a virtual path does not have the fgFileSystemRoot prefix ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath) { @@ -229,12 +241,32 @@ ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath) strcpy(completePath, fgFileSystemRoot); strcat(completePath, vpath); // assumes vpath starts with '/' if ( fgUsesOverlay ) { - // using -overlay means if /overlay/usr/lib exists use it, otherwise use original path + // using -overlay means if /overlay/path/dylib exists use it, otherwise use /path/dylib struct stat stat_buf; if ( stat(completePath, &stat_buf) == 0 ) return this->getNode(completePath); - else + else { + // support when install name is a symlink + if ( (lstat(vpath, &stat_buf) == 0) && S_ISLNK(stat_buf.st_mode) ) { + // requested path did not exist in /overlay, but leaf of path is a symlink in / + char pathInSymLink[MAXPATHLEN]; + size_t res = readlink(vpath, pathInSymLink, sizeof(pathInSymLink)); + if ( res != -1 ) { + pathInSymLink[res] = '\0'; + if ( pathInSymLink[0] != '/' ) { + char symFullPath[MAXPATHLEN]; + strcpy(symFullPath, vpath); + char* lastSlash = strrchr(symFullPath, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, pathInSymLink); + // (re)try looking for what symlink points to, but in /overlay + return this->getNodeForVirtualPath(symFullPath); + } + } + } + } return this->getNode(vpath); + } } else { // using -root means always use /rootpath/usr/lib @@ -257,8 +289,20 @@ ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) // look up real path to see if node already exists pos = fNodes.find(realPath); - if ( pos != fNodes.end() ) + if ( pos != fNodes.end() ) { + // update fAliasesMap with symlinks found + const char* aliasPath = path; + if ( (fgFileSystemRoot != NULL) && (strncmp(path, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) { + aliasPath = &path[strlen(fgFileSystemRoot)]; + } + if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) { + if ( strcmp(aliasPath, pos->second->getLayout()->getID().name) != 0 ) { + fAliasesMap[strdup(aliasPath)] = pos->second->getLayout()->getID().name; + //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]); + } + } return pos->second; + } // still does not exist, so create a new node const UniversalMachOLayout& uni = UniversalMachOLayout::find(realPath); @@ -270,8 +314,29 @@ ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) fNodes[node->getPath()] = node; // if install name is not real path, add install name to node map if ( (node->getLayout()->getFileType() == MH_DYLIB) && (strcmp(realPath, node->getLayout()->getID().name) != 0) ) { - //fprintf(stderr, "adding node alias 0x%08X %s for %s\n", fArch, node->getLayout()->getID().name, realPath); - fNodes[node->getLayout()->getID().name] = node; + //fprintf(stderr, "adding %s node alias %s for %s\n", archName(fArchPair), node->getLayout()->getID().name, realPath); + pos = fNodes.find(node->getLayout()->getID().name); + if ( pos != fNodes.end() ) { + // warn if two dylib in cache have same install_name + char* msg; + asprintf(&msg, "update_dyld_shared_cache: warning, found two dylibs with same install path: %s\n\t%s\n\t%s\n", + node->getLayout()->getID().name, pos->second->getPath(), node->getPath()); + fprintf(stderr, "%s", msg); + warnings.push_back(msg); + } + else + fNodes[node->getLayout()->getID().name] = node; + // update fAliasesMap with symlinks found + const char* aliasPath = realPath; + if ( (fgFileSystemRoot != NULL) && (strncmp(realPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) { + aliasPath = &realPath[strlen(fgFileSystemRoot)]; + } + if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) { + if ( strcmp(aliasPath, node->getLayout()->getID().name) != 0 ) { + fAliasesMap[strdup(aliasPath)] = node->getLayout()->getID().name; + //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]); + } + } } return node; } @@ -292,7 +357,13 @@ void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* m // expand @executable_path path prefix const char* executablePath = mainExecutableLayout->getFilePath(); char newPath[strlen(executablePath) + strlen(dependentPath)+2]; - strcpy(newPath, executablePath); + if ( (fgFileSystemRoot != NULL) && (strncmp(executablePath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) { + // executablePath already has rootPath prefix, need to remove that to get to base virtual path + strcpy(newPath, &executablePath[strlen(fgFileSystemRoot)]); + } + else { + strcpy(newPath, executablePath); + } char* addPoint = strrchr(newPath,'/'); if ( addPoint != NULL ) strcpy(&addPoint[1], &dependentPath[17]); @@ -303,7 +374,13 @@ void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* m else if ( strncmp(dependentPath, "@loader_path/", 13) == 0 ) { // expand @loader_path path prefix char newPath[strlen(fPath) + strlen(dependentPath)+2]; - strcpy(newPath, fPath); + if ( (fgFileSystemRoot != NULL) && (strncmp(fPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) { + // fPath already has rootPath prefix, need to remove that to get to base virtual path + strcpy(newPath, &fPath[strlen(fgFileSystemRoot)]); + } + else { + strcpy(newPath, fPath); + } char* addPoint = strrchr(newPath,'/'); if ( addPoint != NULL ) strcpy(&addPoint[1], &dependentPath[13]); @@ -314,7 +391,15 @@ void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* m else if ( strncmp(dependentPath, "@rpath/", 7) == 0 ) { throw "@rpath not supported in dyld shared cache"; } - fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath)); + // silently ignore dependents from main executables that can't be in shared cache + bool addDependent = true; + if ( fLayout->getFileType() == MH_EXECUTE ) { + if ( (strncmp(dependentPath, "/usr/lib/", 9) != 0) && (strncmp(dependentPath, "/System/Library/", 16) != 0) ) { + addDependent = false; + } + } + if ( addDependent ) + fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath)); } catch (const char* msg) { if ( it->weakImport && ! fLayout->hasSplitSegInfo() ) { @@ -355,15 +440,27 @@ void ArchGraph::findSharedDylibs(ArchPair ap) { const PathToNode& nodes = fgPerArchGraph[ap]->fNodes; std::set possibleLibs; - //fprintf(stderr, "shared for arch 0x%08X\n", arch); + //fprintf(stderr, "shared for arch %s\n", archName(ap)); for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { DependencyNode* node = it->second; // put all dylibs in shared cache - not just ones used by more than one app if ( node->allDependentsFound() /*&& (node->useCount() > 1)*/ ) { const MachOLayoutAbstraction* layout = node->getLayout(); - if ( layout->hasSplitSegInfo() && layout->isRootOwned() && layout->inSharableLocation() ) - possibleLibs.insert(layout); - //fprintf(stderr, "\t%s\n", it->first); + if ( layout->isDylib() ) { + char* msg; + if ( sharable(layout, ap, &msg) ) { + possibleLibs.insert(layout); + } + else { + if ( layout->getID().name[0] == '@' ) { + // update_dyld_shared_cache should suppress warnings for embedded frameworks + } + else { + warnings.push_back(msg); + fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg); + } + } + } } } @@ -405,6 +502,27 @@ const char* ArchGraph::archName(ArchPair ap) } } +bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg) +{ + if ( ! layout->isTwoLevelNamespace() ) + asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name); + else if ( ! layout->hasSplitSegInfo() ) + asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5")); + else if ( ! layout->isRootOwned() ) + asprintf(msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name); + else if ( ! layout->inSharableLocation() ) + asprintf(msg, "can't put %s in shared cache because it is not in /usr/lib or /System/Library", layout->getID().name); + else if ( layout->hasDynamicLookupLinkage() ) + asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name); + else if ( layout->hasMainExecutableLookupLinkage() ) + asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name); + //else if ( ! layout->hasDyldInfo() ) + // asprintf(msg, "can't put %s in shared cache because it was built for older OS", layout->getID().name); + else + return true; + return false; +} + bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set& possibleLibs, std::map& shareableMap) { // check map which is a cache of results @@ -416,14 +534,8 @@ bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, c if ( possibleLibs.count(layout) == 0 ) { shareableMap[layout] = false; char* msg; - if ( ! layout->hasSplitSegInfo() ) - asprintf(&msg, "can't put %s in shared cache because it was not built for 10.5 or later", layout->getID().name); - else if ( ! layout->isRootOwned() ) - asprintf(&msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name); - else if ( ! layout->inSharableLocation() ) - asprintf(&msg, "can't put %s in shared cache because it is not in /usr/lib or /System/Library", layout->getID().name); - else - asprintf(&msg, "can't put %s in shared cache", layout->getID().name); + if ( sharable(layout, ap, &msg) ) + asprintf(&msg, "can't put %s in shared cache, unknown reason", layout->getID().name); warnings.push_back(msg); if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg); @@ -470,22 +582,28 @@ template class SharedCache { public: - SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress); + SharedCache(ArchGraph* graph, const char* rootPath, const char* cacheDir, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress); bool update(bool usesOverlay, bool force, bool optimize, bool deleteExistingFirst, int archIndex, int archCount, bool keepSignatures); static const char* cacheFileSuffix(bool optimized, const char* archName); - uint64_t mappedCacheAddressForAddress(uint64_t addr); + // vm address = address AS WRITTEN into the cache + // mapped address = address AS MAPPED into the update process only + // file offset = offset relative to start of cache file + void * mappedAddressForVMAddress(uint64_t vmaddr); + uint64_t VMAddressForMappedAddress(const void *mapaddr); + uint64_t cacheFileOffsetForVMAddress(uint64_t addr) const; + uint64_t VMAddressForCacheFileOffset(uint64_t addr) const; private: typedef typename A::P P; typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; - bool notUpToDate(const char* path); - bool notUpToDate(const void* cache); + bool notUpToDate(const char* path, unsigned int aliasCount); + bool notUpToDate(const void* cache, unsigned int aliasCount); uint8_t* optimizeLINKEDIT(bool keepSignatures); - void optimizeObjC(); + void optimizeObjC(std::vector& pointersInData); static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable); static cpu_type_t arch(); @@ -495,13 +613,13 @@ private: static uint64_t sharedRegionReadOnlySize(); static uint64_t sharedRegionWritableSize(); static uint64_t getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide); + static bool addCacheSlideInfo(); - - void assignNewBaseAddresses(); - uint64_t cacheFileOffsetForAddress(uint64_t addr); + void assignNewBaseAddresses(bool verify); struct LayoutInfo { const MachOLayoutAbstraction* layout; + std::vector aliases; dyld_cache_image_info info; }; @@ -520,8 +638,8 @@ private: // one image has no segments return segs_l.size() > segs_r.size(); } - const macho_header

*mh_l = (const macho_header

*)segs_l[0].mappedAddress(); - const macho_header

*mh_r = (const macho_header

*)segs_r[0].mappedAddress(); + const macho_header

*mh_l = (macho_header

*)segs_l[0].mappedAddress(); + const macho_header

*mh_r = (macho_header

*)segs_r[0].mappedAddress(); const macho_section

*cstring_l = mh_l->getSection("__TEXT", "__cstring"); const macho_section

*cstring_r = mh_r->getSection("__TEXT", "__cstring"); if (!cstring_l || !cstring_r) { @@ -542,13 +660,15 @@ private: std::map& fMap; }; - + ArchGraph* fArchGraph; const bool fVerify; bool fExistingIsNotUpToDate; + bool fCacheFileInFinalLocation; const char* fCacheFilePath; uint8_t* fExistingCacheForVerification; std::vector fDylibs; + std::vector fDylibAliases; std::vector fMappings; uint32_t fHeaderSize; uint8_t* fInMemoryCache; @@ -561,6 +681,15 @@ private: uint32_t fOffsetOfLazyBindInfoInCombinedLinkedit; uint32_t fOffsetOfExportInfoInCombinedLinkedit; uint32_t fOffsetOfOldSymbolTableInfoInCombinedLinkedit; + uint32_t fSizeOfOldSymbolTableInfoInCombinedLinkedit; + uint32_t fOffsetOfOldExternalRelocationsInCombinedLinkedit; + uint32_t fSizeOfOldExternalRelocationsInCombinedLinkedit; + uint32_t fOffsetOfOldIndirectSymbolsInCombinedLinkedit; + uint32_t fSizeOfOldIndirectSymbolsInCombinedLinkedit; + uint32_t fOffsetOfOldStringPoolInCombinedLinkedit; + uint32_t fSizeOfOldStringPoolInCombinedLinkedit; + uint32_t fOffsetOfFunctionStartsInCombinedLinkedit; + uint32_t fSizeOfFunctionStartsInCombinedLinkedit; uint32_t fLinkEditsTotalOptimizedSize; }; @@ -572,17 +701,17 @@ class PointerSection typedef typename A::P P; typedef typename A::P::uint_t pint_t; - SharedCache* const fCache; - const macho_section

* const fSection; - pint_t * const fBase; - uint64_t const fCount; + SharedCache* const fCache; + const macho_section

* const fSection; + pint_t * const fBase; + uint64_t fCount; public: PointerSection(SharedCache* cache, const macho_header

* header, const char *segname, const char *sectname) : fCache(cache) , fSection(header->getSection(segname, sectname)) - , fBase(fSection ? (pint_t *)cache->mappedCacheAddressForAddress(fSection->addr()) : 0) + , fBase(fSection ? (pint_t *)cache->mappedAddressForVMAddress(fSection->addr()) : 0) , fCount(fSection ? fSection->size() / sizeof(pint_t) : 0) { } @@ -595,13 +724,27 @@ public: } T get(uint64_t index) const { - return (T)fCache->mappedCacheAddressForAddress(getUnmapped(index)); + return (T)fCache->mappedAddressForVMAddress(getUnmapped(index)); } void set(uint64_t index, uint64_t value) { if (index >= fCount) throwf("index out of range"); P::setP(fBase[index], value); } + + void removeNulls() { + uint64_t shift = 0; + for (uint64_t i = 0; i < fCount; i++) { + pint_t value = fBase[i]; + if (value) { + fBase[i-shift] = value; + } else { + shift++; + } + } + fCount -= shift; + const_cast*>(fSection)->set_size(fCount * sizeof(pint_t)); + } }; // Access a section containing an array of structures @@ -620,7 +763,7 @@ public: const char *segname, const char *sectname) : fCache(cache) , fSection(header->getSection(segname, sectname)) - , fBase(fSection ? (T *)cache->mappedCacheAddressForAddress(fSection->addr()) : 0) + , fBase(fSection ? (T *)cache->mappedAddressForVMAddress(fSection->addr()) : 0) , fCount(fSection ? fSection->size() / sizeof(T) : 0) { } @@ -651,19 +794,19 @@ template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x30000000; } template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xA0000000; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xA0000000; } +template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xAC000000; } template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x7FFF70000000LL; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x38000000; } +template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x3E000000; } template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x7FE00000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x08000000; } +template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x1C000000; } +template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x40000000; } +template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x0E000000; } template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x20000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x08000000; } +template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x04000000; } +template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x10000000; } +template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x02000000; } template <> const char* SharedCache::archName() { return "ppc"; } @@ -677,15 +820,25 @@ template <> const char* SharedCache::cacheFileSuffix(bool, const char* template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } template -SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress) - : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true), fCacheFilePath(NULL), - fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress) +SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* cacheDir, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress) + : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true), + fCacheFileInFinalLocation(rootPath[0] == '\0'), fCacheFilePath(NULL), + fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress), + fOffsetOfBindInfoInCombinedLinkedit(0), fOffsetOfWeakBindInfoInCombinedLinkedit(0), + fOffsetOfLazyBindInfoInCombinedLinkedit(0), fOffsetOfExportInfoInCombinedLinkedit(0), + fOffsetOfOldSymbolTableInfoInCombinedLinkedit(0), fSizeOfOldSymbolTableInfoInCombinedLinkedit(0), + fOffsetOfOldExternalRelocationsInCombinedLinkedit(0), fSizeOfOldExternalRelocationsInCombinedLinkedit(0), + fOffsetOfOldIndirectSymbolsInCombinedLinkedit(0), fSizeOfOldIndirectSymbolsInCombinedLinkedit(0), + fOffsetOfOldStringPoolInCombinedLinkedit(0), fSizeOfOldStringPoolInCombinedLinkedit(0), + fOffsetOfFunctionStartsInCombinedLinkedit(0), fSizeOfFunctionStartsInCombinedLinkedit(0) { if ( fArchGraph->getArchPair().arch != arch() ) throwf("SharedCache object is wrong architecture: 0x%08X vs 0x%08X", fArchGraph->getArchPair().arch, arch()); // build vector of all shared dylibs + unsigned int aliasCount = 0; std::set& dylibs = fArchGraph->getSharedDylibs(); + ArchGraph::StringToString& aliases = fArchGraph->getDylibAliases(); for(std::set::iterator it = dylibs.begin(); it != dylibs.end(); ++it) { const MachOLayoutAbstraction* lib = *it; LayoutInfo temp; @@ -693,26 +846,44 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSo temp.info.address = 0; temp.info.modTime = lib->getLastModTime(); temp.info.inode = lib->getInode(); - temp.info.pathFileOffset = lib->getNameFileOffset(); + temp.info.pathFileOffset = lib->getNameFileOffset(); // for now this is the offset within the dylib + for(ArchGraph::StringToString::iterator ait = aliases.begin(); ait != aliases.end(); ++ait) { + if ( strcmp(ait->second, lib->getID().name) == 0 ) { + temp.aliases.push_back(ait->first); + ++aliasCount; + } + } fDylibs.push_back(temp); } - // examine the existing shared cache file + // create path to cache file + char cachePathNonOverlay[1024]; + strcpy(cachePathNonOverlay, cacheDir); + if ( cachePathNonOverlay[strlen(cachePathNonOverlay)-1] != '/' ) + strcat(cachePathNonOverlay, "/"); + strcat(cachePathNonOverlay, DYLD_SHARED_CACHE_BASE_NAME); + strcat(cachePathNonOverlay, cacheFileSuffix(optimize, fArchGraph->archName())); char cachePath[1024]; strcpy(cachePath, rootPath); - strcat(cachePath, DYLD_SHARED_CACHE_DIR); - strcat(cachePath, DYLD_SHARED_CACHE_BASE_NAME); - strcat(cachePath, cacheFileSuffix(optimize, fArchGraph->archName())); - fCacheFilePath = strdup(cachePath); - const char* pathToExistingCacheFile = fCacheFilePath; - char cachePathNonOverlay[1024]; + strcat(cachePath, "/"); + strcat(cachePath, cachePathNonOverlay); + if ( !overlay && (rootPath[0] != '\0') ) + fCacheFilePath = strdup(cachePathNonOverlay); + else + fCacheFilePath = strdup(cachePath); if ( overlay ) { - strcpy(cachePathNonOverlay, DYLD_SHARED_CACHE_DIR); - strcat(cachePathNonOverlay, DYLD_SHARED_CACHE_BASE_NAME); - strcat(cachePathNonOverlay, cacheFileSuffix(optimize, fArchGraph->archName())); - pathToExistingCacheFile = cachePathNonOverlay; + // in overlay mode if there already is a cache file in the overlay + // check if it is up to date. If there is no file, check if + // the one in the boot volume is up to date. + struct stat stat_buf; + if ( stat(fCacheFilePath, &stat_buf) == 0 ) + fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount); + else + fExistingIsNotUpToDate = this->notUpToDate(cachePathNonOverlay, aliasCount); + } + else { + fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount); } - fExistingIsNotUpToDate = this->notUpToDate(pathToExistingCacheFile); // sort shared dylibs if ( verify ) { @@ -730,7 +901,30 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSo } // assign segments in each dylib a new address - this->assignNewBaseAddresses(); + this->assignNewBaseAddresses(verify); + + // calculate where string pool offset will start + // calculate cache file header size + fHeaderSize = sizeof(dyld_cache_header) + + fMappings.size()*sizeof(shared_file_mapping_np) + + (fDylibs.size()+aliasCount)*sizeof(dyld_cache_image_info); + //fprintf(stderr, "aliasCount=%d, fHeaderSize=0x%08X\n", aliasCount, fHeaderSize); + // build list of aliases and compute where each ones path string will go + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + for(std::vector::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait) { + LayoutInfo temp = *it; + // alias looks just like real dylib, but has a different name string + const char* aliasPath = *ait; + temp.aliases.clear(); + temp.aliases.push_back(aliasPath); + temp.info.pathFileOffset = fHeaderSize; + fDylibAliases.push_back(temp); + fHeaderSize += strlen(aliasPath)+1; + } + } + std::sort(fDylibAliases.begin(), fDylibAliases.end(), ByNameSorter()); + //fprintf(stderr, "fHeaderSize=0x%08X, fDylibAliases.size()=%lu\n", fHeaderSize, fDylibAliases.size()); + fHeaderSize = pageAlign(fHeaderSize); // check that cache we are about to create for verification purposes has same layout as existing cache if ( verify ) { @@ -749,13 +943,8 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSo } } - // calculate cache file header size - fHeaderSize = pageAlign(sizeof(dyld_cache_header) - + fMappings.size()*sizeof(shared_file_mapping_np) - + fDylibs.size()*sizeof(dyld_cache_image_info) ); - //+ fDependencyPool.size()*sizeof(uint16_t)); - if ( fHeaderSize > 0x3000 ) + if ( fHeaderSize > FIRST_DYLIB_TEXT_OFFSET ) throwf("header size miscalculation 0x%08X", fHeaderSize); } @@ -775,11 +964,42 @@ uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddr template -void SharedCache::assignNewBaseAddresses() +void SharedCache::assignNewBaseAddresses(bool verify) { - // first layout TEXT and DATA for split-seg (or can be split-seg) dylibs - uint64_t currentExecuteAddress = sharedRegionReadOnlyStartAddress() + 0x3000; - uint64_t currentWritableAddress = sharedRegionWritableStartAddress(); + uint64_t sharedCacheStartAddress = sharedRegionReadOnlyStartAddress(); +#if 0 + if ( arch() == CPU_TYPE_X86_64 ) { + if ( verify ) { + if ( fExistingCacheForVerification == NULL ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n", + getpid(), archName()); + } + const dyldCacheHeader* header = (dyldCacheHeader*)fExistingCacheForVerification; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)(fExistingCacheForVerification + header->mappingOffset()); + sharedCacheStartAddress = mappings[0].address(); + } + else { + // dyld shared cache can be more random + uint64_t readOnlySize = 0; + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + if ( ! it->layout->hasSplitSegInfo() ) + continue; + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + for (int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( ! seg.writable() ) + readOnlySize += pageAlign(seg.size()); + } + } + uint64_t maxSlide = sharedRegionReadOnlySize() - (readOnlySize + FIRST_DYLIB_TEXT_OFFSET); + sharedCacheStartAddress = sharedRegionReadOnlyStartAddress() + pageAlign(arc4random() % maxSlide); + } + } +#endif + uint64_t currentExecuteAddress = sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET; + uint64_t currentWritableAddress = sharedRegionWritableStartAddress() + FIRST_DYLIB_DATA_OFFSET; + + // first layout TEXT and DATA for dylibs for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); MachOLayoutAbstraction::Segment* executableSegment = NULL; @@ -859,13 +1079,19 @@ void SharedCache::assignNewBaseAddresses() } fLinkEditsTotalUnoptimizedSize = (currentReadOnlyAddress - fLinkEditsStartAddress + 4095) & (-4096); + // i386 dyld shared cache overflows after adding libclh.dylib + if ( (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress()) > sharedRegionReadOnlySize() ) + throwf("read-only slice of cache too big: %lluMB (max %lluMB)", + (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress())/(1024*1024), + sharedRegionReadOnlySize()/(1024*1024)); + // populate large mappings uint64_t cacheFileOffset = 0; - if ( currentExecuteAddress > sharedRegionReadOnlyStartAddress() + 0x3000 ) { + if ( currentExecuteAddress > sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET ) { shared_file_mapping_np executeMapping; - executeMapping.sfm_address = sharedRegionReadOnlyStartAddress(); - executeMapping.sfm_size = currentExecuteAddress - sharedRegionReadOnlyStartAddress(); + executeMapping.sfm_address = sharedCacheStartAddress; + executeMapping.sfm_size = currentExecuteAddress - sharedCacheStartAddress; executeMapping.sfm_file_offset = cacheFileOffset; executeMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_EXECUTE; executeMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_EXECUTE; @@ -906,7 +1132,7 @@ void SharedCache::assignNewBaseAddresses() // empty cache shared_file_mapping_np cacheHeaderMapping; cacheHeaderMapping.sfm_address = sharedRegionWritableStartAddress(); - cacheHeaderMapping.sfm_size = 0x3000; + cacheHeaderMapping.sfm_size = FIRST_DYLIB_TEXT_OFFSET; cacheHeaderMapping.sfm_file_offset = cacheFileOffset; cacheHeaderMapping.sfm_max_prot = VM_PROT_READ; cacheHeaderMapping.sfm_init_prot = VM_PROT_READ; @@ -917,26 +1143,43 @@ void SharedCache::assignNewBaseAddresses() template -uint64_t SharedCache::cacheFileOffsetForAddress(uint64_t addr) +uint64_t SharedCache::cacheFileOffsetForVMAddress(uint64_t vmaddr) const { - for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { - if ( (it->sfm_address <= addr) && (addr < it->sfm_address+it->sfm_size) ) - return it->sfm_file_offset + addr - it->sfm_address; + for(std::vector::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + if ( (it->sfm_address <= vmaddr) && (vmaddr < it->sfm_address+it->sfm_size) ) + return it->sfm_file_offset + vmaddr - it->sfm_address; } - throwf("address 0x%0llX is not in cache", addr); + throwf("address 0x%0llX is not in cache", vmaddr); +} + +template +uint64_t SharedCache::VMAddressForCacheFileOffset(uint64_t offset) const +{ + for(std::vector::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + if ( (it->sfm_file_offset <= offset) && (offset < it->sfm_file_offset+it->sfm_size) ) + return it->sfm_address + offset - it->sfm_file_offset; + } + throwf("offset 0x%0llX is not in cache", offset); } +template +void *SharedCache::mappedAddressForVMAddress(uint64_t vmaddr) +{ + if (!vmaddr) return NULL; + else return fInMemoryCache + cacheFileOffsetForVMAddress(vmaddr); +} template -uint64_t SharedCache::mappedCacheAddressForAddress(uint64_t addr) +uint64_t SharedCache::VMAddressForMappedAddress(const void *mapaddr) { - if (!addr) return 0; - else return (uint64_t)(fInMemoryCache + cacheFileOffsetForAddress(addr)); + if (!mapaddr) return 0; + uint64_t offset = (uint8_t *)mapaddr - (uint8_t *)fInMemoryCache; + return VMAddressForCacheFileOffset(offset); } template -bool SharedCache::notUpToDate(const void* cache) +bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) { dyldCacheHeader* header = (dyldCacheHeader*)cache; // not valid if header signature is wrong @@ -950,21 +1193,25 @@ bool SharedCache::notUpToDate(const void* cache) return false; } else { - fprintf(stderr, "update_dyld_shared_cache[%u] current cache file has invalid header\n", getpid()); + fprintf(stderr, "update_dyld_shared_cache[%u] updating cache because current cache file has invalid header\n", getpid()); return true; } } // not valid if count of images does not match current images needed - if ( header->imagesCount() != fDylibs.size() ) { + if ( header->imagesCount() != (fDylibs.size()+aliasCount) ) { if ( fVerify ) { fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file contains a different set of dylibs\n", getpid(), archName()); return false; } else { - fprintf(stderr, "update_dyld_shared_cache[%u] current cache file is invalid because it contains a different set of dylibs\n", getpid()); + fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because current cache file contains a different set of dylibs\n", getpid(), archName()); return true; } } + // get end of TEXT region + const dyldCacheFileMapping* textMapping = (dyldCacheFileMapping*)((uint8_t*)cache+sizeof(dyldCacheHeader)); + const uint32_t textSize = textMapping->size(); + // verify every dylib in constructed graph is in existing cache with same inode and modTime std::map sortingMap; const dyldCacheImageInfo* imagesStart = (dyldCacheImageInfo*)((uint8_t*)cache + header->imagesOffset()); @@ -974,6 +1221,10 @@ bool SharedCache::notUpToDate(const void* cache) //fprintf(stderr, "inode=0x%llX, mTime=0x%llX, path=%s\n", it->info.inode, it->info.modTime, it->layout->getID().name); for(const dyldCacheImageInfo* cacheEntry = imagesStart; cacheEntry < imagesEnd; ++cacheEntry) { if ( fVerify ) { + if ( cacheEntry->pathFileOffset() > textSize ) { + throwf("update_dyld_shared_cache[%u]: for arch=%s, image entries corrupt, bad path offset in %s\n", + getpid(), archName(), it->layout->getID().name); + } // in -verify mode, just match by path and warn if file looks different if ( strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0 ) { found = true; @@ -986,6 +1237,10 @@ bool SharedCache::notUpToDate(const void* cache) } } else { + if ( cacheEntry->pathFileOffset() > textSize ) { + // cache corrupt, needs to be regenerated + return true; + } // in normal update mode, everything has to match for cache to be up-to-date if ( (cacheEntry->inode() == it->info.inode) && (cacheEntry->modTime() == it->info.modTime) @@ -1000,7 +1255,7 @@ bool SharedCache::notUpToDate(const void* cache) throwf("update_dyld_shared_cache[%u] can't verify %s cache because %s is not in existing cache\n", getpid(), archName(), it->layout->getID().name); } else { - fprintf(stderr, "update_dyld_shared_cache[%u] current %s cache file invalid because %s has changed\n", getpid(), archName(), it->layout->getID().name); + fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because dylib at %s has changed\n", getpid(), archName(), it->layout->getID().name); return true; } } @@ -1024,7 +1279,7 @@ bool SharedCache::notUpToDate(const void* cache) template -bool SharedCache::notUpToDate(const char* path) +bool SharedCache::notUpToDate(const char* path, unsigned int aliasCount) { // mmap existing cache file int fd = ::open(path, O_RDONLY); @@ -1032,20 +1287,27 @@ bool SharedCache::notUpToDate(const char* path) return true; struct stat stat_buf; ::fstat(fd, &stat_buf); - uint8_t* mappingAddr = (uint8_t*)mmap(NULL, stat_buf.st_size, PROT_READ , MAP_FILE | MAP_PRIVATE, fd, 0); + uint32_t cacheFileSize = stat_buf.st_size; + uint32_t cacheAllocatedSize = (cacheFileSize + 4095) & (-4096); + uint8_t* mappingAddr = NULL; + if ( vm_allocate(mach_task_self(), (vm_address_t*)(&mappingAddr), cacheAllocatedSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS ) + throwf("can't vm_allocate cache of size %u", cacheFileSize); + // update_dyld_shared_cache -verify finds differences + (void)fcntl(fd, F_NOCACHE, 1); + ssize_t readResult = pread(fd, mappingAddr, cacheFileSize, 0); + if ( readResult != cacheFileSize ) + throw "can't read existing cache file"; ::close(fd); - if ( mappingAddr == (uint8_t*)(-1) ) - return true; // validate it - bool result = this->notUpToDate(mappingAddr); + bool result = this->notUpToDate(mappingAddr, aliasCount); if ( fVerify ) { // don't unmap yet, leave so it can be verified later fExistingCacheForVerification = mappingAddr; } else { // unmap - ::munmap(mappingAddr, stat_buf.st_size); + vm_deallocate(mach_task_self(), (vm_address_t)mappingAddr, cacheAllocatedSize); if ( verbose && !result ) fprintf(stderr, "update_dyld_shared_cache: %s is up-to-date\n", path); } @@ -1128,7 +1390,7 @@ template class LinkEditOptimizer { public: - LinkEditOptimizer(const MachOLayoutAbstraction&, uint8_t*, StringPool&); + LinkEditOptimizer(const MachOLayoutAbstraction&, const SharedCache&, uint8_t*, StringPool&); virtual ~LinkEditOptimizer() {} void copyBindInfo(uint32_t&); @@ -1140,6 +1402,7 @@ public: void copyImportedSymbols(uint32_t symbolTableOffset, uint32_t&); void copyExternalRelocations(uint32_t& offset); void copyIndirectSymbolTable(uint32_t& offset); + void copyFunctionStarts(uint32_t& offset); void updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset, uint32_t linkEditsFileOffset, bool keepSignatures); @@ -1151,12 +1414,14 @@ protected: private: + const SharedCache& fSharedCache; const macho_header

* fHeader; uint8_t* fNewLinkEditStart; uint8_t* fLinkEditBase; const MachOLayoutAbstraction& fLayout; macho_dyld_info_command

* fDyldInfo; macho_dysymtab_command

* fDynamicSymbolTable; + macho_linkedit_data_command

* fFunctionStarts; macho_symtab_command

* fSymbolTableLoadCommand; const macho_nlist

* fSymbolTable; const char* fStrings; @@ -1179,14 +1444,15 @@ private: uint32_t fImportedSymbolsCountInNewLinkEdit; uint32_t fExternalRelocationsOffsetIntoNewLinkEdit; uint32_t fIndirectSymbolTableOffsetInfoNewLinkEdit; + uint32_t fFunctionStartsOffsetInNewLinkEdit; }; template -LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, uint8_t* newLinkEdit, StringPool& stringPool) - : fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL), - fDynamicSymbolTable(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool), +LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, const SharedCache& sharedCache, uint8_t* newLinkEdit, StringPool& stringPool) + : fSharedCache(sharedCache), fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL), + fDynamicSymbolTable(NULL), fFunctionStarts(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool), fBindInfoOffsetIntoNewLinkEdit(0), fBindInfoSizeInNewLinkEdit(0), fWeakBindInfoOffsetIntoNewLinkEdit(0), fWeakBindInfoSizeInNewLinkEdit(0), fLazyBindInfoOffsetIntoNewLinkEdit(0), fLazyBindInfoSizeInNewLinkEdit(0), @@ -1195,7 +1461,8 @@ LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, ui fLocalSymbolsStartIndexInNewLinkEdit(0), fLocalSymbolsCountInNewLinkEdit(0), fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0), fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0), - fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0) + fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0), + fFunctionStartsOffsetInNewLinkEdit(0) { fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); @@ -1228,6 +1495,9 @@ LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, ui case LC_DYLD_INFO_ONLY: fDyldInfo = (macho_dyld_info_command

*)cmd; break; + case LC_FUNCTION_STARTS: + fFunctionStarts = (macho_linkedit_data_command

*)cmd; + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -1290,11 +1560,10 @@ void LinkEditOptimizer::copyLazyBindInfo(uint32_t& offset) template void LinkEditOptimizer::copyExportInfo(uint32_t& offset) { - if ( (fDyldInfo != NULL) && (fDyldInfo->export_off() != 0) ) { + if ( (fDyldInfo != NULL) && (fLayout.getDyldInfoExports() != NULL) ) { fExportInfoOffsetIntoNewLinkEdit = offset; fExportInfoSizeInNewLinkEdit = fDyldInfo->export_size(); - // warning, export_off is only 32-bits so if the trie grows it must be allocated with 32-bits of fLinkeditBase - memcpy(fNewLinkEditStart+offset, fLinkEditBase+(int32_t)fDyldInfo->export_off(), fDyldInfo->export_size()); + memcpy(fNewLinkEditStart+offset, fLayout.getDyldInfoExports(), fDyldInfo->export_size()); offset += fDyldInfo->export_size(); } } @@ -1314,7 +1583,7 @@ void LinkEditOptimizer::copyLocalSymbols(uint32_t symbolTableOffset, uint32_t if ( (entry->n_type() & N_TYPE) == N_SECT ) { macho_nlist

* newSymbolEntry = &newSymbolTableStart[symbolIndex]; *newSymbolEntry = *entry; - newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()])); + newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()])); ++symbolIndex; } } @@ -1336,7 +1605,7 @@ void LinkEditOptimizer::copyExportedSymbols(uint32_t symbolTableOffset, uint3 && (strncmp(&fStrings[entry->n_strx()], "$ld$", 4) != 0) ) { macho_nlist

* newSymbolEntry = &newSymbolTableStart[symbolIndex]; *newSymbolEntry = *entry; - newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()])); + newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()])); fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit; ++symbolIndex; } @@ -1395,6 +1664,16 @@ void LinkEditOptimizer::copyExternalRelocations(uint32_t& offset) } } +template +void LinkEditOptimizer::copyFunctionStarts(uint32_t& offset) +{ + if ( fFunctionStarts != NULL ) { + fFunctionStartsOffsetInNewLinkEdit = offset; + memcpy(&fNewLinkEditStart[offset], &fLinkEditBase[fFunctionStarts->dataoff()], fFunctionStarts->datasize()); + offset += fFunctionStarts->datasize(); + } +} + template void LinkEditOptimizer::copyIndirectSymbolTable(uint32_t& offset) { @@ -1431,6 +1710,20 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t si seg->set_filesize(size); seg->set_fileoff(linkEditsFileOffset); } + // don't alter __TEXT until is fixed + else if ( strcmp(seg->segname(), "__TEXT") != 0 ) { + // update all other segments fileoff to be offset from start of cache file + pint_t oldFileOff = seg->fileoff(); + seg->set_fileoff(fSharedCache.cacheFileOffsetForVMAddress(seg->vmaddr())); + pint_t fileOffsetDelta = seg->fileoff() - oldFileOff; + // update all sections in this segment + macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + sect->set_offset(sect->offset()+fileOffsetDelta); + } + } } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -1475,6 +1768,11 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t si fDynamicSymbolTable->set_locreloff(0); fDynamicSymbolTable->set_nlocrel(0); + // update function starts + if ( fFunctionStarts != NULL ) { + fFunctionStarts->set_dataoff(linkEditsFileOffset+fFunctionStartsOffsetInNewLinkEdit); + } + // now remove load commands no longer needed const macho_load_command

* srcCmd = cmds; macho_load_command

* dstCmd = (macho_load_command

*)cmds; @@ -1528,7 +1826,7 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) // create optimizer object for each LINKEDIT segment std::vector*> optimizers; for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { - optimizers.push_back(new LinkEditOptimizer(*it->layout, newLinkEdit, stringPool)); + optimizers.push_back(new LinkEditOptimizer(*it->layout, *this, newLinkEdit, stringPool)); } // rebase info is not copied because images in shared cache are never rebased @@ -1567,33 +1865,45 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) (*it)->copyExportedSymbols(symbolTableOffset, symbolTableIndex); (*it)->copyImportedSymbols(symbolTableOffset, symbolTableIndex); } + fSizeOfOldSymbolTableInfoInCombinedLinkedit = symbolTableIndex * sizeof(macho_nlist); + offset = symbolTableOffset + fSizeOfOldSymbolTableInfoInCombinedLinkedit & (-8); // copy external relocations, 8-byte aligned after end of symbol table - uint32_t externalRelocsOffset = symbolTableOffset + (symbolTableIndex * sizeof(macho_nlist) + 7) & (-8); - //uint32_t externalRelocsStartOffset = externalRelocsOffset; + fOffsetOfOldExternalRelocationsInCombinedLinkedit = offset; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyExternalRelocations(externalRelocsOffset); + (*it)->copyExternalRelocations(offset); } + fSizeOfOldExternalRelocationsInCombinedLinkedit = offset - fOffsetOfOldExternalRelocationsInCombinedLinkedit; + // copy function starts + fOffsetOfFunctionStartsInCombinedLinkedit = offset; + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyFunctionStarts(offset); + } + fSizeOfFunctionStartsInCombinedLinkedit = offset - fOffsetOfFunctionStartsInCombinedLinkedit; + // copy indirect symbol tables - uint32_t indirectSymbolTableOffset = externalRelocsOffset; + fOffsetOfOldIndirectSymbolsInCombinedLinkedit = offset; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyIndirectSymbolTable(indirectSymbolTableOffset); + (*it)->copyIndirectSymbolTable(offset); } - + fSizeOfOldIndirectSymbolsInCombinedLinkedit = offset - fOffsetOfOldIndirectSymbolsInCombinedLinkedit; + // copy string pool - uint32_t stringPoolOffset = indirectSymbolTableOffset; - memcpy(&newLinkEdit[stringPoolOffset], stringPool.getBuffer(), stringPool.size()); + fOffsetOfOldStringPoolInCombinedLinkedit = offset; + memcpy(&newLinkEdit[offset], stringPool.getBuffer(), stringPool.size()); + fSizeOfOldStringPoolInCombinedLinkedit = stringPool.size(); - // find new size - fLinkEditsTotalOptimizedSize = (stringPoolOffset + stringPool.size() + 4095) & (-4096); - - // choose new linkedit file offset - uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionReadOnlyStartAddress(); + // total new size round up to page size + fLinkEditsTotalOptimizedSize = (fOffsetOfOldStringPoolInCombinedLinkedit + fSizeOfOldStringPoolInCombinedLinkedit + 4095) & (-4096); + // choose new linkedit file offset + uint32_t linkEditsFileOffset = cacheFileOffsetForVMAddress(fLinkEditsStartAddress); +// uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionReadOnlyStartAddress(); + // update load commands so that all dylibs shared different areas of the same LINKEDIT segment for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, stringPoolOffset, linkEditsFileOffset, keepSignatures); + (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, fOffsetOfOldStringPoolInCombinedLinkedit, linkEditsFileOffset, keepSignatures); } //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, fLinkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, fLinkEditsTotalOptimizedSize); @@ -1627,7 +1937,7 @@ template class ObjCSelectorUniquer { private: - objc_selopt::string_map fSelectorStrings; + objc_opt::string_map fSelectorStrings; SharedCache *fCache; size_t fCount; @@ -1643,97 +1953,164 @@ public: { fCount++; const char *s = (const char *) - fCache->mappedCacheAddressForAddress(oldValue); - objc_selopt::string_map::iterator element = - fSelectorStrings.insert(objc_selopt::string_map::value_type(s, oldValue)).first; + fCache->mappedAddressForVMAddress(oldValue); + objc_opt::string_map::iterator element = + fSelectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first; return (typename A::P::uint_t)element->second; } - objc_selopt::string_map& strings() { + objc_opt::string_map& strings() { return fSelectorStrings; } size_t count() const { return fCount; } }; -template <> -void SharedCache::optimizeObjC() -{ - // objc optimizations on arm not yet supported -} - template -void SharedCache::optimizeObjC() +void SharedCache::optimizeObjC(std::vector& pointersInData) { + const char *err; + size_t headerSize = sizeof(objc_opt::objc_opt_t); + if ( verbose ) { - fprintf(stderr, "update_dyld_shared_cache: for %s, uniquing objc selectors\n", archName()); + fprintf(stderr, "update_dyld_shared_cache: for %s, optimizing objc metadata\n", archName()); } - // Find libobjc's __TEXT,__objc_selopt section - const macho_section

*seloptSection = NULL; + // Find libobjc's empty sections to fill in + const macho_section

*optROSection = NULL; + const macho_section

*optRWSection = NULL; for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { - if (0 == strstr(it->layout->getFilePath(), "libobjc")) continue; - const macho_header

*mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); - if ((seloptSection = mh->getSection("__TEXT", "__objc_selopt"))) break; + if ( strstr(it->layout->getFilePath(), "libobjc") != NULL ) { + const macho_header

* mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); + optROSection = mh->getSection("__TEXT", "__objc_opt_ro"); + // __objc_selopt is old name for __objc_opt_ro + if ( optROSection == NULL ) + optROSection = mh->getSection("__TEXT", "__objc_selopt"); + optRWSection = mh->getSection("__DATA", "__objc_opt_rw"); + break; + } } - if (!seloptSection) { - warn(archName(), "couldn't find libobjc's unique selector section (selectors not optimized)"); - return; - } - - objc_selopt::objc_selopt_t *seloptData = (objc_selopt::objc_selopt_t *) - mappedCacheAddressForAddress(seloptSection->addr()); - if (seloptSection->size() < sizeof(seloptData->version)) { - warn(archName(), "libobjc's unique selector section is too small (selectors not optimized)"); - return; - } - - if (E::get32(seloptData->version) != objc_selopt::VERSION) { - warn(archName(), "libobjc's unique selector section version is unrecognized (selectors not optimized)"); - return; - } + if ( optROSection == NULL ) { + warn(archName(), "libobjc's read-only section missing (metadata not optimized)"); + return; + } + + objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t*)mappedAddressForVMAddress(optROSection->addr()); + if (optROSection->size() < headerSize) { + warn(archName(), "libobjc's read-only section is too small (metadata not optimized)"); + return; + } + if (E::get32(optROHeader->version) != objc_opt::VERSION) { + warn(archName(), "libobjc's read-only section version is unrecognized (metadata not optimized)"); + return; + } // Update selector references and build selector list - ObjCSelectorUniquer uniq(this); + + // This is SAFE: if we run out of room for the selector table, + // the modified binaries are still usable. // Heuristic: choose selectors from libraries with more cstring data first. // This tries to localize selector cstring memory. + ObjCSelectorUniquer uniq(this); std::vector sortedDylibs = fDylibs; std::sort(sortedDylibs.begin(), sortedDylibs.end(), ByCStringSectionSizeSorter()); + SelectorOptimizer > selOptimizer(uniq); for(typename std::vector::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) { const macho_header

*mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); LegacySelectorUpdater >::update(this, mh, uniq); - SelectorUpdater >::update(this, mh, uniq); + selOptimizer.optimize(this, mh); } if ( verbose ) { fprintf(stderr, "update_dyld_shared_cache: for %s, found %zu unique objc selectors\n", archName(), uniq.strings().size()); } - // Write selector hash table to libobjc's __TEXT,__objc_selopt section - size_t bytesUsed; - const char *err = - objc_selopt::write_selopt(seloptData, seloptSection->addr(), - seloptSection->size(), uniq.strings(), - E::little_endian, &bytesUsed); + // Write selector table in read-only data. + size_t selTableOffset = P::round_up(headerSize); + size_t selTableSize; + objc_opt::objc_selopt_t *seloptData = (objc_opt::objc_selopt_t *) + mappedAddressForVMAddress(optROSection->addr() + selTableOffset); + err = objc_opt::write_selopt(seloptData, + optROSection->addr() + selTableOffset, + optROSection->size() - selTableOffset, + uniq.strings(), + E::little_endian, &selTableSize); if (err) { warn(archName(), err); return; } if ( verbose ) { + size_t totalSize = headerSize + selTableSize; fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes " - "(%d%%) used in libobjc unique selector section\n", - archName(), bytesUsed, seloptSection->size(), - (int)(bytesUsed / (double)seloptSection->size() * 100)); + "(%d%%) used in libobjc read-only optimization section\n", + archName(), totalSize, optROSection->size(), + (int)(totalSize / (double)optROSection->size() * 100)); fprintf(stderr, "update_dyld_shared_cache: for %s, " "updated %zu selector references\n", archName(), uniq.count()); + fprintf(stderr, "update_dyld_shared_cache: for %s, " + "wrote objc metadata optimization version %d\n", + archName(), objc_opt::VERSION); } + // if r/w section exists in libojc attempt to optimize categories into classes + if ( optRWSection != NULL ) { + // Attach categories to classes in the same framework. + // Build aggregated (but unsorted) method lists in read-write data. + + // This is SAFE: if we run out of room while attaching categories in + // a binary then previously-edited binaries are still valid. (This assumes + // each binary is processed all-or-nothing, which CategoryAttacher does.) + // This must be done AFTER uniquing selectors. + // This must be done BEFORE sorting method lists. + + size_t categoryOffset = 0; + uint8_t *categoryData = (uint8_t*)mappedAddressForVMAddress(optRWSection->addr() + categoryOffset); + CategoryAttacher categoryAttacher(categoryData, optRWSection->size() - categoryOffset); + for(typename std::vector::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) { + macho_header

*mh = (macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); + err = categoryAttacher.optimize(this, mh, pointersInData); + if (err) { + warn(archName(), err); + return; + } + } + size_t categorySize = categoryAttacher.bytesUsed(); + + + // Sort method lists. + + // This is SAFE: modified binaries are still usable as unsorted lists. + // This must be done AFTER uniquing selectors. + // This must be done AFTER attaching categories. + + MethodListSorter methodSorter; + for(typename std::vector::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) { + macho_header

*mh = (macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); + methodSorter.optimize(this, mh); + } + + if ( verbose ) { + size_t totalRWSize = categorySize; + fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes " + "(%d%%) used in libobjc read-write optimization section\n", + archName(), totalRWSize, optRWSection->size(), + (int)(totalRWSize / (double)optRWSection->size() * 100)); + fprintf(stderr, "update_dyld_shared_cache: for %s, " + "attached %zu categories (%zd bytes used)\n", + archName(), categoryAttacher.count(), + categoryAttacher.bytesUsed()); + } + } + + // Success. Update RO header last + E::set32(optROHeader->selopt_offset, headerSize); + return; } @@ -1751,6 +2128,14 @@ static void cleanup(int sig) } + +template <> bool SharedCache::addCacheSlideInfo(){ return true; } +template <> bool SharedCache::addCacheSlideInfo() { return true; } +template <> bool SharedCache::addCacheSlideInfo() { return false; } +template <> bool SharedCache::addCacheSlideInfo() { return false; } + + + template bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool deleteExistingFirst, int archIndex, int archCount, bool keepSignatures) @@ -1778,7 +2163,7 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de uint32_t cacheFileSize = 0; for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { uint32_t end = it->sfm_file_offset + it->sfm_size; - if ( end > cacheFileSize ) + if ( end > cacheFileSize ) cacheFileSize = end; } if ( vm_allocate(mach_task_self(), (vm_address_t*)(&inMemoryCache), cacheFileSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS ) @@ -1797,10 +2182,12 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de header->set_mappingOffset(sizeof(dyldCacheHeader)); header->set_mappingCount(fMappings.size()); header->set_imagesOffset(header->mappingOffset() + fMappings.size()*sizeof(dyldCacheFileMapping)); - header->set_imagesCount(fDylibs.size()); + header->set_imagesCount(fDylibs.size()+fDylibAliases.size()); header->set_dyldBaseAddress(fDyldBaseAddress); - //header->set_dependenciesOffset(sizeof(dyldCacheHeader) + fMappings.size()*sizeof(dyldCacheFileMapping) + fDylibs.size()*sizeof(dyldCacheImageInfo)); - //header->set_dependenciesCount(fDependencyPool.size()); + header->set_codeSignatureOffset(cacheFileSize); + header->set_codeSignatureSize(0); + header->set_slideInfoOffset(0); + header->set_slideInfoSize(0); // fill in mappings dyldCacheFileMapping* mapping = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; @@ -1822,8 +2209,18 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de image->set_address(it->info.address); image->set_modTime(it->info.modTime); image->set_inode(it->info.inode); - image->set_pathFileOffset(cacheFileOffsetForAddress(it->info.address+it->info.pathFileOffset)); - //image->set_dependenciesStartOffset(it->info.dependenciesStartOffset); + image->set_pathFileOffset(cacheFileOffsetForVMAddress(it->info.address+it->info.pathFileOffset)); + ++image; + } + + // add aliases to end of image table + for(typename std::vector::iterator it = fDylibAliases.begin(); it != fDylibAliases.end(); ++it) { + image->set_address(it->info.address); + image->set_modTime(it->info.modTime); + image->set_inode(it->info.inode); + image->set_pathFileOffset(it->info.pathFileOffset); + strcpy((char*)inMemoryCache+it->info.pathFileOffset, it->aliases[0]); + //fprintf(stderr, "adding alias to offset 0x%08X %s\n", it->info.pathFileOffset, it->aliases[0]); ++image; } @@ -1856,7 +2253,7 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de if ( seg.size() > 0 ) { const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset(); const uint64_t segmentSize = seg.fileSize(); - const uint64_t segmentDstStartOffset = cacheFileOffsetForAddress(seg.newAddress()); + const uint64_t segmentDstStartOffset = cacheFileOffsetForVMAddress(seg.newAddress()); ssize_t readResult = ::pread(src, &inMemoryCache[segmentDstStartOffset], segmentSize, segmentSrcStartOffset); if ( readResult != segmentSize ) { if ( readResult == -1 ) @@ -1887,16 +2284,24 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de for (int i=0; i < segs.size(); ++i) { MachOLayoutAbstraction::Segment& seg = segs[i]; if ( seg.size() > 0 ) - seg.setMappedAddress(inMemoryCache + cacheFileOffsetForAddress(seg.newAddress())); + seg.setMappedAddress(inMemoryCache + cacheFileOffsetForVMAddress(seg.newAddress())); //fprintf(stderr, "%s at %p to %p for %s\n", seg.name(), seg.mappedAddress(), (char*)seg.mappedAddress()+ seg.size(), it->layout->getID().name); } } + + // also construct list of all pointers in cache to other things in cache + std::vector pointersInData; + pointersInData.reserve(1024); + + // add pointer in start of __DATA to start of __TEXT to remain compatible with previous dylds + pint_t* dataStartPtr = (pint_t*)(&inMemoryCache[fMappings[1].sfm_file_offset]); + P::setP(*dataStartPtr, fMappings[0].sfm_address); // rebase each dylib in shared cache for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { try { Rebaser r(*it->layout); - r.rebase(); + r.rebase(pointersInData); //if ( verbose ) // fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name); } @@ -1905,20 +2310,6 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de } } - // merge/optimize all LINKEDIT segments - if ( optimize ) { - //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); - cacheFileSize = (this->optimizeLINKEDIT(keepSignatures) - inMemoryCache); - //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); - // update header to reduce mapping size - dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; - dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; - dyldCacheFileMapping* lastMapping = &mappings[cacheHeader->mappingCount()-1]; - lastMapping->set_size(cacheFileSize-lastMapping->file_offset()); - // update fMappings so .map file will print correctly - fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset; - } - if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information for %lu files:\n", archName(), fDylibs.size()); // instantiate a Binder for each image and add to map @@ -1942,7 +2333,16 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information in cache for %s\n", archName(), (*it)->getDylibID()); try { - (*it)->bind(); + (*it)->bind(pointersInData); + } + catch (const char* msg) { + throwf("%s in %s", msg, (*it)->getDylibID()); + } + } + // optimize binding + for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { + try { + (*it)->optimize(); } catch (const char* msg) { throwf("%s in %s", msg, (*it)->getDylibID()); @@ -1953,16 +2353,118 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de delete *it; } + // merge/optimize all LINKEDIT segments + if ( optimize ) { + //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); + cacheFileSize = (this->optimizeLINKEDIT(keepSignatures) - inMemoryCache); + //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); + // update header to reduce mapping size + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; + dyldCacheFileMapping* lastMapping = &mappings[cacheHeader->mappingCount()-1]; + lastMapping->set_size(cacheFileSize-lastMapping->file_offset()); + // update fMappings so .map file will print correctly + fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset; + // update header + //fprintf(stderr, "update_dyld_shared_cache: changing end of cache address from 0x%08llX to 0x%08llX\n", + // header->codeSignatureOffset(), fMappings.back().sfm_address + fMappings.back().sfm_size); + header->set_codeSignatureOffset(fMappings.back().sfm_file_offset + fMappings.back().sfm_size); + } + // unique objc selectors and update other objc metadata - if (optimize) { - optimizeObjC(); - } - if ( progress ) { - // assuming objc optimizations takes 15% of time - fprintf(stdout, "%3u/%u\n", (archIndex+1)*55, archCount*100); + if ( optimize ) { + optimizeObjC(pointersInData); + if ( progress ) { + // assuming objc optimizations takes 15% of time + fprintf(stdout, "%3u/%u\n", (archIndex+1)*55, archCount*100); + } } + if ( addCacheSlideInfo() ) { + // build bitmap of which pointers need sliding + uint8_t* const dataStart = &inMemoryCache[fMappings[1].sfm_file_offset]; // R/W mapping is always second + uint8_t* const dataEnd = &inMemoryCache[fMappings[1].sfm_file_offset+fMappings[1].sfm_size]; + const int bitmapSize = (dataEnd - dataStart)/(4*8); + uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1); + void* lastPointer = inMemoryCache; + for(std::vector::iterator pit=pointersInData.begin(); pit != pointersInData.end(); ++pit) { + if ( *pit != lastPointer ) { + void* p = *pit; + if ( (p < dataStart) || ( p > dataEnd) ) + throwf("DATA pointer for sliding, out of range 0x%08lX\n", (long)((uint8_t*)p-inMemoryCache)); + long offset = (long)((uint8_t*)p - dataStart); + if ( (offset % 4) != 0 ) + throwf("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset); + long byteIndex = offset / (4*8); + long bitInByte = (offset % 32) >> 2; + bitmap[byteIndex] |= (1 << bitInByte); + lastPointer = p; + } + } + + // allocate worst case size block of all slide info + const int entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes. + const int toc_count = bitmapSize/entry_size; + int slideInfoSize = sizeof(dyldCacheSlideInfo) + 2*toc_count + entry_size*(toc_count+1); + dyldCacheSlideInfo* slideInfo = (dyldCacheSlideInfo*)calloc(slideInfoSize, 1); + slideInfo->set_version(1); + slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo)); + slideInfo->set_toc_count(toc_count); + slideInfo->set_entries_offset((slideInfo->toc_offset()+2*toc_count+127)&(-128)); + slideInfo->set_entries_count(0); + slideInfo->set_entries_size(entry_size); + // append each unique entry + const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap; + dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset()); + int entry_count = 0; + for (int i=0; i < toc_count; ++i) { + const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i]; + // see if it is same as one already added + bool found = false; + for (int j=0; j < entry_count; ++j) { + if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) { + //fprintf(stderr, "toc[%d] optimized to %d\n", i, j); + slideInfo->set_toc(i, j); + found = true; + break; + } + } + if ( ! found ) { + // append to end + memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size); + slideInfo->set_toc(i, entry_count++); + } + } + slideInfo->set_entries_count(entry_count); + + int slideInfoPageSize = (slideInfo->entries_offset() + entry_count*entry_size + 4095) & (-4096); + cacheFileSize += slideInfoPageSize; + + // update mappings to increase RO size + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; + dyldCacheFileMapping* lastMapping = &mappings[cacheHeader->mappingCount()-1]; + lastMapping->set_size(lastMapping->size()+slideInfoPageSize); + + // update header to show location of slidePointers + cacheHeader->set_slideInfoOffset(cacheHeader->codeSignatureOffset()); + cacheHeader->set_slideInfoSize(slideInfoPageSize); + cacheHeader->set_codeSignatureOffset(cacheHeader->codeSignatureOffset()+slideInfoPageSize); + + // update fMappings so .map file will print correctly + fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset; + + // copy compressed into into buffer + memcpy(&inMemoryCache[cacheHeader->slideInfoOffset()], slideInfo, slideInfoPageSize); + } + + if ( fVerify ) { + // if no existing cache, say so + if ( fExistingCacheForVerification == NULL ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n", + getpid(), archName()); + } // new cache is built, compare header entries const dyldCacheHeader* newHeader = (dyldCacheHeader*)inMemoryCache; const dyldCacheHeader* oldHeader = (dyldCacheHeader*)fExistingCacheForVerification; @@ -2070,11 +2572,33 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de if ( result != 0 ) fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath); - // atomically swap in new cache file, do this after F_FULLFSYNC + // Make life easier for the kernel at shutdown. + // If we just move the new cache file over the old, the old file + // may need to exist in the open-unlink state. But because it + // may be mapped into the shared region, it cannot be deleted + // until all user processes are terminated. That leaves are + // small to non-existent window for the kernel to delete the + // old cache file. + if ( fCacheFileInFinalLocation ) { + char tmpDirPath[64]; + const char* pathLastSlash = strrchr(fCacheFilePath, '/'); + if ( pathLastSlash != NULL ) { + sprintf(tmpDirPath, "/var/run%s.old.%u", pathLastSlash, getpid()); + // move existing cache file to /var/run to be clean up next boot + result = ::rename(fCacheFilePath, tmpDirPath); + if ( result != 0 ) { + if ( errno != ENOENT ) + fprintf(stderr, "update_dyld_shared_cache: warning, unable to move existing cache to %s errno=%d for %s\n", tmpDirPath, errno, fCacheFilePath); + } + } + } + + // move new cache file to correct location for use after reboot result = ::rename(tempCachePath, fCacheFilePath); if ( result != 0 ) throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, fCacheFilePath, errno); - + + // flush everything to disk to assure rename() gets recorded ::sync(); didUpdate = true; @@ -2128,13 +2652,33 @@ bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool de (fOffsetOfOldSymbolTableInfoInCombinedLinkedit-fOffsetOfLazyBindInfoInCombinedLinkedit)/1024, fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit); - fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table info\n", - (fLinkEditsTotalOptimizedSize-fOffsetOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024), + fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table size\n", + (fSizeOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024), fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit, - fLinkEditsStartAddress+fLinkEditsTotalOptimizedSize); + fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit+fSizeOfOldSymbolTableInfoInCombinedLinkedit); + if ( fSizeOfFunctionStartsInCombinedLinkedit != 0 ) + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld functions starts size\n", + fSizeOfFunctionStartsInCombinedLinkedit/1024, + fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit+fSizeOfFunctionStartsInCombinedLinkedit); + if ( fSizeOfOldExternalRelocationsInCombinedLinkedit != 0 ) + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld external relocs size\n", + fSizeOfOldExternalRelocationsInCombinedLinkedit/1024, + fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit+fSizeOfOldExternalRelocationsInCombinedLinkedit); + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld indirect symbol table size\n", + fSizeOfOldIndirectSymbolsInCombinedLinkedit/1024, + fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit+fSizeOfOldIndirectSymbolsInCombinedLinkedit); + fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld string pool\n", + (fSizeOfOldStringPoolInCombinedLinkedit)/(1024*1024), + fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit+fSizeOfOldStringPoolInCombinedLinkedit); for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { fprintf(fmap, "%s\n", it->layout->getID().name); + for (std::vector::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait) + fprintf(fmap, "%s\n", *ait); const std::vector& segs = it->layout->getSegments(); for (int i=0; i < segs.size(); ++i) { const MachOLayoutAbstraction::Segment& seg = segs[i]; @@ -2227,7 +2771,15 @@ static void parsePathsFile(const char* filePath, std::vector& paths *last = '\0'; --last; } - paths.push_back(symbolStart); + // images in shared cache are bound against different IOKit than found at runtime + // HACK: Just ignore the known bad IOKit + if ( strcmp(symbolStart, "/System/Library/Frameworks/IOKit.framework/IOKit") == 0 ) { + fprintf(stderr, "update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); + warnings.push_back("update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); + } + else { + paths.push_back(symbolStart); + } symbolStart = NULL; state = lineStart; } @@ -2281,14 +2833,23 @@ static void scanForSharedDylibs(const char* rootPath, bool usesOverlay, const ch if ( dir == NULL ) throwf("%s does not exist, errno=%d\n", dirOfPathFiles, errno); for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) { - if ( entry->d_type == DT_REG ) { - // only look at files ending in .paths + if ( entry->d_type == DT_REG || entry->d_type == DT_UNKNOWN ) { + // only look at regular files ending in .paths if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) { + struct stat tmpStatPathsFile; char fullPath[strlen(dirOfPathFiles)+entry->d_namlen+2]; strcpy(fullPath, dirOfPathFiles); strcat(fullPath, "/"); strcat(fullPath, entry->d_name); - parsePathsFile(fullPath, rootsPaths); + if ( lstat(fullPath, &tmpStatPathsFile) == -1 ) { + fprintf(stderr, "update_dyld_shared_cache: can't access %s\n", fullPath); + } + else if ( S_ISREG(tmpStatPathsFile.st_mode) ) { + parsePathsFile(fullPath, rootsPaths); + } + else { + fprintf(stderr, "update_dyld_shared_cache: wrong file type for %s\n", fullPath); + } } else { fprintf(stderr, "update_dyld_shared_cache: warning, ignore file with wrong extension: %s\n", entry->d_name); @@ -2317,15 +2878,15 @@ static void setSharedDylibs(const char* rootPath, bool usesOverlay, const char* // creation time was before the last restart of this machine. static void deleteOrphanTempCacheFiles() { - DIR* dir = ::opendir(DYLD_SHARED_CACHE_DIR); + DIR* dir = ::opendir(MACOSX_DYLD_SHARED_CACHE_DIR); if ( dir != NULL ) { std::vector filesToDelete; for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) { if ( entry->d_type == DT_REG ) { // only look at files with .tmp in name if ( strstr(entry->d_name, ".tmp") != NULL ) { - char fullPath[strlen(DYLD_SHARED_CACHE_DIR)+entry->d_namlen+2]; - strcpy(fullPath, DYLD_SHARED_CACHE_DIR); + char fullPath[strlen(MACOSX_DYLD_SHARED_CACHE_DIR)+entry->d_namlen+2]; + strcpy(fullPath, MACOSX_DYLD_SHARED_CACHE_DIR); strcat(fullPath, "/"); strcat(fullPath, entry->d_name); struct stat tmpFileStatInfo; @@ -2383,36 +2944,37 @@ static bool updateSharedeCacheFile(const char* rootPath, bool usesOverlay, const { #if __i386__ || __x86_64__ // Rosetta does not work with optimized dyld shared cache - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, false, usesOverlay, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, false, usesOverlay, dyldBaseAddress); didUpdate |= cache.update(usesOverlay, force, false, deleteExistingFirst, index, archCount, keepSignatures); #else - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); #endif } break; case CPU_TYPE_I386: { - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; case CPU_TYPE_X86_64: { - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; case CPU_TYPE_ARM: { - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; } } - deleteOrphanTempCacheFiles(); + if ( !iPhoneOS ) + deleteOrphanTempCacheFiles(); return didUpdate; } @@ -2436,6 +2998,7 @@ int main(int argc, const char* argv[]) bool hasOverlay = false; bool verify = false; bool keepSignatures = false; + const char* cacheDir = NULL; try { // parse command line options @@ -2463,11 +3026,13 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-no_opt") == 0 ) { optimize = false; } + else if ( strcmp(arg, "-iPhone") == 0 ) { + iPhoneOS = true; + } else if ( strcmp(arg, "-dylib_list") == 0 ) { dylibListFile = argv[++i]; if ( dylibListFile == NULL ) throw "-dylib_list missing path argument"; - keepSignatures = true; } else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) { if ( hasOverlay ) @@ -2485,6 +3050,11 @@ int main(int argc, const char* argv[]) throw "-root missing path argument"; hasOverlay = true; } + else if ( strcmp(arg, "-cache_dir") == 0 ) { + cacheDir = argv[++i]; + if ( cacheDir == NULL ) + throw "-cache_dir missing path argument"; + } else if ( strcmp(arg, "-arch") == 0 ) { const char* arch = argv[++i]; if ( strcmp(arch, "ppc") == 0 ) @@ -2523,41 +3093,55 @@ int main(int argc, const char* argv[]) } // strip tailing slashes on -root or -overlay + // make it a real path so as to not make all dylibs look like symlink aliases if ( rootPath[0] != '\0' ) { - int len = strlen(rootPath)-1; - if ( rootPath[len] == '/' ) { - char* newRootPath = strdup(rootPath); - while ( newRootPath[len] == '/' ) - newRootPath[len--] = '\0'; - rootPath = newRootPath; - } + char realRootPath[MAXPATHLEN]; + if ( realpath(rootPath, realRootPath) == NULL ) + throwf("realpath() failed on %s\n", rootPath); + rootPath = strdup(realRootPath); } + // set location to write cache dir + if ( cacheDir == NULL ) { + if ( (rootPath[0] == '\0') || hasOverlay ) { + cacheDir = (iPhoneOS ? IPHONE_DYLD_SHARED_CACHE_DIR : MACOSX_DYLD_SHARED_CACHE_DIR); + } + else { + asprintf((char**)&cacheDir, "%s/%s", rootPath, (iPhoneOS ? IPHONE_DYLD_SHARED_CACHE_DIR : MACOSX_DYLD_SHARED_CACHE_DIR)); + } + } + // if no restrictions specified, use architectures that work on this machine if ( onlyArchs.size() == 0 ) { - int available; - size_t len = sizeof(int); - #if __i386__ || __x86_64__ - onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); - // check rosetta is installed - char rosettaPath[1024]; - strlcpy(rosettaPath, rootPath, 1024); - strlcat(rosettaPath, "/usr/libexec/oah/translate", 1024); - struct stat stat_buf; - if ( stat(rosettaPath, &stat_buf) == 0 ) { - onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)); + if ( iPhoneOS ) { + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6)); + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7)); } - else if ( hasOverlay ) { - // in overlay mode, rosetta may be installed on base system, but is not in update root - if ( stat("/usr/libexec/oah/translate", &stat_buf) == 0 ) + else { + int available; + size_t len = sizeof(int); + #if __i386__ || __x86_64__ + onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); + // check rosetta is installed + char rosettaPath[1024]; + strlcpy(rosettaPath, rootPath, 1024); + strlcat(rosettaPath, "/usr/libexec/oah/translate", 1024); + struct stat stat_buf; + if ( stat(rosettaPath, &stat_buf) == 0 ) { onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)); + } + else if ( hasOverlay ) { + // in overlay mode, rosetta may be installed on base system, but is not in update root + if ( stat("/usr/libexec/oah/translate", &stat_buf) == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)); + } + // check system is capable of running 64-bit programs + if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); + #else + #error unsupported architecture + #endif } - // check system is capable of running 64-bit programs - if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) - onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); - #else - #error unknown architecture - #endif } if ( !verify && (geteuid() != 0) ) @@ -2568,7 +3152,7 @@ int main(int argc, const char* argv[]) setSharedDylibs(rootPath, hasOverlay, dylibListFile, onlyArchs); else scanForSharedDylibs(rootPath, hasOverlay, "/var/db/dyld/shared_region_roots/", onlyArchs); - updateSharedeCacheFile(rootPath, hasOverlay, DYLD_SHARED_CACHE_DIR, onlyArchs, force, alphaSort, optimize, + updateSharedeCacheFile(rootPath, hasOverlay, cacheDir, onlyArchs, force, alphaSort, optimize, false, verify, keepSignatures); } catch (const char* msg) { diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 23f89cb..3b0e824 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,7 +40,6 @@ uint32_t ImageLoader::fgImagesUsedFromSharedCache = 0; uint32_t ImageLoader::fgImagesWithUsedPrebinding = 0; -uint32_t ImageLoader::fgImagesRequiringNoFixups = 0; uint32_t ImageLoader::fgImagesRequiringCoalescing = 0; uint32_t ImageLoader::fgImagesHasWeakDefinitions = 0; uint32_t ImageLoader::fgTotalRebaseFixups = 0; @@ -59,6 +58,7 @@ uint64_t ImageLoader::fgTotalWeakBindTime; uint64_t ImageLoader::fgTotalDOF; uint64_t ImageLoader::fgTotalInitTime; uint16_t ImageLoader::fgLoadOrdinal = 0; +std::vectorImageLoader::fgInterposingTuples; uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; @@ -67,10 +67,11 @@ ImageLoader::ImageLoader(const char* path, unsigned int libCount) : fPath(path), fDevice(0), fInode(0), fLastModified(0), fPathHash(0), fDlopenReferenceCount(0), fStaticReferenceCount(0), fDynamicReferenceCount(0), fDynamicReferences(NULL), fInitializerRecursiveLock(NULL), - fDepth(0), fLoadOrder(0), fState(0), fLibraryCount(libCount), + fDepth(0), fLoadOrder(fgLoadOrdinal++), fState(0), fLibraryCount(libCount), fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), fHideSymbols(false), fMatchByInstallName(false), - fRegisteredDOF(false), fAllLazyPointersBound(false), fBeingRemoved(false), fAddFuncNotified(false), + fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false), + fBeingRemoved(false), fAddFuncNotified(false), fPathOwnedByImage(false), fWeakSymbolsBound(false) { if ( fPath != NULL ) @@ -82,9 +83,11 @@ void ImageLoader::deleteImage(ImageLoader* image) { // this cannot be done in destructor because libImage() is implemented // in a subclass + DependentLibraryInfo libraryInfos[image->libraryCount()]; + image->doGetDependentLibraries(libraryInfos); for(unsigned int i=0; i < image->libraryCount(); ++i) { ImageLoader* lib = image->libImage(i); - if ( lib != NULL ) + if ( (lib != NULL) && ! libraryInfos[i].upward ) lib->fStaticReferenceCount--; } delete image; @@ -96,7 +99,7 @@ ImageLoader::~ImageLoader() if ( fPathOwnedByImage && (fPath != NULL) ) delete [] fPath; if ( fDynamicReferences != NULL ) { - for (std::set::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { + for (std::vector::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { const_cast(*it)->fDynamicReferenceCount--; } delete fDynamicReferences; @@ -118,10 +121,20 @@ void ImageLoader::setMapped(const LinkContext& context) void ImageLoader::addDynamicReference(const ImageLoader* target) { - if ( fDynamicReferences == NULL ) - fDynamicReferences = new std::set(); - if ( fDynamicReferences->count(target) == 0 ) { - fDynamicReferences->insert(target); + bool alreadyInVector = false; + if ( fDynamicReferences == NULL ) { + fDynamicReferences = new std::vector(); + } + else { + for (std::vector::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { + if ( *it == target ) { + alreadyInVector = true; + break; + } + } + } + if ( ! alreadyInVector ) { + fDynamicReferences->push_back(target); const_cast(target)->fDynamicReferenceCount++; } //dyld::log("dyld: addDynamicReference() from %s to %s, fDynamicReferences->size()=%lu\n", this->getPath(), target->getPath(), fDynamicReferences->size()); @@ -246,6 +259,13 @@ bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) c for(unsigned int i=0, e=segmentCount(); i < e; ++i) { const uint8_t* segStart = (const uint8_t*)segActualLoadAddress(i); const uint8_t* segEnd = (const uint8_t*)segActualEndAddress(i); + if ( strcmp(segName(i), "__UNIXSTACK") == 0 ) { + // __UNIXSTACK never slides. This is the only place that cares + // and checking for that segment name in segActualLoadAddress() + // is too expensive. + segStart -= getSlide(); + segEnd -= getSlide(); + } if ( (start <= segStart) && (segStart < end) ) return true; if ( (start <= segEnd) && (segEnd < end) ) @@ -332,11 +352,20 @@ const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImag return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); } +// this is called by initializeMainExecutable() to interpose on the initial set of images +void ImageLoader::applyInterposing(const LinkContext& context) +{ + if ( fgInterposingTuples.size() != 0 ) + this->recursiveApplyInterposing(context); +} void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, const RPathChain& loaderRPaths) { //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fStaticReferenceCount, fNeverUnload); + // clear error strings + (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL); + uint64_t t0 = mach_absolute_time(); this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths); context.notifyBatch(dyld_image_state_dependents_mapped); @@ -358,20 +387,29 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr uint64_t t4 = mach_absolute_time(); this->weakBind(context); + uint64_t t5 = mach_absolute_time(); + context.notifyBatch(dyld_image_state_bound); + uint64_t t6 = mach_absolute_time(); - uint64_t t5 = mach_absolute_time(); std::vector dofs; this->recursiveGetDOFSections(context, dofs); context.registerDOFs(dofs); - uint64_t t6 = mach_absolute_time(); + uint64_t t7 = mach_absolute_time(); + // interpose any dynamically loaded images + if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) { + this->recursiveApplyInterposing(context); + } + // clear error strings + (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL); + fgTotalLoadLibrariesTime += t1 - t0; fgTotalRebaseTime += t3 - t2; fgTotalBindTime += t4 - t3; fgTotalWeakBindTime += t5 - t4; - fgTotalDOF += t6 - t5; + fgTotalDOF += t7 - t6; // done with initial dylib loads fgNextPIEDylibAddress = 0; @@ -393,11 +431,11 @@ bool ImageLoader::decrementDlopenReferenceCount() return false; } -void ImageLoader::runInitializers(const LinkContext& context) +void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) { uint64_t t1 = mach_absolute_time(); mach_port_t this_thread = mach_thread_self(); - this->recursiveInitialization(context, this_thread); + this->recursiveInitialization(context, this_thread, timingInfo); context.notifyBatch(dyld_image_state_initialized); mach_port_deallocate(mach_task_self(), this_thread); uint64_t t2 = mach_absolute_time(); @@ -486,7 +524,7 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) { // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized // in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded - setLibImage(i, NULL, false); + setLibImage(i, NULL, false, false); continue; } #endif @@ -500,7 +538,8 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli } if ( fNeverUnload ) dependentLib->setNeverUnload(); - dependentLib->fStaticReferenceCount += 1; + if ( ! requiredLibInfo.upward ) + dependentLib->fStaticReferenceCount += 1; LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(); depLibReRequired = requiredLibInfo.required; depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum ); @@ -511,13 +550,18 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli } // check found library version is compatible if ( actualInfo.minVersion < requiredLibInfo.info.minVersion ) { + // record values for possible use by CrashReporter or Finder dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d", this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff, dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff); } - // prebinding for this image disabled if any dependent library changed or slid - if ( !depLibCheckSumsMatch || (dependentLib->getSlide() != 0) ) + // prebinding for this image disabled if any dependent library changed + if ( !depLibCheckSumsMatch ) + canUsePrelinkingInfo = false; + // prebinding for this image disabled unless both this and dependent are in the shared cache + if ( !dependentLib->inSharedCache() || !this->inSharedCache() ) canUsePrelinkingInfo = false; + //if ( context.verbosePrebinding ) { // if ( !requiredLib.checksumMatches ) // fprintf(stderr, "dyld: checksum mismatch, (%u v %u) for %s referencing %s\n", @@ -531,13 +575,20 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath()); if ( requiredLibInfo.required ) { fState = dyld_image_state_mapped; + // record values for possible use by CrashReporter or Finder + if ( strstr(msg, "Incompatible") != NULL ) + (*context.setErrorStrings)(dyld_error_kind_dylib_version, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "architecture") != NULL ) + (*context.setErrorStrings)(dyld_error_kind_dylib_wrong_arch, this->getPath(), requiredLibInfo.name, NULL); + else + (*context.setErrorStrings)(dyld_error_kind_dylib_missing, this->getPath(), requiredLibInfo.name, NULL); dyld::throwf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getPath(), msg); } // ok if weak library not found dependentLib = NULL; canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero } - setLibImage(i, dependentLib, depLibReExported); + setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward); } fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo; @@ -592,11 +643,36 @@ void ImageLoader::recursiveRebase(const LinkContext& context) catch (const char* msg) { // this image is not rebased fState = dyld_image_state_dependents_mapped; + CRSetCrashLogMessage2(NULL); throw; } } } +void ImageLoader::recursiveApplyInterposing(const LinkContext& context) +{ + if ( ! fInterposed ) { + // break cycles + fInterposed = true; + + try { + // interpose lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveApplyInterposing(context); + } + + // interpose this image + doInterpose(context); + } + catch (const char* msg) { + // this image is not interposed + fInterposed = false; + throw; + } + } +} @@ -628,6 +704,7 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound catch (const char* msg) { // restore state fState = dyld_image_state_rebased; + CRSetCrashLogMessage2(NULL); throw; } } @@ -644,15 +721,19 @@ void ImageLoader::weakBind(const LinkContext& context) // count how many have not already had weakbinding done int countNotYetWeakBound = 0; int countOfImagesWithWeakDefinitions = 0; + int countOfImagesWithWeakDefinitionsNotInSharedCache = 0; for(int i=0; i < count; ++i) { if ( ! imagesNeedingCoalescing[i]->fWeakSymbolsBound ) ++countNotYetWeakBound; - if ( imagesNeedingCoalescing[i]->hasCoalescedExports() ) + if ( imagesNeedingCoalescing[i]->hasCoalescedExports() ) { ++countOfImagesWithWeakDefinitions; + if ( ! imagesNeedingCoalescing[i]->inSharedCache() ) + ++countOfImagesWithWeakDefinitionsNotInSharedCache; + } } // don't need to do any coalescing if only one image has overrides, or all have already been done - if ( (countOfImagesWithWeakDefinitions > 1) && (countNotYetWeakBound > 0) ) { + if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { // make symbol iterators for each ImageLoader::CoalIterator iterators[count]; ImageLoader::CoalIterator* sortedIts[count]; @@ -781,7 +862,7 @@ void ImageLoader::recursiveSpinUnLock() } -void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread) +void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, InitializerTimingList& timingInfo) { recursive_lock lock_info(this_thread); recursiveSpinLock(lock_info); @@ -796,8 +877,8 @@ void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_ ImageLoader* dependentImage = libImage(i); if ( dependentImage != NULL ) // don't try to initialize stuff "above" me - if ( (dependentImage != NULL) && (dependentImage->fDepth >= fDepth) ) - dependentImage->recursiveInitialization(context, this_thread); + if ( (dependentImage != NULL) && (dependentImage->fDepth >= fDepth) && !libIsUpward(i) ) + dependentImage->recursiveInitialization(context, this_thread, timingInfo); } // record termination order @@ -805,17 +886,25 @@ void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_ context.terminationRecorder(this); // let objc know we are about to initalize this image + uint64_t t1 = mach_absolute_time(); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this); - + // initialize this image - this->doInitialization(context); - + bool hasInitializers = this->doInitialization(context); + // let anyone know we finished initalizing this image fState = dyld_image_state_initialized; oldState = fState; context.notifySingle(dyld_image_state_initialized, this); + + if ( hasInitializers ) { + uint64_t t2 = mach_absolute_time(); + timingInfo.images[timingInfo.count].image = this; + timingInfo.images[timingInfo.count].initTime = (t2-t1); + timingInfo.count++; + } } catch (const char* msg) { // this image is not initialized @@ -876,14 +965,21 @@ static char* commatize(uint64_t in, char* out) } -void ImageLoader::printStatistics(unsigned int imageCount) +void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo) { uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; char commaNum1[40]; char commaNum2[40]; printTime("total time", totalTime, totalTime); - dyld::log("total images loaded: %d (%u from dyld shared cache, %u needed no fixups)\n", imageCount, fgImagesUsedFromSharedCache, fgImagesRequiringNoFixups); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( fgImagesUsedFromSharedCache != 0 ) + dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache); + else + dyld::log("total images loaded: %d\n", imageCount); +#else + dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache); +#endif dyld::log("total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096); printTime("total images loading time", fgTotalLoadLibrariesTime, totalTime); printTime("total dtrace DOF registration time", fgTotalDOF, totalTime); @@ -901,6 +997,11 @@ void ImageLoader::printStatistics(unsigned int imageCount) printTime("total weak binding fixups time", fgTotalWeakBindTime, totalTime); dyld::log("total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); printTime("total initializer time", fgTotalInitTime, totalTime); + for (uintptr_t i=0; i < timingInfo.count; ++i) { + dyld::log("%21s ", timingInfo.images[i].image->getShortName()); + printTime("", timingInfo.images[i].initTime, totalTime); + } + } diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 587a339..582e60c 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -35,9 +35,18 @@ #include #include #include -#include #include +#if (__i386__ || __x86_64__) + #include +#else + // work around until iOS has CrashReporterClient.h + #define CRSetCrashLogMessage(x) + #define CRSetCrashLogMessage2(x) +#endif + +#define LOG_BINDINGS 0 + #include "mach-o/dyld_images.h" #include "mach-o/dyld_priv.h" @@ -58,16 +67,27 @@ #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM #endif +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif #define SPLIT_SEG_SHARED_REGION_SUPPORT __arm__ #define SPLIT_SEG_DYLIB_SUPPORT (__ppc__ || __i386__ || __arm__) #define PREBOUND_IMAGE_SUPPORT (__ppc__ || __i386__ || __arm__) -#define TEXT_RELOC_SUPPORT (__ppc__ || __i386__) -#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__) +#define TEXT_RELOC_SUPPORT (__ppc__ || __i386__ || __arm__) +#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__ || __arm__) #define SUPPORT_OLD_CRT_INITIALIZATION (__ppc__ || __i386__) -#define COMPRESSED_DYLD_INFO_SUPPORT (__i386__ || __x86_64__) -#define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__) +#define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) +#define SUPPORT_VERSIONED_PATHS (__i386__ || __x86_64__) +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define CORESYMBOLICATION_SUPPORT 1 +#else + #define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__) +#endif #if __arm__ #define INITIAL_IMAGE_COUNT 100 #else @@ -82,6 +102,9 @@ namespace dyld { extern void log(const char* format, ...) __attribute__((format(printf, 1, 2))); extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2))); extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2))); +#if LOG_BINDINGS + extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2))); +#endif }; @@ -167,6 +190,8 @@ public: unsigned int (*imageCount)(); void (*setNewProgramVars)(const ProgramVars&); bool (*inSharedCache)(const char* path); + void (*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath, + const char* errorTargetDylibPath, const char* errorSymbol); #if SUPPORT_OLD_CRT_INITIALIZATION void (*setRunInitialzersOldWay)(); #endif @@ -188,7 +213,6 @@ public: bool bindFlat; bool linkingMainExecutable; bool startedInitializingMainExecutable; - bool noPIE; bool processIsRestricted; bool verboseOpts; bool verboseEnv; @@ -199,7 +223,10 @@ public: bool verboseInit; bool verboseDOF; bool verbosePrebinding; + bool verboseCoreSymbolication; bool verboseWarnings; + bool verboseRPaths; + bool verboseInterposing; }; struct CoalIterator @@ -223,6 +250,14 @@ public: virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0; virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0; + struct InitializerTimingList + { + uintptr_t count; + struct { + ImageLoader* image; + uint64_t initTime; + } images[1]; + }; // constructor is protected, but anyone can delete an image @@ -234,7 +269,7 @@ public: // runInitializers() is normally called in link() but the main executable must // run crt code before initializers - void runInitializers(const LinkContext& context); + void runInitializers(const LinkContext& context, InitializerTimingList& timingInfo); // called after link() forces all lazy pointers to be bound void bindAllLazyPointers(const LinkContext& context, bool recursive); @@ -272,6 +307,9 @@ public: // even if image is deleted, leave segments mapped in bool leaveMapped() { return fLeaveMapped; } + // image resides in dyld shared cache + virtual bool inSharedCache() const = 0; + // checks if the specifed address is within one of this image's segments virtual bool containsAddress(const void* addr) const; @@ -306,7 +344,8 @@ public: virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const = 0; // gets address of implementation (code) of the specified exported symbol - virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor=NULL) const = 0; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor=NULL, bool runResolver=false) const = 0; // gets attributes of the specified exported symbol virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0; @@ -362,7 +401,8 @@ public: virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; // called at runtime when a fast lazily bound function is first called - virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0; + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) = 0; // calls termination routines (e.g. C++ static destructors for image) virtual void doTermination(const LinkContext& context) = 0; @@ -414,6 +454,13 @@ public: virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + // if the image contains interposing functions, register them + virtual void registerInterposing() = 0; + + // when resolving symbols look in subImage if symbol can't be found + void reExport(ImageLoader* subImage); + + void applyInterposing(const LinkContext& context); dyld_image_states getState() { return (dyld_image_states)fState; } @@ -433,7 +480,7 @@ public: void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast - static void printStatistics(unsigned int imageCount); + static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo); // used with DYLD_IMAGE_SUFFIX static void addSuffix(const char* path, const char* suffix, char* result); @@ -481,9 +528,16 @@ protected: LibraryInfo info; bool required; bool reExported; + bool upward; }; + struct InterposeTuple { + uintptr_t replacement; + ImageLoader* replacementImage; // don't apply replacement to this image + uintptr_t replacee; + }; + typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars); typedef void (*Terminator)(void); @@ -492,7 +546,8 @@ protected: unsigned int libraryCount() const { return fLibraryCount; } virtual ImageLoader* libImage(unsigned int) const = 0; virtual bool libReExported(unsigned int) const = 0; - virtual void setLibImage(unsigned int, ImageLoader*, bool) = 0; + virtual bool libIsUpward(unsigned int) const = 0; + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0; // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. @@ -504,8 +559,9 @@ protected: void recursiveRebase(const LinkContext& context); void recursiveBind(const LinkContext& context, bool forceLazysBound); void weakBind(const LinkContext& context); + void recursiveApplyInterposing(const LinkContext& context); void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); - void recursiveInitialization(const LinkContext& context, mach_port_t this_thread); + void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, ImageLoader::InitializerTimingList&); // fill in information about dependent libraries (array length is fLibraryCount) virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; @@ -525,8 +581,11 @@ protected: // if image has any dtrace DOF sections, append them to list to be registered virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) = 0; + // do interpose + virtual void doInterpose(const LinkContext& context) = 0; + // run any initialization routines in this image - virtual void doInitialization(const LinkContext& context) = 0; + virtual bool doInitialization(const LinkContext& context) = 0; // return if this image has termination routines virtual bool needsTermination() = 0; @@ -560,7 +619,6 @@ protected: static uintptr_t fgNextPIEDylibAddress; static uint32_t fgImagesWithUsedPrebinding; static uint32_t fgImagesUsedFromSharedCache; - static uint32_t fgImagesRequiringNoFixups; static uint32_t fgImagesHasWeakDefinitions; static uint32_t fgImagesRequiringCoalescing; static uint32_t fgTotalRebaseFixups; @@ -578,6 +636,7 @@ protected: static uint64_t fgTotalWeakBindTime; static uint64_t fgTotalDOF; static uint64_t fgTotalInitTime; + static std::vector fgInterposingTuples; const char* fPath; dev_t fDevice; ino_t fInode; @@ -586,7 +645,7 @@ protected: uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image uint32_t fStaticReferenceCount; // count of images that have a fLibraries entry pointing to this image uint32_t fDynamicReferenceCount; // count of images that have a fDynamicReferences entry pointer to this image - std::set* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup + std::vector* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup private: struct recursive_lock { @@ -612,6 +671,7 @@ private: fNeverUnload : 1, // image was statically loaded by main executable fHideSymbols : 1, // ignore this image's exported symbols when linking other images fMatchByInstallName : 1,// look at image's install-path not its load path + fInterposed : 1, fRegisteredDOF : 1, fAllLazyPointersBound : 1, fBeingRemoved : 1, diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 094d56d..b068b7a 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,7 +27,7 @@ #define __eip eip #define __rip rip - +#define __STDC_LIMIT_MACROS #include #include #include @@ -42,13 +42,19 @@ #include #include #include +#include #include "ImageLoaderMachO.h" #include "ImageLoaderMachOCompressed.h" #include "ImageLoaderMachOClassic.h" #include "mach-o/dyld_images.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 // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables #if __LP64__ @@ -82,7 +88,7 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns fReadOnlyImportSegment(false), #endif fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), - fHasInitializers(false), fHasTerminators(false) + fHasInitializers(false), fHasTerminators(false), fGoodFirstSegment(false), fRegisteredAsRequiresCoalescing(false) { fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); @@ -108,11 +114,13 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // 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* compressed, - unsigned int* segCount, unsigned int* libCount) + unsigned int* segCount, unsigned int* libCount, + const linkedit_data_command** codeSigCmd) { *compressed = false; *segCount = 0; *libCount = 0; + *codeSigCmd = NULL; 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* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds); @@ -131,8 +139,12 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: *libCount += 1; break; + case LC_CODE_SIGNATURE: + *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image + break; } uint32_t cmdLength = cmd->cmdsize; cmd = (const struct load_command*)(((char*)cmd)+cmdLength); @@ -163,7 +175,8 @@ ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, bool compressed; unsigned int segCount; unsigned int libCount; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount); + const linkedit_data_command* codeSigCmd; + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); @@ -190,27 +203,29 @@ ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, con bool compressed; unsigned int segCount; unsigned int libCount; - sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount); + const linkedit_data_command* codeSigCmd; + sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, context); + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); else - return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, context); + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); } // create image by using cached mach-o file -ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, const LinkContext& context) +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; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount); + const linkedit_data_command* codeSigCmd; + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, info, segCount, libCount, context); + return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); else - return ImageLoaderMachOClassic::instantiateFromCache(mh, path, info, segCount, libCount, context); + return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); } // create image by copying an in-memory mach-o file @@ -219,7 +234,8 @@ ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, con bool compressed; unsigned int segCount; unsigned int libCount; - sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount); + const linkedit_data_command* sigcmd; + sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, &sigcmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); @@ -239,7 +255,7 @@ void ImageLoaderMachO::parseLoadCmds() #if TEXT_RELOC_SUPPORT // __TEXT segment always starts at beginning of file and contains mach_header and load commands if ( strcmp(segName(i),"__TEXT") == 0 ) { - if ( segHasRebaseFixUps(i) ) + if ( segHasRebaseFixUps(i) && (fSlide != 0) ) fTextSegmentRebases = true; if ( segHasBindFixUps(i) ) fTextSegmentBinds = true; @@ -258,6 +274,7 @@ void ImageLoaderMachO::parseLoadCmds() // keep count of prebound images with weak exports if ( this->participatesInCoalescing() ) { ++fgImagesRequiringCoalescing; + fRegisteredAsRequiresCoalescing = true; if ( this->hasCoalescedExports() ) ++fgImagesHasWeakDefinitions; } @@ -334,6 +351,7 @@ void ImageLoaderMachO::parseLoadCmds() case LC_RPATH: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: // do nothing, just prevent LC_REQ_DYLD exception from occuring break; default: @@ -354,8 +372,8 @@ void ImageLoaderMachO::parseLoadCmds() // for UnmapSegments() to work void ImageLoaderMachO::destroy() { - // keep count of images with weak exports - if ( this->participatesInCoalescing() ) { + // update count of images with weak exports + if ( fRegisteredAsRequiresCoalescing ) { --fgImagesRequiringCoalescing; if ( this->hasCoalescedExports() ) --fgImagesHasWeakDefinitions; @@ -629,36 +647,16 @@ void ImageLoaderMachO::setSlide(intptr_t slide) } #if CODESIGNING_SUPPORT -void ImageLoaderMachO::loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile) +void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile) { - // look for code signature load command - // do this in the read() memory buffer - not in the mapped __TEXT segment - const uint32_t cmd_count = ((macho_header*)fileData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fileData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_CODE_SIGNATURE ) { - const struct linkedit_data_command *sigcmd = (struct linkedit_data_command*) cmd; - // fLinkEditBase is not set up yet, so compute it - const uint8_t* linkEditBase = NULL; - for(unsigned int i=0; i < fSegmentsCount; ++i) { - // set up pointer to __LINKEDIT segment - if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { - linkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); - break; - } - } - fsignatures_t siginfo; - siginfo.fs_file_start=offsetInFatFile; // CD coverage offset - siginfo.fs_blob_start=(void*)(linkEditBase+sigcmd->dataoff); // start of CD in file - siginfo.fs_blob_size=sigcmd->datasize; // size of CD - int result = fcntl(fd, F_ADDSIGS, &siginfo); - if ( result == -1 ) - dyld::log("dyld: code signature failed for %s with errno=%d\n", this->getPath(), errno); - break; // only support one LC_CODE_SIGNATURE - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } + fsignatures_t siginfo; + siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(codeSigCmd->dataoff); // start of CD in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS, &siginfo); + if ( result == -1 ) + dyld::log("dyld: F_ADDFILESIGS failed for %s with errno=%d\n", this->getPath(), errno); + //dyld::log("dyld: registered code signature for %s\n", this->getPath()); } #endif @@ -672,6 +670,52 @@ const char* ImageLoaderMachO::getInstallPath() const return NULL; } +void ImageLoaderMachO::registerInterposing() +{ + // mach-o files advertise interposing by having a __DATA __interpose section + uintptr_t textStart = this->segActualLoadAddress(0); + uintptr_t textEnd = this->segActualEndAddress(0); + // verify that the first segment load command is for a read-only segment + if ( ! fGoodFirstSegment ) + return; + 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)) ) { + const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); + const unsigned int count = sect->size / sizeof(InterposeData); + for (uint32_t i=0; i < count; ++i) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = interposeArray[i].replacement; + tuple.replacementImage = this; + tuple.replacee = interposeArray[i].replacee; + // verify that replacement is in this image + if ( (tuple.replacement >= textStart) && (tuple.replacement < textEnd) ) { + 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); + } +} void* ImageLoaderMachO::getMain() const @@ -728,6 +772,7 @@ bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const m case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: return false; case LC_ID_DYLIB: { @@ -756,6 +801,7 @@ void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) lib->info.maxVersion = 0; lib->required = false; lib->reExported = false; + lib->upward = false; } else { uint32_t index = 0; @@ -767,6 +813,7 @@ void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) 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++]; @@ -777,6 +824,7 @@ void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) 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; } @@ -810,6 +858,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorcmd) { case LC_RPATH: + const char* pathToAdd = NULL; const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset; if ( strncmp(path, "@loader_path/", 13) == 0 ) { if ( context.processIsRestricted && (context.mainExecutable == this) ) { @@ -825,7 +874,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorcmdsize); @@ -972,8 +1022,9 @@ void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool segMakeWritable(textSegmentIndex, context); } else { - segProtect(textSegmentIndex, context); + // iPhoneOS requires range to be invalidated before it is made executable sys_icache_invalidate((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); + segProtect(textSegmentIndex, context); } } #endif @@ -1004,14 +1055,27 @@ const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name -uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const +uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver) const { - return this->getSymbolAddress(sym, requestor, context); + return this->getSymbolAddress(sym, requestor, context, runResolver); } -uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const +uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, + const LinkContext& context, bool runResolver) const { - uintptr_t result = exportedSymbolAddress(sym); + uintptr_t result = exportedSymbolAddress(context, sym, runResolver); + // check for interposing overrides + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (result == it->replacee) && (requestor != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld interposing: replace 0x%lX with 0x%lX in %s\n", + it->replacee, it->replacement, this->getPath()); + } + result = it->replacement; + } + } return result; } @@ -1149,8 +1213,11 @@ bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segme } -void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn) +void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* expectedIn) { + // record values for possible use by CrashReporter or Finder + (*context.setErrorStrings)(dyld_error_kind_symbol_missing, referencedFrom, expectedIn, symbol); dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn); } @@ -1196,6 +1263,9 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l ((targetImage != NULL) ? targetImage->getShortName() : "import-missing>"), 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; @@ -1333,22 +1403,22 @@ void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const // lookup _NXArgc sym = this->findExportedSymbol("_NXArgc", false, NULL); if ( sym != NULL ) - vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this); + vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false); // lookup _NXArgv sym = this->findExportedSymbol("_NXArgv", false, NULL); if ( sym != NULL ) - vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); + vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false); // lookup _environ sym = this->findExportedSymbol("_environ", false, NULL); if ( sym != NULL ) - vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); + vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false); // lookup __progname sym = this->findExportedSymbol("___progname", false, NULL); if ( sym != NULL ) - vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this); + vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false); context.setNewProgramVars(vars); } @@ -1357,8 +1427,7 @@ void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const 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() || fInSharedCache) - && (this->getSlide() == 0) + if ( ((this->isPrebindable() && (this->getSlide() == 0)) || fInSharedCache) && this->usesTwoLevelNameSpace() && this->allDependentLibrariesAsWhenPreBound() ) { // allow environment variables to disable prebinding @@ -1382,6 +1451,16 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const void ImageLoaderMachO::doImageInit(const LinkContext& context) { if ( fHasDashInit ) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in first segment for dylibs + if ( this->isDylib() && !fGoodFirstSegment ) { + if ( context.verboseInit ) + dyld::log("dyld: ignoring -init in %s\n", this->getPath()); + return; + } + uintptr_t textStart = this->segActualLoadAddress(0); + uintptr_t textEnd = this->segActualEndAddress(0); +#endif 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; @@ -1389,9 +1468,20 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) switch (cmd->cmd) { case LC_ROUTINES_COMMAND: Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); - if ( context.verboseInit ) - dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in first segment for dylibs + if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) { + if ( context.verboseInit ) + dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath()); + } + else { +#endif + if ( context.verboseInit ) + dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + } +#endif break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -1402,6 +1492,16 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) { if ( fHasInitializers ) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in first segment for dylibs + if ( this->isDylib() && !fGoodFirstSegment ) { + if ( context.verboseInit ) + dyld::log("dyld: ignoring all initializers in %s\n", this->getPath()); + return; + } + uintptr_t textStart = this->segActualLoadAddress(0); + uintptr_t textEnd = this->segActualEndAddress(0); +#endif 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; @@ -1417,14 +1517,25 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) const uint32_t count = sect->size / sizeof(uintptr_t); for (uint32_t i=0; i < count; ++i) { Initializer func = inits[i]; - if ( context.verboseInit ) - dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple, &context.programVars); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // verify initializers are in first segment for dylibs + if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) { + if ( context.verboseInit ) + dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath()); + } + else { +#endif + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + } +#endif } } } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } } } @@ -1466,11 +1577,17 @@ void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector< } -void ImageLoaderMachO::doInitialization(const LinkContext& context) +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() @@ -1516,12 +1633,13 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) } -void ImageLoaderMachO::printStatistics(unsigned int imageCount) +void ImageLoaderMachO::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo) { - ImageLoader::printStatistics(imageCount); + ImageLoader::printStatistics(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/using weak symbols: %u/%u\n", fgImagesHasWeakDefinitions, fgImagesRequiringCoalescing); + dyld::log("total images defining weak symbols: %u\n", fgImagesHasWeakDefinitions); + dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing); } @@ -1557,7 +1675,7 @@ intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) if ( strcmp(segName(i), "__PAGEZERO") == 0 ) continue; if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) - throw "can't map"; + dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i)); } } else { @@ -1573,7 +1691,9 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad vm_size_t size = length; // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either if ( fgNextPIEDylibAddress != 0 ) { - addr = fgNextPIEDylibAddress + (arc4random() & 0x3) * 4096; // add small random padding between dylibs + // add small (0-3 pages) random padding between dylibs + addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*4096; + //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED); if ( r == KERN_SUCCESS ) { fgNextPIEDylibAddress = addr + size; @@ -1606,14 +1726,30 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF intptr_t slide = this->assignSegmentAddresses(context); if ( context.verboseMapping ) dyld::log("dyld: Mapping %s\n", this->getPath()); + // verify that the first segment load command is for a r-x segment + // that starts at begining of file and is larger than all load commands + uintptr_t firstSegMappedStart = segPreferredLoadAddress(0) + slide; + uintptr_t firstSegMappedEnd = firstSegMappedStart + this->segSize(0); + if ( (this->segLoadCommand(0)->initprot == (VM_PROT_EXECUTE|VM_PROT_READ)) + && (this->segFileOffset(0) == 0) + && (this->segFileSize(0) != 0) + && (this->segSize(0) > ((macho_header*)fMachOData)->sizeofcmds) ) { + fGoodFirstSegment = true; + } // 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); - void* requestedLoadAddress = (void*)(segPreferredLoadAddress(i) + slide); + uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide; + // verify other segments map after first + if ( (i != 0) && (requestedLoadAddress < firstSegMappedEnd) ) + fGoodFirstSegment = false; int protection = 0; if ( !segUnaccessible(i) ) { - if ( segExecutable(i) ) + // If has text-relocs, don't set x-bit initially. + // Instead set it later after text-relocs have been done. + // The iPhone OS does not like it when you make executable code writable. + if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) ) protection |= PROT_EXEC; if ( segReadable(i) ) protection |= PROT_READ; @@ -1631,19 +1767,20 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF 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 = mmap(requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); + void* loadAddress = mmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); if ( loadAddress == ((void*)(-1)) ) { dyld::throwf("mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", - errno, (uintptr_t)requestedLoadAddress, (uintptr_t)size, segName(i), getPath()); + errno, 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), (uintptr_t)requestedLoadAddress, (uintptr_t)((char*)requestedLoadAddress+size-1), + 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); } @@ -1689,8 +1826,10 @@ void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::Link 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 ) - throw "can't set vm permissions for mapped segment"; + 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' : '.' ); @@ -1703,11 +1842,13 @@ void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader: vm_size_t size = segSize(segIndex); const bool setCurrentPermissions = false; vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ; - if ( segExecutable(segIndex) ) + 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 ) - throw "can't set vm permissions for mapped segment"; + 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' : '.' ); diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index 5d735bf..a2b657a 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -43,10 +43,11 @@ public: static ImageLoader* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context); static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, const LinkContext& context); - static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, const LinkContext& context); + static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context); static ImageLoader* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context); + bool inSharedCache() const { return fInSharedCache; } const char* getInstallPath() const; virtual void* getMain() const; virtual const struct mach_header* machHeader() const; @@ -54,7 +55,8 @@ public: virtual const void* getEnd() const; virtual bool hasCoalescedExports() const; virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const; - virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver) const; virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const; virtual const char* getExportedSymbolName(const Symbol* sym) const; virtual uint32_t getExportedSymbolCount() const; @@ -75,7 +77,7 @@ public: virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0; virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0; virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; - virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 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(); virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length); @@ -96,9 +98,10 @@ public: virtual uintptr_t segActualLoadAddress(unsigned int) const; virtual uintptr_t segPreferredLoadAddress(unsigned int) const; virtual uintptr_t segActualEndAddress(unsigned int) const; + virtual void registerInterposing(); - static void printStatistics(unsigned int imageCount); + static void printStatistics(unsigned int imageCount, const InitializerTimingList&); protected: ImageLoaderMachO(const ImageLoaderMachO&); @@ -108,7 +111,7 @@ protected: void operator=(const ImageLoaderMachO&); - virtual void setDyldInfo(const dyld_info_command*) = 0; + virtual void setDyldInfo(const struct dyld_info_command*) = 0; virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) = 0; virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0; virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; @@ -116,7 +119,7 @@ protected: virtual void rebase(const LinkContext& context) = 0; virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0; virtual bool containsSymbol(const void* addr) const = 0; - virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const = 0; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const = 0; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0; virtual const char* exportedSymbolName(const Symbol* symbol) const = 0; virtual unsigned int exportedSymbolCount() const = 0; @@ -136,7 +139,7 @@ protected: virtual void doRebase(const LinkContext& context); virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; virtual void doBindJustLazies(const LinkContext& context) = 0; - virtual void doInitialization(const LinkContext& context); + virtual bool doInitialization(const LinkContext& context); virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs); virtual bool needsTermination(); virtual bool segmentsMustSlideTogether() const; @@ -150,10 +153,11 @@ protected: void destroy(); static void sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, - unsigned int* segCount, unsigned int* libCount); + unsigned int* segCount, unsigned int* libCount, + const linkedit_data_command** codeSigCmd); static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); #if CODESIGNING_SUPPORT - void loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile); + void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile); #endif const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; void parseLoadCmds(); @@ -170,7 +174,8 @@ protected: void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); void UnmapSegments(); - void __attribute__((noreturn)) throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn); + void __attribute__((noreturn)) throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* expectedIn); void doImageInit(const LinkContext& context); void doModInitFunctions(const LinkContext& context); void setupLazyPointerHandler(const LinkContext& context); @@ -183,8 +188,10 @@ protected: void preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context); + void doInterpose(const LinkContext& context) = 0; bool hasReferencesToWeakSymbols() const; - uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const; + uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, + const LinkContext& context, bool runResolver) const; static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); protected: @@ -210,7 +217,9 @@ protected: fHasDOFSections : 1, fHasDashInit : 1, fHasInitializers : 1, - fHasTerminators : 1; + fHasTerminators : 1, + fGoodFirstSegment : 1, + fRegisteredAsRequiresCoalescing : 1; // Loading MH_DYLIB_STUB causing coalescable miscount static uint32_t fgSymbolTableBinarySearchs; diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp index 182047e..33b055d 100644 --- a/src/ImageLoaderMachOClassic.cpp +++ b/src/ImageLoaderMachOClassic.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -106,11 +106,12 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons image->setSlide(slide); // for PIE record end of program, to know where to start loading dylibs - if ( (mh->flags & MH_PIE) && !context.noPIE ) + if ( slide != 0 ) fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); image->instantiateFinish(context); - + image->setMapped(context); + #if __i386__ // kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding if ( image->fReadOnlyImportSegment ) { @@ -138,21 +139,26 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons // create image by mapping in a mach-o file ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, - unsigned int segCount, unsigned int libCount, const LinkContext& context) + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context) { ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart((macho_header*)fileData, path, segCount, libCount); try { // record info about file image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); - // mmap segments - image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); - #if CODESIGNING_SUPPORT - // if this code is signed, validate the signature before accessing any mapped pages - image->loadCodeSignature(fileData, fd, offsetInFat); + // if this image is code signed, let kernel validate signature before mapping any pages from image + if ( codeSigCmd != NULL ) + image->loadCodeSignature(codeSigCmd, fd, offsetInFat); #endif + // mmap segments + image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); + + // finish up + image->instantiateFinish(context); + // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path const char* installName = image->getInstallPath(); if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) @@ -168,13 +174,14 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char else image->setPath(path); + // make sure path is stable before recording in dyld_all_image_infos + image->setMapped(context); + // pre-fetch content of __DATA segment for faster launches // don't do this on prebound images or if prefetching is disabled if ( !context.preFetchDisabled && !image->isPrebindable()) image->preFetchDATA(fd, offsetInFat, context); - // finish up - image->instantiateFinish(context); } catch (...) { // ImageLoader::setMapped() can throw an exception to block loading of image @@ -187,7 +194,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char } // create image by using cached mach-o file -ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, unsigned int segCount, unsigned int libCount, const LinkContext& context) { ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); @@ -208,6 +215,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const mac } image->instantiateFinish(context); + image->setMapped(context); } catch (...) { // ImageLoader::setMapped() can throw an exception to block loading of image @@ -240,6 +248,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const ch image->setPath(moduleName); image->instantiateFinish(context); + image->setMapped(context); } catch (...) { // ImageLoader::setMapped() can throw an exception to block loading of image @@ -278,9 +287,6 @@ void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide this->parseLoadCmds(); - - // notify state change - this->setMapped(context); } ImageLoaderMachOClassic::~ImageLoaderMachOClassic() @@ -298,8 +304,8 @@ uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const { const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); - // mask off low bit - return (ImageLoader*)(images[libIndex] & (-2)); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); } bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const @@ -309,13 +315,22 @@ bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const return ((images[libIndex] & 1) != 0); } +bool ImageLoaderMachOClassic::libIsUpward(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // upward flag is second bit + return ((images[libIndex] & 2) != 0); +} + -void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported) +void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) { uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); uintptr_t value = (uintptr_t)image; if ( reExported ) value |= 1; + if ( upward ) + value |= 2; images[libIndex] = value; } @@ -506,9 +521,12 @@ void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, u return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); #if SPLIT_SEG_SHARED_REGION_SUPPORT - // try to map into shared region at preferred address - if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0) - return; + // don't map split-seg dylibs into shared region if shared cache is in use + if ( ! context.dyldLoadedAtSameAddressNeededBySharedCache ) { + // try to map into shared region at preferred address + if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0) + return; + } // if there is a problem, fall into case where we map file somewhere outside the shared region #endif @@ -779,6 +797,7 @@ void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& conte void ImageLoaderMachOClassic::rebase(const LinkContext& context) { + CRSetCrashLogMessage2(this->getPath()); register const uintptr_t slide = this->fSlide; const uintptr_t relocBase = this->getRelocBase(); @@ -885,6 +904,7 @@ void ImageLoaderMachOClassic::rebase(const LinkContext& context) // update stats fgTotalRebaseFixups += fDynamicInfo->nlocrel; + CRSetCrashLogMessage2(NULL); } @@ -986,7 +1006,7 @@ bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const } -uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const Symbol* symbol) const +uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const { const struct macho_nlist* sym = (macho_nlist*)symbol; uintptr_t result = sym->n_value + fSlide; @@ -1066,9 +1086,9 @@ bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* sy return false; } -uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context) const +uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const { - return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context); + return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver); } uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, @@ -1085,7 +1105,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, // flat lookup if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) { // is a multi-module private_extern internal reference that the linker did not optimize away - uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context); + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); *foundIn = this; return addr; } @@ -1107,7 +1127,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, // if reference is weak_import, then it is ok, just return 0 return 0; } - throwSymbolNotFound(symbolName, this->getPath(), "flat namespace"); + throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); } else { // symbol requires searching images with coalesced symbols (not done during prebinding) @@ -1118,14 +1138,14 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, this->addDynamicReference(*foundIn); return (*foundIn)->getExportedSymbolAddress(sym, context, this); } - //throwSymbolNotFound(symbolName, this->getPath(), "coalesced namespace"); + //throwSymbolNotFound(context, symbolName, this->getPath(), "coalesced namespace"); //dyld::log("dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName); } // if this is a real definition (not an undefined symbol) there is no ordinal if ( (undefinedSymbol->n_type & N_TYPE) == N_SECT ) { // static linker should never generate this case, but if it does, do something sane - uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context); + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); *foundIn = this; return addr; } @@ -1152,7 +1172,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, if ( context.flatExportFinder(symbolName, &sym, foundIn) ) return (*foundIn)->getExportedSymbolAddress(sym, context, this); - throwSymbolNotFound(symbolName, this->getPath(), "dynamic lookup"); + throwSymbolNotFound(context, symbolName, this->getPath(), "dynamic lookup"); } else if ( ord <= libraryCount() ) { target = libImage(ord-1); @@ -1178,7 +1198,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) { // don't know why the static linker did not eliminate the internal reference to a private extern definition *foundIn = this; - return this->getSymbolAddress(undefinedSymbol, context); + return this->getSymbolAddress(undefinedSymbol, context, false); } else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { // if definition not found and reference is weak return 0 @@ -1186,7 +1206,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, } // nowhere to be found - throwSymbolNotFound(symbolName, this->getPath(), target->getPath()); + throwSymbolNotFound(context, symbolName, this->getPath(), target->getPath()); } } @@ -1412,7 +1432,7 @@ uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, cons return targetAddr; } -uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) +uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) { throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; } @@ -1557,7 +1577,15 @@ uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, cons } const struct macho_nlist* sym = &fSymbolTable[symbol_index]; //dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath()); +#if __arm__ + // processor assumes code address with low bit set is thumb + if (sym->n_desc & N_ARM_THUMB_DEF) + return (sym->n_value | 1) + fSlide ; + else + return sym->n_value + fSlide; +#else return sym->n_value + fSlide; +#endif } @@ -1622,7 +1650,7 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t #if __arm__ // if weak and thumb subtract off extra thumb bit if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) - addend += 1; + addend &= -2; #endif } } @@ -1645,6 +1673,11 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t // to be definition address plus addend //dyld::log("weak def, initialValue=0x%lX, undefAddr=0x%lX\n", initialValue, undefinedSymbol->n_value+fSlide); addend = initialValue - (undefinedSymbol->n_value + fSlide); + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + addend &= -2; + #endif } else { // nothing fixed up yet, addend is just initial value @@ -1905,6 +1938,7 @@ void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) { + CRSetCrashLogMessage2(this->getPath()); #if __i386__ this->initializeLazyStubs(context); #endif @@ -1918,6 +1952,12 @@ void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazys // no valid prebinding, so bind symbols. // values bound by name are stored two different ways in classic mach-o: + #if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); + #endif + // 1) external relocations are used for data initialized to external symbols this->doBindExternalRelocations(context); @@ -1925,10 +1965,17 @@ void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazys // if this image is in the shared cache, there is no way to reset the lazy pointers, so bind them now this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache); + #if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, false); + #endif } // set up dyld entry points in image this->setupLazyPointerHandler(context); + + CRSetCrashLogMessage2(NULL); } void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) @@ -1937,6 +1984,100 @@ void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) this->bindIndirectSymbolPointers(context, false, true); } +void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto: %s\n", fgInterposingTuples.size(), this->getPath()); + + // scan indirect symbols + 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) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { + const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (uint32_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (symbolPointers[pointerIndex] == it->replacee) && (this != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", + &symbolPointers[pointerIndex], it->replacee, it->replacement, this->getPath()); + } + symbolPointers[pointerIndex] = it->replacement; + } + } + } + } + #if __i386__ + // i386 has special self-modifying stubs that might be prebound to "JMP rel32" that need checking + else if ( (type == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { + // check each jmp entry in this section + uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); + uint8_t* end = start + sect->size; + for (uint8_t* entry = start; entry < end; entry += 5) { + if ( entry[0] == 0xE9 ) { // 0xE9 == JMP + uint32_t rel32 = *((uint32_t*)&entry[1]); // assume unaligned load of uint32_t is ok + uint32_t target = (uint32_t)&entry[5] + rel32; + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (it->replacee == target) && (this != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: interposing: at %p replace JMP 0x%lX with JMP 0x%lX in %s\n", + &entry[1], it->replacee, it->replacement, this->getPath()); + } + uint32_t newRel32 = it->replacement - (uint32_t)&entry[5]; + *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok + } + } + } + } + } + #endif + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // scan external relocations + const uintptr_t relocBase = this->getRelocBase(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (*location == it->replacee) && (this != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", + location, it->replacee, it->replacement, this->getPath()); + } + *location = it->replacement; + } + } + } + break; + } + } + } +} + + const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const { uintptr_t targetAddress = (uintptr_t)addr - fSlide; @@ -1970,7 +2111,14 @@ const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const v } } if ( bestSymbol != NULL ) { +#if __arm__ + if (bestSymbol->n_desc & N_ARM_THUMB_DEF) + *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); + else + *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#else *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#endif return &fStrings[bestSymbol->n_un.n_strx]; } return NULL; diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h index 6a339ec..5d76650 100644 --- a/src/ImageLoaderMachOClassic.h +++ b/src/ImageLoaderMachOClassic.h @@ -41,8 +41,9 @@ public: unsigned int segCount, unsigned int libCount, const LinkContext& context); static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, - unsigned int segCount, unsigned int libCount, const LinkContext& context); - static ImageLoaderMachOClassic* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, unsigned int segCount, unsigned int libCount, const LinkContext& context); static ImageLoaderMachOClassic* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, unsigned int segCount, unsigned int libCount, const LinkContext& context); @@ -51,11 +52,12 @@ public: virtual ImageLoader* libImage(unsigned int) const; virtual bool libReExported(unsigned int) const; - virtual void setLibImage(unsigned int, ImageLoader*, bool); + 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 uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context); + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder); virtual bool incrementCoalIterator(CoalIterator&); @@ -63,6 +65,7 @@ public: virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context); protected: + virtual void doInterpose(const LinkContext& context); virtual void setDyldInfo(const dyld_info_command*) {} virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*); virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; @@ -71,7 +74,7 @@ protected: virtual void rebase(const LinkContext& context); virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; virtual bool containsSymbol(const void* addr) const; - virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; virtual const char* exportedSymbolName(const Symbol* symbol) const; virtual unsigned int exportedSymbolCount() const; @@ -99,7 +102,7 @@ private: static bool symbolIsWeakDefinition(const struct macho_nlist* symbol); uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel, bool dontCoalesce, const ImageLoader **foundIn); - uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context) const; + uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context, bool runResolver) const; bool isAddrInSection(uintptr_t addr, uint8_t sectionIndex); void doBindExternalRelocations(const LinkContext& context); uintptr_t bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 7d3812b..dda3e8a 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -67,14 +67,13 @@ static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end) uint64_t slice = *p & 0x7f; - if (bit >= 64 || slice << bit >> bit != slice) - dyld::throwf("uleb128 too big"); + if (bit > 63) + dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); else { result |= (slice << bit); bit += 7; } - } - while (*p++ & 0x80); + } while (*p++ & 0x80); return result; } @@ -108,11 +107,12 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl image->setSlide(slide); // for PIE record end of program, to know where to start loading dylibs - if ( (mh->flags & MH_PIE) && !context.noPIE ) + if ( slide != 0 ) fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); image->setNeverUnload(); image->instantiateFinish(context); + image->setMapped(context); if ( context.verboseMapping ) { dyld::log("dyld: Main executable mapped %s\n", path); @@ -131,7 +131,8 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl // create image by mapping in a mach-o file ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, - unsigned int segCount, unsigned int libCount, const LinkContext& context) + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context) { ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); @@ -139,6 +140,12 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons // record info about file image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + #if CODESIGNING_SUPPORT + // if this image is code signed, let kernel validate signature before mapping any pages from image + if ( codeSigCmd != NULL ) + image->loadCodeSignature(codeSigCmd, fd, offsetInFat); + #endif + // mmap segments image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); @@ -165,6 +172,9 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons else image->setPath(path); + // make sure path is stable before recording in dyld_all_image_infos + image->setMapped(context); + // pre-fetch content of __DATA and __LINKEDIT segment for faster launches // don't do this on prebound images or if prefetching is disabled if ( !context.preFetchDisabled && !image->isPrebindable()) { @@ -183,8 +193,9 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons } // create image by using cached mach-o file -ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, - unsigned int segCount, unsigned int libCount, const LinkContext& context) +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, + const struct stat& info, unsigned int segCount, + unsigned int libCount, const LinkContext& context) { ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); try { @@ -193,7 +204,9 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(con // remember this is from shared cache and cannot be unloaded image->fInSharedCache = true; + image->fGoodFirstSegment = true; image->setNeverUnload(); + image->setSlide(slide); // segments already mapped in cache if ( context.verboseMapping ) { @@ -204,6 +217,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(con } image->instantiateFinish(context); + image->setMapped(context); } catch (...) { // ImageLoader::setMapped() can throw an exception to block loading of image @@ -236,6 +250,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(co image->setPath(moduleName); image->instantiateFinish(context); + image->setMapped(context); } catch (...) { // ImageLoader::setMapped() can throw an exception to block loading of image @@ -281,9 +296,6 @@ void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide this->parseLoadCmds(); - - // notify state change - this->setMapped(context); } uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const @@ -295,8 +307,8 @@ uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const { const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); - // mask off low bit - return (ImageLoader*)(images[libIndex] & (-2)); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); } bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const @@ -306,13 +318,22 @@ bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const return ((images[libIndex] & 1) != 0); } +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 + return ((images[libIndex] & 2) != 0); +} + -void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported) +void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward) { uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); uintptr_t value = (uintptr_t)image; if ( reExported ) value |= 1; + if ( upward ) + value |= 2; images[libIndex] = value; } @@ -364,7 +385,7 @@ void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int ad const char* adstr = "sequential"; if ( advise == MADV_FREE ) adstr = "free"; - dyld::log("%18s %s 0x%0lX -> 0x%0lX\n", "__LINKEDIT", adstr, start, end-1); + dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); } } @@ -396,6 +417,7 @@ void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintpt void ImageLoaderMachOCompressed::rebase(const LinkContext& context) { + CRSetCrashLogMessage2(this->getPath()); const uintptr_t slide = this->fSlide; const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; const uint8_t* const end = &start[fDyldInfo->rebase_size]; @@ -481,70 +503,117 @@ void ImageLoaderMachOCompressed::rebase(const LinkContext& context) free((void*)msg); throw newMsg; } + CRSetCrashLogMessage2(NULL); } - - - -const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const +// +// This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol() +// to enable it to be re-written in assembler if needed. +// +const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) { - //dyld::log("findExportedSymbolCompressed(%s) in %s\n", symbol, this->getShortName()); - if ( fDyldInfo->export_size == 0 ) - return NULL; - ++ImageLoaderMachO::fgSymbolTrieSearchs; - const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; - const uint8_t* end = &start[fDyldInfo->export_size]; const uint8_t* p = start; - const char* s = symbol; - do { - const uint8_t terminalSize = *p++; - const uint8_t* children = p + terminalSize; + while ( p != NULL ) { + uint32_t terminalSize = *p++; + if ( terminalSize > 127 ) { + // except for re-export-with-rename, all terminal sizes fit in one byte + --p; + terminalSize = read_uleb128(p, end); + } if ( (*s == '\0') && (terminalSize != 0) ) { - // found match, return pointer to terminal part of node - //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); - if ( foundIn != NULL ) - *foundIn = (ImageLoader*)this; - return (Symbol*)p; + //dyld::log("trieWalk(%p) returning %p\n", start, p); + return p; } - const uint8_t childrenCount = *children++; - const uint8_t* e = children; - const uint8_t* newNode = NULL; - for (uint8_t i=0; i < childrenCount; ++i) { + const uint8_t* children = p + terminalSize; + //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children); + uint8_t childrenRemaining = *children++; + p = children; + uint32_t nodeOffset = 0; + for (; childrenRemaining > 0; --childrenRemaining) { const char* ss = s; + //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); bool wrongEdge = false; - //dyld::log("findExportedSymbol() looking at edge %s for match to %s\n", e, s); // scan whole edge to get to next edge // if edge is longer than target symbol name, don't read past end of symbol name - while ( *e != '\0' ) { + char c = *p; + while ( c != '\0' ) { if ( !wrongEdge ) { - if ( *e != *ss++ ) + if ( c != *ss ) wrongEdge = true; + ++ss; } - ++e; + ++p; + c = *p; } if ( wrongEdge ) { // advance to next child - ++e; - read_uleb128(e, end); + ++p; // skip over zero terminator + // skip over uleb128 until last byte is found + while ( (*p & 0x80) != 0 ) + ++p; + ++p; // skil over last byte of uleb128 } else { - // the symbol so far matches this edge (child) + // the symbol so far matches this edge (child) // so advance to the child's node - ++e; - uint32_t nodeOffset = read_uleb128(e, end); - newNode = &start[nodeOffset]; + ++p; + nodeOffset = read_uleb128(p, end); s = ss; - //dyld::log("findExportedSymbol() found matching edge advancing to node 0x%x\n", nodeOffset); + //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset); break; } } - if ( newNode != NULL ) - p = newNode; + if ( nodeOffset != 0 ) + p = &start[nodeOffset]; + else + p = NULL; + } + //dyld::log("trieWalk(%p) return NULL\n", start); + return NULL; +} + + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const +{ + //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName()); + if ( fDyldInfo->export_size == 0 ) + return NULL; +#if LOG_BINDINGS + dyld::logBindings("%s: %s\n", this->getShortName(), symbol); +#endif + ++ImageLoaderMachO::fgSymbolTrieSearchs; + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; + const uint8_t* end = &start[fDyldInfo->export_size]; + const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); + if ( foundNodeStart != NULL ) { + const uint8_t* p = foundNodeStart; + const uint32_t flags = read_uleb128(p, end); + // found match, return pointer to terminal part of node + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uint32_t ordinal = read_uleb128(p, end); + const char* importedName = (char*)p; + if ( importedName[0] == '\0' ) + importedName = symbol; + if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { + const ImageLoader* reexportedFrom = libImage(ordinal-1); + //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); + return reexportedFrom->findExportedSymbol(importedName, true, foundIn); + } + else { + //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s", + // ordinal, libraryCount(), symbol, this->getPath()); + } + } else { - //dyld::log("findExportedSymbol(%s) in %s failed\n", symbol, this->getShortName()); - return NULL; + //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p); + if ( foundIn != NULL ) + *foundIn = (ImageLoader*)this; + // return pointer to terminal part of node + return (Symbol*)foundNodeStart; } - } while ( true ); + } + return NULL; } @@ -556,18 +625,35 @@ bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const } -uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const Symbol* symbol) const +uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const { const uint8_t* exportNode = (uint8_t*)symbol; const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) throw "symbol is not in trie"; + //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); uint32_t flags = read_uleb128(exportNode, exportTrieEnd); - if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) { + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + read_uleb128(exportNode, exportTrieEnd); // skip over stub + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); + uintptr_t result = (*resolver)(); + if ( context.verboseBind ) + dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); + return result; + } + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + } + else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) { + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + } else - throw "unsupported exported symbol kind"; + dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); } bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const @@ -614,36 +700,37 @@ const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) -uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, const ImageLoader** foundIn) +uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, + bool runResolver, const ImageLoader** foundIn) { const Symbol* sym; if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) this->addDynamicReference(*foundIn); - return (*foundIn)->getExportedSymbolAddress(sym, context, this); + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); } // if a bundle is loaded privately the above will not find its exports if ( this->isBundle() && this->hasHiddenExports() ) { // look in self for needed symbol sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn); if ( sym != NULL ) - return (*foundIn)->getExportedSymbolAddress(sym, context, this); + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); } if ( weak_import ) { // definition can't be found anywhere, ok because it is weak, just return 0 return 0; } - throwSymbolNotFound(symbolName, this->getPath(), "flat namespace"); + throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); } uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, - const char* symbolName, const ImageLoader** foundIn) + const char* symbolName, bool runResolver, const ImageLoader** foundIn) { // two level lookup const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn); if ( sym != NULL ) { - return (*foundIn)->getExportedSymbolAddress(sym, context, this); + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); } if ( weak_import ) { @@ -652,13 +739,13 @@ uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context } // nowhere to be found - throwSymbolNotFound(symbolName, this->getPath(), targetImage->getPath()); + throwSymbolNotFound(context, symbolName, this->getPath(), targetImage->getPath()); } uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, - LastLookup* last) + LastLookup* last, bool runResolver) { *targetImage = NULL; @@ -675,7 +762,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); uintptr_t symbolAddress; if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { - symbolAddress = this->resolveFlat(context, symbolName, weak_import, targetImage); + symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); } else { if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { @@ -706,7 +793,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const } } else { - symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, targetImage); + symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage); } } @@ -723,13 +810,14 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const } uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, - uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, LastLookup* last) + uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, + LastLookup* last, bool runResolver) { const ImageLoader* targetImage; uintptr_t symbolAddress; // resolve symbol - symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last); + symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver); // do actual update return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); @@ -746,28 +834,50 @@ void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintp void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) { + CRSetCrashLogMessage2(this->getPath()); + // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) if ( this->usablePrebinding(context) ) { // don't need to bind } else { + + #if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); + #endif + // run through all binding opcodes eachBind(context, &ImageLoaderMachOCompressed::bindAt); - // if this image is in the shared cache, but depends on someting no longer in the shared cache, + #if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, false); + #endif + + // 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 ) + if ( forceLazysBound || fInSharedCache ) this->doBindJustLazies(context); + + // this image is in cache, but something below it is not. If + // this image has lazy pointer to a resolver function, then + // the stub may have been altered to point to a shared lazy pointer. + if ( fInSharedCache ) + this->updateOptimizedLazyPointers(context); + + // tell kernel we are done with chunks of LINKEDIT + if ( !context.preFetchDisabled ) + this->markFreeLINKEDIT(context); } // set up dyld entry points in image // do last so flat main executables will have __dyld or __program_vars set up this->setupLazyPointerHandler(context); - - // tell kernel we are done with chunks of LINKEDIT - if ( !context.preFetchDisabled ) - this->markFreeLINKEDIT(context); + CRSetCrashLogMessage2(NULL); } @@ -844,19 +954,19 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl case BIND_OPCODE_DO_BIND: if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); address += sizeof(intptr_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); address += read_uleb128(p, end) + sizeof(intptr_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); address += immediate*sizeof(intptr_t) + sizeof(intptr_t); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: @@ -865,7 +975,7 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl for (uint32_t i=0; i < count; ++i) { if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); address += skip + sizeof(intptr_t); } break; @@ -946,7 +1056,7 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h case BIND_OPCODE_DO_BIND: if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "lazy forced", NULL); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "lazy forced", NULL, true); address += sizeof(intptr_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: @@ -1039,12 +1149,25 @@ uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, c } -uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) +uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) { + // race condition with flat-namespace lazy binding + if ( this->usesTwoLevelNameSpace() ) { + // two-level namespace lookup does not require lock because dependents can't be unloaded before this image + } + else { + // acquire dyld global lock + if ( lock != NULL ) + lock(); + } + const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; - if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) - throw "fast lazy bind offset out of range"; + if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) { + dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s", + lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath()); + } uint8_t type = BIND_TYPE_POINTER; uintptr_t address = 0; @@ -1094,7 +1217,9 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI address = segActualLoadAddress(immediate) + read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: - result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); + + + result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); break; case BIND_OPCODE_SET_ADDEND_SLEB: case BIND_OPCODE_ADD_ADDR_ULEB: @@ -1105,6 +1230,12 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI dyld::throwf("bad lazy bind opcode %d", *p); } } + + if ( !this->usesTwoLevelNameSpace() ) { + // release dyld global lock + if ( unlock != NULL ) + unlock(); + } return result; } @@ -1290,6 +1421,35 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt this->addDynamicReference(targetImage); } +uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t value = *fixupLocation; + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (value == it->replacee) && (this != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", + fixupLocation, it->replacee, it->replacement, this->getPath()); + } + *fixupLocation = it->replacement; + } + } + } + return 0; +} + +void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath()); + + // update prebound symbols + eachBind(context, &ImageLoaderMachOCompressed::interposeAt); + eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt); +} @@ -1354,7 +1514,14 @@ const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, cons } } if ( bestSymbol != NULL ) { +#if __arm__ + if (bestSymbol->n_desc & N_ARM_THUMB_DEF) + *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide); + else + *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#else *closestAddr = (void*)(bestSymbol->n_value + fSlide); +#endif return &symbolTableStrings[bestSymbol->n_un.n_strx]; } return NULL; @@ -1368,3 +1535,96 @@ void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& co } #endif + +#if __arm__ || __x86_64__ +void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr) +{ +#if __arm__ + uint32_t* instructions = (uint32_t*)stub; + // sanity check this is a stub we understand + if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) ) + return; + + void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12)); +#endif +#if __x86_64__ + // sanity check this is a stub we understand + if ( (stub[0] != 0xFF) || (stub[1] != 0x25) ) + return; + int32_t ripOffset = *((int32_t*)(&stub[2])); + void** lazyPointerAddr = (void**)(ripOffset + stub + 6); +#endif + + // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) + if ( lazyPointerAddr != originalLazyPointerAddr ) { + // copy newly re-bound lazy pointer value to shared lazy pointer + *lazyPointerAddr = *originalLazyPointerAddr; + } +} +#endif + + +// overriding shared cache dylibs with resolvers fails +void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) +{ +#if __arm__ || __x86_64__ + // find stubs and lazy pointer sections + const struct macho_section* stubsSection = NULL; + const struct macho_section* lazyPointerSection = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const macho_header* mh = (macho_header*)fMachOData; + const uint32_t cmd_count = mh->ncmds; + const 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_SYMBOL_STUBS ) + stubsSection = sect; + else if ( type == S_LAZY_SYMBOL_POINTERS ) + lazyPointerSection = sect; + } + } + else if ( cmd->cmd == LC_DYSYMTAB ) { + dynSymbolTable = (struct dysymtab_command*)cmd; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // sanity check + if ( dynSymbolTable == NULL ) + return; + if ( (stubsSection == NULL) || (lazyPointerSection == NULL) ) + return; + const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2; + const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*); + if ( stubsCount != lazyPointersCount ) + return; + const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; + const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; + if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) + return; + if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) + return; + + // walk stubs and lazy pointers + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide); + uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide); + uint8_t* stub = stubsStartAddr; + void** lpa = lazyPointersStartAddr; + for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) { + // sanity check symbol index of stub and lazy pointer match + if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) + continue; + this->updateAlternateLazyPointer(stub, lpa); + } + +#endif +} + + diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h index ae137c0..99c5529 100644 --- a/src/ImageLoaderMachOCompressed.h +++ b/src/ImageLoaderMachOCompressed.h @@ -41,8 +41,9 @@ public: unsigned int segCount, unsigned int libCount, const LinkContext& context); static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, - unsigned int segCount, unsigned int libCount, const LinkContext& context); - static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, const LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, unsigned int segCount, unsigned int libCount, const LinkContext& context); static ImageLoaderMachOCompressed* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, unsigned int segCount, unsigned int libCount, const LinkContext& context); @@ -52,11 +53,12 @@ public: virtual ImageLoader* libImage(unsigned int) const; virtual bool libReExported(unsigned int) const; - virtual void setLibImage(unsigned int, ImageLoader*, bool); + 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 uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context); + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()); virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder); virtual bool incrementCoalIterator(CoalIterator&); @@ -65,6 +67,7 @@ public: protected: + virtual void doInterpose(const LinkContext& context); virtual void setDyldInfo(const dyld_info_command* dyldInfo) { fDyldInfo = dyldInfo; } virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) {} virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } @@ -73,7 +76,7 @@ protected: virtual void rebase(const LinkContext& context); virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; virtual bool containsSymbol(const void* addr) const; - virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; virtual const char* exportedSymbolName(const Symbol* symbol) const; virtual unsigned int exportedSymbolCount() const; @@ -92,7 +95,7 @@ private: typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, uint8_t symboFlags, intptr_t addend, int libraryOrdinal, - const char* msg, LastLookup* last); + const char* msg, LastLookup* last, bool runResolver); void eachLazyBind(const LinkContext& context, bind_handler); void eachBind(const LinkContext& context, bind_handler); @@ -111,18 +114,24 @@ private: const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, - LastLookup* last); + 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, int libraryOrdinal, const ImageLoader** targetImage, LastLookup* last = NULL); - uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, + uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, + LastLookup* last = NULL, bool runResolver=false); + uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, const ImageLoader** foundIn); uintptr_t resolveCoalesced(const LinkContext& context, const char* symbolName, const ImageLoader** foundIn); uintptr_t resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, - const char* symbolName, const ImageLoader** foundIn); - + const char* symbolName, bool runResolver, const ImageLoader** foundIn); + uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver); + static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s); + void updateOptimizedLazyPointers(const LinkContext& context); + void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr); + const struct dyld_info_command* fDyldInfo; }; diff --git a/src/dyld.cpp b/src/dyld.cpp index 3d5bcd0..d86e0b1 100644 --- a/src/dyld.cpp +++ b/src/dyld.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,10 +27,12 @@ #include #include #include +#include #include #include // mach_absolute_time() #include #include +#include #include #include #include @@ -40,6 +42,9 @@ #include #include #include +#include +#include + #ifndef CPU_SUBTYPE_ARM_V5TEJ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) @@ -50,6 +55,13 @@ #ifndef CPU_SUBTYPE_ARM_V7 #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) #endif +#ifndef LC_DYLD_ENVIRONMENT + #define LC_DYLD_ENVIRONMENT 0x27 +#endif + +#ifndef VM_PROT_SLIDE + #define VM_PROT_SLIDE 0x20 +#endif #include #include @@ -90,6 +102,8 @@ extern "C" char * _simple_string(_SIMPLE_STRING __b); extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); extern void removeImageFromAllImages(const mach_header* mh); extern void setAlImageInfosHalt(const char* message, uintptr_t flags); +extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info); +extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]); // magic so CrashReporter logs message extern "C" { @@ -123,6 +137,8 @@ struct EnvironmentVariables { 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; bool DYLD_PRINT_LIBRARIES_POST_LAUNCH; bool DYLD_BIND_AT_LAUNCH; @@ -130,6 +146,7 @@ struct EnvironmentVariables { 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 @@ -149,16 +166,21 @@ struct EnvironmentVariables { // 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 }; typedef std::vector StateHandlers; struct RegisteredDOF { const mach_header* mh; int registrationID; }; +struct DylibOverride { const char* installName; const char* override; }; // all global state static const char* sExecPath = NULL; static const macho_header* sMainExecutableMachHeader = NULL; +#if CPU_SUBTYPES_SUPPORTED static cpu_type_t sHostCPU; static cpu_subtype_t sHostCPUsubtype; +#endif static ImageLoader* sMainExecutable = NULL; static bool sProcessIsRestricted = false; static unsigned int sInsertedDylibCount = 0; @@ -178,8 +200,15 @@ 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; -static const char* sSharedCacheDir = DYLD_SHARED_CACHE_DIR; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + bool gSharedCacheOverridden = false; + static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR; + static bool sDylibsOverrideCache = false; +#else + static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; +#endif #endif ImageLoader::LinkContext gLinkContext; bool gLogAPIs = false; @@ -187,7 +216,7 @@ const struct LibSystemHelpers* gLibSystemHelpers = NULL; #if SUPPORT_OLD_CRT_INITIALIZATION bool gRunInitializersOldWay = false; #endif - +static std::vector sDylibOverrides; // // The MappedRanges structure is used for fast address->image lookups. @@ -306,6 +335,35 @@ void throwf(const char* format, ...) //#define ALTERNATIVE_LOGFILE "/dev/console" static int sLogfile = STDERR_FILENO; +#if LOG_BINDINGS +static int sBindingsLogfile = -1; +static void mysprintf(char* dst, const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + strcpy(dst, _simple_string(buf)); + _simple_sfree(buf); + } + else { + strcpy(dst, "out of memory"); + } +} +void logBindings(const char* format, ...) +{ + if ( sBindingsLogfile != -1 ) { + va_list list; + va_start(list, format); + _simple_vdprintf(sBindingsLogfile, format, list); + va_end(list); + } +} + +#endif + void log(const char* format, ...) { va_list list; @@ -432,10 +490,13 @@ static void notifyAddImageCallbacks(ImageLoader* image) } + // notify gdb about these new images -static const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) { - addImagesToAllImages(infoCount, info); + // don't add images without paths to all-image-info-list + if ( info[0].imageFilePath != NULL ) + addImagesToAllImages(infoCount, info); return NULL; } @@ -487,17 +548,70 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image) } } } + 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 CORESYMBOLICATION_SUPPORT // mach message csdlc about dynamically loaded images - if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { - CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; - if ( connection->is_valid_version() ) { - if ( state == dyld_image_state_terminated ) { + if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath()); + } + if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { + CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; + if ( connection->is_valid_version() ) { coresymbolication_unload_image(connection, image); } } } -#endif +#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_all_image_infos. +// 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->getPath(); + info.imageFileModDate = image->lastModified(); + // add to all_image_infos if not already there + bool found = false; + int existingCount = dyld_all_image_infos.infoArrayCount; + const dyld_image_info* existing = dyld_all_image_infos.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); + } + } } @@ -567,10 +681,17 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image } } #if CORESYMBOLICATION_SUPPORT - if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { - CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; - if ( connection->is_valid_version() ) { - if ( state == dyld_image_state_rebased ) { + if ( state == dyld_image_state_rebased ) { + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + dyld_image_states imageState = (*it)->getState(); + if ( (imageState == dyld_image_state_rebased) || (orLater && (imageState > dyld_image_state_rebased)) ) + dyld::log("dyld core symbolication load notification: %p %s\n", (*it)->machHeader(), (*it)->getPath()); + } + } + if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { + CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; + if ( connection->is_valid_version() ) { // This needs to be captured now uint64_t load_timestamp = mach_absolute_time(); for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -584,6 +705,8 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image #endif } + + static void notifyBatch(dyld_image_states state) { notifyBatchPartial(state, false, NULL); @@ -701,6 +824,10 @@ void removeImage(ImageLoader* image) // notify notifySingle(dyld_image_state_terminated, image); + // dyld should directly call __cxa_finalize() + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 8) ) + (*gLibSystemHelpers->cxa_finalize)(image->machHeader()); + // remove from mapped images table removedMappedRanges(image); @@ -747,19 +874,26 @@ const char* getExecutablePath() void initializeMainExecutable() { + // apply interposing to initial set of images + // do this before making the __IMPORT segments in shared cache read-only + sMainExecutable->applyInterposing(gLinkContext); // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true; // run initialzers for any inserted dylibs + ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()]; const int rootCount = sImageRoots.size(); if ( rootCount > 1 ) { - for(int i=1; i < rootCount; ++i) - sImageRoots[i]->runInitializers(gLinkContext); + for(int i=1; i < rootCount; ++i) { + initializerTimes[0].count = 0; + sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); + } } // run initializers for main executable and everything it brings up - sMainExecutable->runInitializers(gLinkContext); + initializerTimes[0].count = 0; + sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); // register atexit() handler to run terminators in all loaded images when this process exits if ( gLibSystemHelpers != NULL ) @@ -767,7 +901,7 @@ void initializeMainExecutable() // dump info if requested if ( sEnv.DYLD_PRINT_STATISTICS ) - ImageLoaderMachO::printStatistics(sAllImages.size()); + ImageLoaderMachO::printStatistics(sAllImages.size(), initializerTimes[0]); } bool mainExecutablePrebound() @@ -793,12 +927,122 @@ void runTerminators(void* extra) } +// forward reference +static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName); + + // -// Turns a colon separated list of strings -// into a NULL terminated array of string -// pointers. +// 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 const char** parseColonList(const char* list) +static void checkDylibOverride(const char* dylibFile) +{ + //dyld::log("checkDylibOverride('%s')\n", dylibFile); + uint32_t altVersion; + char sysInstallName[PATH_MAX]; + if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) ) { + //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)); + it->override = strdup(dylibFile); + break; + } + } + } + } + if ( ! entryExists ) { + DylibOverride entry; + entry.installName = strdup(sysInstallName); + 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]; + int dirPathLen = strlen(dirPath); + strlcpy(dylibPath, dirPath, PATH_MAX); + 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]; + int dirPathLen = strlen(dirPath); + strlcpy(frameworkPath, dirPath, PATH_MAX); + 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 = 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); + } +} + +// +// 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 }; @@ -817,22 +1061,90 @@ static const char** parseColonList(const char* list) for(const char* s=list; *s != '\0'; ++s) { if (*s == ':') { int len = s-start; - char* str = new char[len+1]; - strncpy(str, start, len); - str[len] = '\0'; - start = &s[1]; - result[index++] = str; + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { + int 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) ) { + int 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; + } } } int len = strlen(start); - char* str = new char[len+1]; - strcpy(str, start); - result[index++] = str; + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { + int 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) ) { + int 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; + } +} + static void paths_expand_roots(const char **paths, const char *key, const char *val) { @@ -898,23 +1210,23 @@ static void printEnvironmentVariables(const char* envp[]) } } -void processDyldEnvironmentVarible(const char* key, const char* value) +void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir) { if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { - sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value); + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FRAMEWORK_PATH); } else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value); + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_FRAMEWORK_PATH); } else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { - sEnv.DYLD_LIBRARY_PATH = parseColonList(value); + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_LIBRARY_PATH); } else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { - sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value); + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH); } else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) { if ( strcmp(value, "/") != 0 ) { - gLinkContext.rootPaths = parseColonList(value); + 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"); @@ -928,7 +1240,7 @@ void processDyldEnvironmentVarible(const char* key, const char* value) gLinkContext.imageSuffix = value; } else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { - sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value); + sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL); } else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { sEnv.DYLD_PRINT_OPTS = true; @@ -989,8 +1301,14 @@ void processDyldEnvironmentVarible(const char* key, const char* value) else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) { gLinkContext.verboseWarnings = true; } - else if ( strcmp(key, "DYLD_NO_PIE") == 0 ) { - gLinkContext.noPIE = 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_SHARED_REGION") == 0 ) { if ( strcmp(value, "private") == 0 ) { @@ -1034,12 +1352,83 @@ void processDyldEnvironmentVarible(const char* key, const char* value) 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); + } + else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH); + } +#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)]; + 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 int keyLen = equals-keyEqualsValue; + 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 + + +#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 + + // // For security, setuid programs ignore DYLD_* environment variables. // Additionally, the DYLD_* enviroment variables are removed @@ -1066,6 +1455,8 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) 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 @@ -1088,7 +1479,7 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) char key[keyLen+1]; strncpy(key, keyEqualsValue, keyLen); key[keyLen] = '\0'; - processDyldEnvironmentVarible(key, value); + processDyldEnvironmentVariable(key, value, NULL); } } else if ( strncmp(keyEqualsValue, "HOME=", 5) == 0 ) { @@ -1096,9 +1487,13 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) } else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) { const char* path = &keyEqualsValue[16]; - sEnv.LD_LIBRARY_PATH = parseColonList(path); + sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL); } } + +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#endif // SUPPORT_LC_DYLD_ENVIRONMENT // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { @@ -1119,34 +1514,38 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) paths_expand_roots(paths, "$HOME", home); sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths; } + +#if SUPPORT_VERSIONED_PATHS + checkVersionedPaths(); +#endif } static void getHostInfo() { -#if 1 +#if CPU_SUBTYPES_SUPPORTED +#if __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; +#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; -#else - size_t valSize = sizeof(sHostCPU); - if (sysctlbyname ("hw.cputype", &sHostCPU, &valSize, NULL, 0) != 0) - throw "sysctlbyname(hw.cputype) failed"; - valSize = sizeof(sHostCPUsubtype); - if (sysctlbyname ("hw.cpusubtype", &sHostCPUsubtype, &valSize, NULL, 0) != 0) - throw "sysctlbyname(hw.cpusubtype) failed"; +#endif #endif } static void checkSharedRegionDisable() { - #if !__LP64__ +#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)) ) { @@ -1154,7 +1553,8 @@ static void checkSharedRegionDisable() if ( gLinkContext.verboseMapping ) dyld::warn("disabling shared region because main executable overlaps\n"); } - #endif +#endif + // iPhoneOS cannot run without shared region } bool validImage(const ImageLoader* possibleImage) @@ -1563,70 +1963,73 @@ static ImageLoader* instantiateFromLoadedImage(const macho_header* mh, uintptr_t throw "main executable not a known format"; } + #if DYLD_SHARED_CACHE_SUPPORT -bool inSharedCache(const char* path) +static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) { if ( sSharedCache != NULL ) { - struct stat stat_buf; - if ( stat(path, &stat_buf) == -1 ) - return false; - +#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 ( stat(path, &statb) == -1 ) + return false; + stat_buf = &statb; + } +#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]; 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 ( strcmp(path, aPath) == 0 ) { + // found image in cache + *mh = (macho_header*)(p->address+sSharedCacheSlide); + *pathInCache = aPath; + *slide = sSharedCacheSlide; + return true; + } +#elif __MAC_OS_X_VERSION_MIN_REQUIRED // check mtime and inode first because it is fast if ( sSharedCacheIgnoreInodeAndTimeStamp - || ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) ) { + || ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ) ) { // mod-time and inode match an image in the shared cache, now check path - const char* pathInCache = (char*)sSharedCache + p->pathFileOffset; - bool cacheHit = (strcmp(path, pathInCache) == 0); + const char* aPath = (char*)sSharedCache + p->pathFileOffset; + bool cacheHit = (strcmp(path, aPath) == 0); if ( ! 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 ( stat(pathInCache, &pathInCacheStatBuf) != -1 ) - cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf.st_dev) && (pathInCacheStatBuf.st_ino == stat_buf.st_ino) ); + if ( 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 + // found image in cache, return info + *mh = (macho_header*)(p->address+sSharedCacheSlide); + //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%p\n", + // *mh, p->address, sSharedCacheSlide, aPath); + *pathInCache = aPath; + *slide = sSharedCacheSlide; return true; } } +#endif } } return false; } -static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char* path) +bool inSharedCache(const char* path) { - if ( sSharedCache != NULL ) { - // 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]; - for( const dyld_cache_image_info* p = start; p != end; ++p) { - // check mtime and inode first because it is fast - if ( sSharedCacheIgnoreInodeAndTimeStamp - || ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) ) { - // mod-time and inode match an image in the shared cache, now check path - const char* pathInCache = (char*)sSharedCache + p->pathFileOffset; - bool cacheHit = (strcmp(path, pathInCache) == 0); - if ( ! 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 ( stat(pathInCache, &pathInCacheStatBuf) != -1 ) - cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf.st_dev) && (pathInCacheStatBuf.st_ino == stat_buf.st_ino) ); - } - if ( cacheHit ) { - // found image in cache, instantiate an ImageLoader with it - return ImageLoaderMachO::instantiateFromCache((macho_header*)(p->address), pathInCache, stat_buf, gLinkContext); - } - } - } - } - return NULL; + const macho_header* mhInCache; + const char* pathInCache; + long slide; + return findInSharedCacheImage(path, NULL, &mhInCache, &pathInCache, &slide); } + #endif static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) @@ -1640,6 +2043,7 @@ static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& cont if ( installPath != NULL) { if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { //dyld::log("duplicate(%s) => %p\n", installPath, anImage); + removeImage(image); ImageLoader::deleteImage(image); return anImage; } @@ -1667,7 +2071,7 @@ static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& cont } // map in file and instantiate an ImageLoader -static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, const LoadContext& context) +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; @@ -1706,10 +2110,20 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, } // try mach-o loader + if ( shortPage ) + throw "file too short"; if ( isCompatibleMachO(firstPage, path) ) { - if ( shortPage ) - throw "file too short"; + // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded + switch ( ((mach_header*)firstPage)->filetype ) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + throw "mach-o, but wrong filetype"; + } + // instantiate an image ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext); @@ -1734,8 +2148,37 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, } -// try to open file -static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, std::vector* exceptions) +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 = 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, std::vector* exceptions) { //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; @@ -1761,40 +2204,111 @@ static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, #if DYLD_SHARED_CACHE_SUPPORT // see if this image is in shared cache - image = findSharedCacheImage(stat_buf, path); - if ( image != NULL ) { + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(path, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) { + image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); return checkandAddImage(image, context); } #endif - - // 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 = dyld::mkstringf("%s: open() failed with errno=%d", path, err); - exceptions->push_back(newMsg); + // 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; + if ( 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; } - return NULL; } + else { + *statErrNo = errno; + } + return NULL; +} - try { - return loadPhase6(file.getFileDescriptor(), stat_buf, path, context); +// try to open file +static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, 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 ( sDylibsOverrideCache ) { + // 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; } - catch (const char* msg) { - const char* newMsg = dyld::mkstringf("%s: %s", path, msg); - exceptions->push_back(newMsg); - free((void*)msg); - return NULL; + // see if this image is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(path, 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 ( 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 ( !sDylibsOverrideCache ) { + // 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 ) { + 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 LoadContext& context) +static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context) { - //dyld::log("%s(%s)\n", __func__ , path); + //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); @@ -1802,12 +2316,13 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context 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 ( 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) { @@ -1818,6 +2333,16 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context } } } + // 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); @@ -1826,37 +2351,46 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context // open or check existing -static ImageLoader* loadPhase5(const char* path, const LoadContext& context, std::vector* exceptions) +static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, 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 loadPhase5open(path, context, exceptions); + return loadPhase5load(path, orgPath, context, exceptions); else - return loadPhase5check(path, context); + return loadPhase5check(path, orgPath, context); } // try with and without image suffix -static ImageLoader* loadPhase4(const char* path, const LoadContext& context, std::vector* exceptions) +static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, 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, context, exceptions); + image = loadPhase5(pathWithSuffix, orgPath, context, exceptions); } if ( image == NULL ) - image = loadPhase5(path, context, exceptions); + image = loadPhase5(path, orgPath, context, exceptions); return image; } -static ImageLoader* loadPhase2(const char* path, const LoadContext& context, +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, const char* const frameworkPaths[], const char* const libraryPaths[], std::vector* exceptions); // forward reference // expand @ variables -static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std::vector* exceptions) +static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, std::vector* exceptions) { //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; @@ -1873,7 +2407,7 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std strcpy(&addPoint[1], &path[17]); else strcpy(newPath, &path[17]); - image = loadPhase4(newPath, context, exceptions); + image = loadPhase4(newPath, orgPath, context, exceptions); if ( image != NULL ) return image; @@ -1887,7 +2421,7 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std strcpy(&addPoint[1], &path[17]); else strcpy(newRealPath, &path[17]); - image = loadPhase4(newRealPath, context, exceptions); + image = loadPhase4(newRealPath, orgPath, context, exceptions); if ( image != NULL ) return image; } @@ -1904,7 +2438,7 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std strcpy(&addPoint[1], &path[13]); else strcpy(newPath, &path[13]); - image = loadPhase4(newPath, context, exceptions); + image = loadPhase4(newPath, orgPath, context, exceptions); if ( image != NULL ) return image; @@ -1918,7 +2452,7 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std strcpy(&addPoint[1], &path[13]); else strcpy(newRealPath, &path[13]); - image = loadPhase4(newRealPath, context, exceptions); + image = loadPhase4(newRealPath, orgPath, context, exceptions); if ( image != NULL ) return image; } @@ -1934,7 +2468,13 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std strcpy(newPath, anRPath); strcat(newPath, "/"); strcat(newPath, trailingPath); - image = loadPhase4(newPath, context, exceptions); + image = loadPhase4(newPath, orgPath, context, 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; } @@ -1943,7 +2483,7 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std // substitute @rpath with LD_LIBRARY_PATH if ( sEnv.LD_LIBRARY_PATH != NULL ) { - image = loadPhase2(trailingPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions); + image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions); if ( image != NULL ) return image; } @@ -1956,12 +2496,12 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); } - return loadPhase4(path, context, exceptions); + return loadPhase4(path, orgPath, context, exceptions); } // try search paths -static ImageLoader* loadPhase2(const char* path, const LoadContext& context, +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, const char* const frameworkPaths[], const char* const libraryPaths[], std::vector* exceptions) { @@ -1977,7 +2517,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, strcat(npath, "/"); strcat(npath, frameworkPartialPath); //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); - image = loadPhase4(npath, context, exceptions); + image = loadPhase4(npath, orgPath, context, exceptions); if ( image != NULL ) return image; } @@ -1992,7 +2532,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, strcat(libpath, "/"); strcat(libpath, libraryLeafName); //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); - image = loadPhase4(libpath, context, exceptions); + image = loadPhase4(libpath, orgPath, context, exceptions); if ( image != NULL ) return image; } @@ -2001,27 +2541,27 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, } // try search overrides and fallbacks -static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std::vector* exceptions) +static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, 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, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions); + image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, 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, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions); + image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions); if ( image != NULL ) return image; } // try raw path - image = loadPhase3(path, context, exceptions); + image = loadPhase3(path, orgPath, context, exceptions); if ( image != NULL ) return image; @@ -2030,7 +2570,7 @@ static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths ) fallbackLibraryPaths = NULL; if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) { - image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions); + image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions); if ( image != NULL ) return image; } @@ -2039,7 +2579,7 @@ static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std } // try root substitutions -static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std::vector* exceptions) +static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, std::vector* exceptions) { //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); @@ -2049,14 +2589,14 @@ static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std char newPath[strlen(*rootPath) + strlen(path)+2]; strcpy(newPath, *rootPath); strcat(newPath, path); - ImageLoader* image = loadPhase1(newPath, context, exceptions); + ImageLoader* image = loadPhase1(newPath, orgPath, context, exceptions); if ( image != NULL ) return image; } } // try raw path - return loadPhase1(path, context, exceptions); + return loadPhase1(path, orgPath, context, exceptions); } // @@ -2075,6 +2615,9 @@ static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std // ImageLoader* load(const char* path, const LoadContext& context) { + CRSetCrashLogMessage2(path); + const char* orgPath = path; + //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 @@ -2084,18 +2627,36 @@ ImageLoader* load(const char* path, const LoadContext& context) } // try all path permutations and check against existing loaded images - ImageLoader* image = loadPhase0(path, context, NULL); - if ( image != NULL ) + ImageLoader* image = loadPhase0(path, orgPath, context, NULL); + if ( image != NULL ) { + CRSetCrashLogMessage2(NULL); return image; + } - // try all path permutations and try open() until first sucesss + // try all path permutations and try open() until first success std::vector exceptions; - image = loadPhase0(path, context, &exceptions); - if ( image != NULL ) + image = loadPhase0(path, orgPath, context, &exceptions); + 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 __IPHONE_OS_VERSION_MIN_REQUIRED + // if loaded image is not from cache, but original path is in cache + // set gSharedCacheOverridden flag to disable some ObjC optimizations + if ( !gSharedCacheOverridden ) { + if ( !image->inSharedCache() && inSharedCache(path) ) { + gSharedCacheOverridden = true; + } + } +#endif return image; + } else if ( exceptions.size() == 0 ) { - if ( context.dontLoad ) + if ( context.dontLoad ) { return NULL; + } else throw "image not found"; } @@ -2122,44 +2683,83 @@ ImageLoader* load(const char* path, const LoadContext& context) #if DYLD_SHARED_CACHE_SUPPORT -// hack until dyld no longer needs to run on Leopard kernels that don't have new shared region syscall -static bool newSharedRegionSyscallAvailable() -{ - int shreg_version; - size_t buffer_size = sizeof(shreg_version); - if ( sysctlbyname("vm.shared_region_version", &shreg_version, &buffer_size, NULL, 0) == 0 ) { - if ( shreg_version == 3 ) - return true; - } - return false; -} + + +#if __ppc__ + #define ARCH_NAME "ppc" + #define ARCH_NAME_ROSETTA "rosetta" + #define ARCH_CACHE_MAGIC "dyld_v1 ppc" +#elif __ppc64__ + #define ARCH_NAME "ppc64" + #define ARCH_CACHE_MAGIC "dyld_v1 ppc64" +#elif __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 SHARED_REGION_READ_ONLY_START 0x7FFF80000000LL + #define SHARED_REGION_READ_ONLY_END 0x7FFFC0000000LL + #define SHARED_REGION_WRITABLE_START 0x7FFF70000000LL + #define SHARED_REGION_WRITABLE_END 0x7FFF80000000LL + #define SLIDEABLE_CACHE_SUPPORT 1 +#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_7A__ + #define ARCH_NAME "armv7" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7" + #define SHARED_REGION_READ_ONLY_START 0x30000000 + #define SHARED_REGION_READ_ONLY_END 0x3E000000 + #define SHARED_REGION_WRITABLE_START 0x3E000000 + #define SHARED_REGION_WRITABLE_END 0x40000000 + #define SLIDEABLE_CACHE_SUPPORT 1 +#endif static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address) { - if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() ) + if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) ) return syscall(294, start_address); return -1; } -static int __attribute__((noinline)) _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) +static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], + int codeSignatureMappingIndex, int slide, void* slideInfo, uint32_t slideInfoSize) { - int result; - if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() ) { - return syscall(295, fd, count, mappings); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // register code signature blob for whole dyld cache + if ( codeSignatureMappingIndex != -1 ) { + fsignatures_t siginfo; + siginfo.fs_file_start = 0; // cache always starts at beginning of file + siginfo.fs_blob_start = (void*)mappings[codeSignatureMappingIndex].sfm_file_offset; + siginfo.fs_blob_size = mappings[codeSignatureMappingIndex].sfm_size; + int result = fcntl(fd, F_ADDFILESIGS, &siginfo); + if ( result == -1 ) + dyld::log("dyld: code signature for shared cache failed with errno=%d\n", errno); + } +#endif + 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_all_image_infos.processDetachedFromSharedRegion = true; + // map cache just for this process with mmap() - bool failed = false; - const shared_file_mapping_np* start = mappings; - const shared_file_mapping_np* end = &mappings[count]; + 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; @@ -2168,45 +2768,57 @@ static int __attribute__((noinline)) _shared_region_map_np(int fd, uint32_t coun if ( p->sfm_init_prot & VM_PROT_WRITE ) protection |= PROT_WRITE; off_t offset = p->sfm_file_offset; - mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); - if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) - failed = true; - } - if ( !failed ) { - result = 0; - gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; - } - else { - result = -1; - gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - if ( gLinkContext.verboseMapping ) - dyld::log("dyld: shared cached cannot be mapped\n"); + 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; + } + } + +#if SLIDEABLE_CACHE_SUPPORT + // update all __DATA pages with slide info + if ( slide != 0 ) { + const uintptr_t dataPagesStart = mappings[1].sfm_address; + const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo; + const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset); + const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset); + for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) { + 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< rwSpace) ? rwSpace : roSpace; + long slide = (arc4random() % slideSpace) & (-4096); + //dyld::log("roSpace=0x%0llX\n", roSpace); + //dyld::log("rwSpace=0x%0llX\n", rwSpace); + //dyld::log("slideSpace=0x%0lX\n", slideSpace); + //dyld::log("slide=0x%0lX\n", slide); + + // update mappings + for(uint32_t i=0; i < mappingsCount; ++i) { + mappings[i].sfm_address += slide; + } + + return slide; +} +#endif // SLIDEABLE_CACHE_SUPPORT + static void mapSharedCache() { uint64_t cacheBaseAddress; @@ -2239,6 +2903,17 @@ static void mapSharedCache() if ( gLinkContext.verboseMapping ) dyld::log("dyld: existing shared cached in memory is not compatible\n"); } + // check if cache file is slidable + dyld_cache_header* header = (dyld_cache_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_all_image_infos.sharedCacheSlide = sSharedCacheSlide; + //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress); + } } else { #if __i386__ || __x86_64__ @@ -2251,13 +2926,13 @@ static void mapSharedCache() // user booted machine in safe-boot mode struct stat dyldCacheStatInfo; // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path - if ( ::stat(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { + if ( ::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(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); + ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; return; } @@ -2272,22 +2947,48 @@ static void mapSharedCache() if ( ::read(fd, firstPages, 8192) == 8192 ) { dyld_cache_header* header = (dyld_cache_header*)firstPages; if ( strcmp(header->magic, ARCH_CACHE_MAGIC) == 0 ) { - const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPages[header->mappingOffset]; - const shared_file_mapping_np* const end = &mappings[header->mappingCount]; + const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset]; + const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount]; + shared_file_mapping_np mappings[header->mappingCount+1]; // add room for code-sig + unsigned int mappingCount = header->mappingCount; + int codeSignatureMappingIndex = -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; - for (const shared_file_mapping_np* p = mappings; p < end; ++p) { + 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->sfm_file_offset+p->sfm_size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) { + 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 __IPHONE_OS_VERSION_MIN_REQUIRED + // if shared cache is code signed, add a mapping for the code signature + uint32_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 ) { + int linkeditMapping = mappingCount-1; + codeSignatureMappingIndex = mappingCount++; + mappings[codeSignatureMappingIndex].sfm_address = mappings[linkeditMapping].sfm_address + mappings[linkeditMapping].sfm_size; + mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+4095) & (-4096); + mappings[codeSignatureMappingIndex].sfm_file_offset = header->codeSignatureOffset; + mappings[codeSignatureMappingIndex].sfm_max_prot = VM_PROT_READ; + mappings[codeSignatureMappingIndex].sfm_init_prot = VM_PROT_READ; + } +#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; @@ -2302,16 +3003,43 @@ static void mapSharedCache() } } if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) { - dyld::log("dyld: shared cached file was build against a different libSystem.dylib, ignoring cache\n"); + 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 ( goodCache ) { - const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPages[header->mappingOffset]; - if (_shared_region_map_np(fd, header->mappingCount, mappings) == 0) { - // sucessfully mapped cache into shared region + long cacheSlide = 0; + void* slideInfo = NULL; + uint32_t slideInfoSize = 0; + #if SLIDEABLE_CACHE_SUPPORT + // check if shared cache contains slid info + if ( header->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*)(long)(mappings[2].sfm_address + (header->slideInfoOffset - mappings[2].sfm_file_offset)); + slideInfoSize = header->slideInfoSize; + // add VM_PROT_SLIDE bit to __DATA area of cache + mappings[1].sfm_max_prot |= VM_PROT_SLIDE; + mappings[1].sfm_init_prot |= VM_PROT_SLIDE; + } + } + #endif + if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, codeSignatureMappingIndex, cacheSlide, slideInfo, slideInfoSize) == 0) { + // successfully mapped cache into shared region sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; + sSharedCacheSlide = cacheSlide; + dyld_all_image_infos.sharedCacheSlide = cacheSlide; + //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide); + } + else { + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached file could not be mapped\n"); } } } @@ -2339,44 +3067,66 @@ static void mapSharedCache() // tell gdb where the shared cache is if ( sSharedCache != NULL ) { - const shared_file_mapping_np* const start = (shared_file_mapping_np*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset); + 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; if ( gLinkContext.verboseMapping ) { if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) - dyld::log("dyld: Mapping shared cache from %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + dyld::log("dyld: Mapping shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); else if ( gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion ) - dyld::log("dyld: Mapping private shared cache from %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + dyld::log("dyld: Mapping private shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); } - const shared_file_mapping_np* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; + const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; int index = 0; - for (const shared_file_mapping_np* p = start; p < end; ++p, ++index ) { - dyld_shared_cache_ranges.ranges[index].start = p->sfm_address; - dyld_shared_cache_ranges.ranges[index].length = p->sfm_size; + 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->sfm_address, p->sfm_address+p->sfm_size-1, - ((p->sfm_init_prot & VM_PROT_READ) ? "read " : ""), - ((p->sfm_init_prot & VM_PROT_WRITE) ? "write " : ""), - ((p->sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), p->sfm_init_prot, p->sfm_max_prot); + 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->sfm_init_prot == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->sfm_address & 0xF0000000) == 0xA0000000) ) { - if ( p->sfm_size != 0 ) { + 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->sfm_address, p->sfm_size, false, prot); + 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->sfm_address, - p->sfm_address+p->sfm_size-1, + 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 __IPHONE_OS_VERSION_MIN_REQUIRED + if ( gLinkContext.verboseMapping ) { + // list the code blob + dyld_cache_header* header = (dyld_cache_header*)sSharedCache; + uint32_t signatureSize = header->codeSignatureSize; + // zero size in header means signature runs to end-of-file + if ( signatureSize == 0 ) { + struct stat stat_buf; + if ( ::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); + } + } + // check for file that enables dyld shared cache dylibs to be overridden + struct stat enableStatBuf; + sDylibsOverrideCache = ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0 ); +#endif } } @@ -2404,8 +3154,8 @@ ImageLoader* cloneImage(ImageLoader* image) context.mustBeBundle = true; context.mustBeDylib = false; context.canBePIE = false; - context.origin = false; - context.rpath = false; + context.origin = NULL; + context.rpath = NULL; return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); } @@ -2491,12 +3241,21 @@ void halt(const char* message) dyld::log("dyld: %s\n", message); setErrorMessage(message); uintptr_t terminationFlags = 0; - if ( !gLinkContext.startedInitializingMainExecutable ) + if ( !gLinkContext.startedInitializingMainExecutable ) terminationFlags = 1; setAlImageInfosHalt(error_string, terminationFlags); dyld_fatal_error(error_string); } +static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath, + const char* errorTargetDylibPath, const char* errorSymbol) +{ + dyld_all_image_infos.errorKind = errorCode; + dyld_all_image_infos.errorClientOfDylibPath = errorClientOfDylibPath; + dyld_all_image_infos.errorTargetDylibPath = errorTargetDylibPath; + dyld_all_image_infos.errorSymbol = errorSymbol; +} + uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) { @@ -2537,7 +3296,6 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) } -#if COMPRESSED_DYLD_INFO_SUPPORT uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) { uintptr_t result = 0; @@ -2554,7 +3312,9 @@ uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindi // bind lazy pointer and return it try { - result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext); + result = (*imageLoaderCache)->doBindFastLazySymbol(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); @@ -2564,7 +3324,6 @@ uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindi // return target address to glue which jumps to it with real parameters restored return result; } -#endif // COMPRESSED_DYLD_INFO_SUPPORT @@ -2771,6 +3530,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha #if DYLD_SHARED_CACHE_SUPPORT gLinkContext.inSharedCache = &inSharedCache; #endif + gLinkContext.setErrorStrings = &setErrorStrings; #if SUPPORT_OLD_CRT_INITIALIZATION gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; #endif @@ -2851,8 +3611,70 @@ static bool hasRestrictedSegment(const macho_header* mh) return false; } + + +// +// 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) +{ + // open file (automagically closed when this function exits) + FileOpener file(dylibPath); - + if ( file.getFileDescriptor() == -1 ) + return false; + + uint8_t firstPage[4096]; + 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; + } + } + + // check mach-o header + const mach_header* mh = (mach_header*)firstPage; + 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; +} + #if 0 static void printAllImages() { @@ -2866,7 +3688,6 @@ static void printAllImages() } #endif - void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths) { // add to list of known images. This did not happen at creation time for bundles @@ -2891,7 +3712,9 @@ void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChai void runInitializers(ImageLoader* image) { // do bottom up initialization - image->runInitializers(gLinkContext); + ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()]; + initializerTimes[0].count = 0; + image->runInitializers(gLinkContext, initializerTimes[0]); } void garbageCollectImages() @@ -2970,6 +3793,33 @@ static void loadInsertedDylib(const char* path) } } +static bool processRestricted(const macho_header* mainExecutableMH) +{ + // all processes with setuid or setgid bit set are restricted + if ( issetugid() ) + return true; + + if ( hasRestrictedSegment(mainExecutableMH) && (geteuid() != 0) ) { + // existence of __RESTRICT/__restrict section make process restricted + return true; + } + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ask kernel if code signature of program makes it restricted + uint32_t flags; + if ( syscall(SYS_csops /* 169 */, + 0 /* asking about myself */, + CS_OPS_STATUS, + &flags, + sizeof(flags)) != -1) { + if (flags & CS_RESTRICT) + return true; + } +#endif + return false; +} + + // // Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which // sets up some registers and call this function. @@ -2979,6 +3829,7 @@ static void loadInsertedDylib(const char* path) uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]) { + CRSetCrashLogMessage("dyld: launch started"); #ifdef ALTERNATIVE_LOGFILE sLogfile = open(ALTERNATIVE_LOGFILE, O_WRONLY | O_CREAT | O_APPEND); if ( sLogfile == -1 ) { @@ -2986,7 +3837,26 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a dyld::log("error opening alternate log file %s, errno = %d\n", ALTERNATIVE_LOGFILE, errno); } #endif + +#if LOG_BINDINGS + char bindingsLogPath[256]; + const char* shortProgName = "unknown"; + if ( argc > 0 ) { + shortProgName = strrchr(argv[0], '/'); + if ( shortProgName == NULL ) + shortProgName = argv[0]; + else + ++shortProgName; + } + mysprintf(bindingsLogPath, "/tmp/bindings/%d-%s", getpid(), shortProgName); + sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666); + if ( sBindingsLogfile == -1 ) { + ::mkdir("/tmp/bindings", 0777); + sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666); + } + //dyld::log("open(%s) => %d, errno = %d\n", bindingsLogPath, sBindingsLogfile, errno); +#endif setContext(mainExecutableMH, argc, argv, envp, apple); // Pickup the pointer to the exec path. @@ -3016,13 +3886,18 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a } uintptr_t result = 0; sMainExecutableMachHeader = mainExecutableMH; - sProcessIsRestricted = issetugid(); - if ( geteuid() != 0 ) { - // if we are not root, see if the binary is requesting restricting the use of DYLD_ env vars. - sProcessIsRestricted |= hasRestrictedSegment(mainExecutableMH); - } - if ( sProcessIsRestricted ) + sProcessIsRestricted = processRestricted(mainExecutableMH); + if ( sProcessIsRestricted ) { +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#if SUPPORT_VERSIONED_PATHS + checkVersionedPaths(); +#endif +#endif pruneEnvironmentVariables(envp, &apple); + // set again because envp and apple may have changed or moved + setContext(mainExecutableMH, argc, argv, envp, apple); + } else checkEnvironmentVariables(envp, ignoreEnvironmentVariables); if ( sEnv.DYLD_PRINT_OPTS ) @@ -3032,6 +3907,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a getHostInfo(); // 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 sAllImages.reserve(INITIAL_IMAGE_COUNT); sImageRoots.reserve(16); @@ -3046,6 +3922,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a #endif try { + CRSetCrashLogMessage("dyld: launch, loading dependent libraries"); // instantiate ImageLoader for main executable sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); sMainExecutable->setNeverUnload(); @@ -3083,9 +3960,12 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a for(unsigned int i=0; i < sInsertedDylibCount; ++i) { ImageLoader* image = sAllImages[i+1]; link(image, sEnv.DYLD_BIND_AT_LAUNCH, ImageLoader::RPathChain(NULL, NULL)); + // only INSERTED libraries can interpose + image->registerInterposing(); } } + CRSetCrashLogMessage("dyld: launch, running initializers"); #if SUPPORT_OLD_CRT_INITIALIZATION // Old way is to run initializers via a callback from crt1.o if ( ! gRunInitializersOldWay ) @@ -3093,6 +3973,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a initializeMainExecutable(); // run all initializers } catch(const char* message) { + syncAllImages(); halt(message); } catch(...) { @@ -3106,6 +3987,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int a sLogfile = STDERR_FILENO; } #endif + CRSetCrashLogMessage(NULL); return result; } diff --git a/src/dyld.h b/src/dyld.h index 2deab7d..9ad1ef8 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -59,6 +59,7 @@ namespace dyld { extern ImageLoader::LinkContext gLinkContext; extern bool gLogAPIs; + extern bool gSharedCacheOverridden; extern const struct LibSystemHelpers* gLibSystemHelpers; #if SUPPORT_OLD_CRT_INITIALIZATION extern bool gRunInitializersOldWay; @@ -94,7 +95,7 @@ namespace dyld { extern void clearErrorMessage(); extern bool mainExecutablePrebound(); extern ImageLoader* mainExecutable(); - extern void processDyldEnvironmentVarible(const char* key, const char* value); + extern void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainDir); extern void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler); extern void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler); extern void garbageCollectImages(); @@ -102,6 +103,8 @@ namespace dyld { extern const void* imMemorySharedCacheHeader(); extern uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset); extern bool inSharedCache(const char* path); - +#if LOG_BINDINGS + extern void logBindings(const char* format, ...); +#endif }; diff --git a/src/dyld.order b/src/dyld.order new file mode 100644 index 0000000..ba83697 --- /dev/null +++ b/src/dyld.order @@ -0,0 +1,98 @@ +# +# 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@ +# + +# +# Cluster these __DATA symbols in dyld to reduce pages dirtied +# +_sDyldInfo.0 +_sDyldInfo.1 +_sDyldInfo.2 +_sDyldInfo.3 +_sDyldInfo.4 +_sDyldTextEnd +_mach_init_inited.7154.b +__task_reply_port +_mach_task_self_ +_mach_host_self_ +_vm_page_size +_vm_page_mask +_vm_page_shift +___stack_chk_guard +__ZN4dyldL23sFrameworkFallbackPathsE +__ZN4dyldL21sLibraryFallbackPathsE +__ZL11initialPool +__ZN4dyld12gLinkContextE +__ZN4dyld17gLibSystemHelpersE +__ZN4dyldL17sSharedCacheSlideE +_dyld_shared_cache_ranges +__ZN11ImageLoader27fgImagesUsedFromSharedCacheE +__ZN11ImageLoader19fgInterposingTuplesE +__ZN11ImageLoader24fgTotalLoadLibrariesTimeE +__ZN11ImageLoader24fgTotalLoadLibrariesTimeE +__ZN11ImageLoader17fgTotalRebaseTimeE +__ZN11ImageLoader15fgTotalBindTimeE +__ZN11ImageLoader19fgTotalWeakBindTimeE +__ZN11ImageLoader10fgTotalDOFE +__ZN11ImageLoader15fgTotalInitTimeE +__ZN11ImageLoader22fgTotalBytesPreFetchedE +__ZN11ImageLoader18fgTotalBytesMappedE +__ZN11ImageLoader21fgTotalSegmentsMappedE +__ZN11ImageLoader19fgTotalRebaseFixupsE +__ZN11ImageLoader17fgTotalBindFixupsE +__ZN11ImageLoader26fgTotalBindSymbolsResolvedE +__ZN11ImageLoader24fgTotalBindImageSearchesE +__ZN11ImageLoader29fgTotalPossibleLazyBindFixupsE +__ZN11ImageLoader21fgTotalLazyBindFixupsE +__ZN11ImageLoader27fgImagesRequiringCoalescingE +__ZN11ImageLoader21fgNextPIEDylibAddressE +__ZN11ImageLoader26fgImagesWithUsedPrebindingE +__ZN11ImageLoader26fgImagesHasWeakDefinitionsE +__ZN16ImageLoaderMachO26fgSymbolTableBinarySearchsE +__ZN16ImageLoaderMachO19fgSymbolTrieSearchsE +__ZN11ImageLoader13fgLoadOrdinalE +__ZN4dyldL9sExecPathE +__ZN4dyldL25sMainExecutableMachHeaderE +__ZN4dyldL12sSharedCacheE +__ZN4dyldL17sUndefinedHandlerE +__ZN4dyldL10sAllImagesE +__ZN4dyldL18sAddImageCallbacksE +__ZN4dyldL11sImageRootsE +__ZN4dyldL29sImageFilesNeedingTerminationE +__ZN4dyldL35sImageFilesNeedingDOFUnregistrationE +__ZN4dyldL35sImageFilesNeedingDOFUnregistrationE +__ZN4dyldL21sRemoveImageCallbacksE +__ZN4dyldL15sSingleHandlersE +__ZN4dyldL14sBatchHandlersE +__ZN4dyldL15sMainExecutableE +__ZN4dyldL4sEnvE +__ZL11sImageInfos +__ZL11sImageUUIDs +__ZN4dyldL19sInsertedDylibCountE +__ZN4dyldL20sProcessIsRestrictedE +__ZL17sObjectFileImages +__ZN11ImageLoader23fgDynamicImageReExportsE +__ZN4dyldL15sDylibOverridesE +__ZN4dyldL19sInsertedDylibCountE +__ZN4dyldL20sProcessIsRestrictedE +__ZN4dyldL18sMappedRangesStartE +__thread diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index 609f0b3..0d0dbc7 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.cpp @@ -57,6 +57,11 @@ #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[]); // deprecated APIs are still availble on Mac OS X, but not on iPhone OS #if __IPHONE_OS_VERSION_MIN_REQUIRED @@ -80,7 +85,6 @@ static int sLastErrorNo; // The following functions have no prototype in any header. They are special cases // where _dyld_func_lookup() is used directly. -static void _dyld_fork_child(); static void _dyld_make_delayed_module_initializer_calls(); static void registerThreadHelpers(const dyld::LibSystemHelpers*); #if DEPRECATED_APIS_SUPPORTED @@ -97,7 +101,9 @@ static void _dyld_call_module_initializers_for_dylib(const struct mach_header* m 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 !__arm__ static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info); +#endif static void unimplemented() { @@ -118,7 +124,6 @@ static struct dyld_func dyld_funcs[] = { {"__dyld_dlopen", (void*)dlopen }, {"__dyld_dlsym", (void*)dlsym }, {"__dyld_dlopen_preflight", (void*)dlopen_preflight }, - {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address }, {"__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 }, @@ -136,13 +141,17 @@ static struct dyld_func dyld_funcs[] = { #if !__arm__ {"__dyld_find_unwind_sections", (void*)client_dyld_find_unwind_sections }, #endif -#if __i386__ || __x86_64__ +#if __i386__ || __x86_64__ || __arm__ {"__dyld_fast_stub_entry", (void*)dyld::fastBindLazySymbol }, #endif {"__dyld_image_path_containing_address", (void*)dyld_image_path_containing_address }, +#if __IPHONE_OS_VERSION_MIN_REQUIRED + {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, +#endif // deprecated #if DEPRECATED_APIS_SUPPORTED + {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address }, {"__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 }, @@ -970,10 +979,17 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, // 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 ) + 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 ) @@ -1150,7 +1166,7 @@ static void _dyld_register_binding_handler(void * (*bindingHandler)(const char * // Call by fork() in libSystem after the kernel trap is done on the child side -static void _dyld_fork_child() +void _dyld_fork_child() { if ( dyld::gLogAPIs ) dyld::log("%s()\n", __func__); @@ -1243,12 +1259,23 @@ bool lookupDyldFunction(const char* name, uintptr_t* address) return false; } + static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) { dyld::gLibSystemHelpers = helpers; // let gdb know it is safe to run code in inferior that might call malloc() dyld_all_image_infos.libSystemInitialized = true; + +#if __arm__ + 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 } @@ -1287,6 +1314,8 @@ bool dlopen_preflight(const char* path) return true; #endif + CRSetCrashLogMessage("dyld: in dlopen_preflight()"); + bool result = false; std::vector rpathsFromCallerImage; try { @@ -1303,6 +1332,27 @@ bool dlopen_preflight(const char* path) ImageLoader* image = 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 dyld::LoadContext context; context.useSearchPaths = true; context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths @@ -1333,6 +1383,7 @@ bool dlopen_preflight(const char* path) const char* str = *it; free((void*)str); } + CRSetCrashLogMessage(NULL); return result; } @@ -1357,6 +1408,7 @@ void* dlopen(const char* path, int mode) bool lockHeld = false; if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) { dyld::gLibSystemHelpers->acquireGlobalDyldLock(); + CRSetCrashLogMessage("dyld: in dlopen()"); lockHeld = true; } @@ -1376,6 +1428,27 @@ void* dlopen(const char* path, int mode) 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 dyld::LoadContext context; context.useSearchPaths = true; context.useFallbackPaths= leafName; // a partial path means no fallback paths @@ -1416,6 +1489,7 @@ void* dlopen(const char* path, int mode) // release global dyld lock early, this enables initializers to do threaded operations if ( lockHeld ) { + CRSetCrashLogMessage(NULL); dyld::gLibSystemHelpers->releaseGlobalDyldLock(); lockHeld = false; } @@ -1462,8 +1536,10 @@ void* dlopen(const char* path, int mode) dlerrorSet("image not already loaded"); } - if ( lockHeld ) + if ( lockHeld ) { + CRSetCrashLogMessage(NULL); dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + } return result; } @@ -1505,6 +1581,7 @@ int dladdr(const void* address, Dl_info* info) if ( dyld::gLogAPIs ) dyld::log("%s(%p, %p)\n", __func__, address, info); + CRSetCrashLogMessage("dyld: in dladdr()"); ImageLoader* image = dyld::findImageContainingAddress(address); if ( image != NULL ) { info->dli_fname = image->getPath(); @@ -1513,6 +1590,7 @@ int dladdr(const void* address, Dl_info* info) // 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 @@ -1521,12 +1599,15 @@ int dladdr(const void* address, Dl_info* info) 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 } @@ -1552,6 +1633,7 @@ void* dlsym(void* handle, const char* symbolName) if ( dyld::gLogAPIs ) dyld::log("%s(%p, %s)\n", __func__, handle, symbolName); + CRSetCrashLogMessage("dyld: in dlsym()"); dlerrorClear(); const ImageLoader* image; @@ -1566,11 +1648,13 @@ void* dlsym(void* handle, const char* symbolName) // magic "search all" handle if ( handle == RTLD_DEFAULT ) { if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) { + CRSetCrashLogMessage(NULL); return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); dlerrorSet(str); free((void*)str); + CRSetCrashLogMessage(NULL); return NULL; } @@ -1579,25 +1663,37 @@ void* dlsym(void* handle, const char* symbolName) image = dyld::mainExecutable(); sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); dlerrorSet(str); free((void*)str); + CRSetCrashLogMessage(NULL); return NULL; } // magic "search what I would see" handle if ( handle == RTLD_NEXT ) { +#if __ppc__ + // work around for llvmgcc bug + void* fa = __builtin_frame_address(0); + fa = *(void**)fa; + fa = *(void**)fa; + void* callerAddress = *((void**)(((int)fa)+8)); +#else void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue +#endif ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName); dlerrorSet(str); free((void*)str); + CRSetCrashLogMessage(NULL); return NULL; } // magic "search me, then what I would see" handle @@ -1606,11 +1702,13 @@ void* dlsym(void* handle, const char* symbolName) ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName); dlerrorSet(str); free((void*)str); + CRSetCrashLogMessage(NULL); return NULL; } // real handle @@ -1622,6 +1720,7 @@ void* dlsym(void* handle, const char* symbolName) sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); @@ -1631,6 +1730,7 @@ void* dlsym(void* handle, const char* symbolName) else { dlerrorSet("invalid handle passed to dlsym()"); } + CRSetCrashLogMessage(NULL); return NULL; } @@ -1675,7 +1775,6 @@ void dyld_register_image_state_change_handler(dyld_image_states state, bool batc dyld::registerImageStateSingleChangeHandler(state, handler); } - const char* dyld_image_path_containing_address(const void* address) { if ( dyld::gLogAPIs ) @@ -1688,3 +1787,13 @@ const char* dyld_image_path_containing_address(const void* address) } + +#if __IPHONE_OS_VERSION_MIN_REQUIRED +bool dyld_shared_cache_some_image_overridden() +{ + return dyld::gSharedCacheOverridden; +} +#endif + + + diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index e14a6ef..cb99cdd 100644 --- a/src/dyldAPIsInLibSystem.cpp +++ b/src/dyldAPIsInLibSystem.cpp @@ -34,9 +34,14 @@ #include "dyldLock.h" -extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); +extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); +extern "C" void __cxa_finalize(const void *dso); -#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__) +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__ || __arm__) // deprecated APIs are still availble on Mac OS X, but not on iPhone OS #if __IPHONE_OS_VERSION_MIN_REQUIRED @@ -104,15 +109,16 @@ void NSInstallLinkEditErrorHandlers( const NSLinkEditErrorHandlers* handlers) { DYLD_LOCK_THIS_BLOCK; - static void (*p)( - void (*undefined)(const char* symbol_name), - NSModule (*multiple)(NSSymbol s, NSModule old, NSModule newhandler), - void (*linkEdit)(NSLinkEditErrors c, int errorNumber, - const char* fileName, const char* errorString)) = NULL; + typedef void (*ucallback_t)(const char* symbol_name); + 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; if(p == NULL) _dyld_func_lookup("__dyld_install_handlers", (void**)&p); - p(handlers->undefined, handlers->multiple, handlers->linkEdit); + mcallback_t m = handlers->multiple; + p(handlers->undefined, m, handlers->linkEdit); } const char* @@ -346,6 +352,7 @@ const char* libraryName) switch ( lc->cmd ) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: dl = (struct dylib_command *)lc; install_name = (char *)dl + dl->dylib.name.offset; if(names_match(install_name, libraryName) == TRUE) @@ -765,7 +772,8 @@ _dyld_register_func_for_add_image( void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL; + typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); + static void (*p)(callback_t func) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_register_func_for_add_image", (void**)&p); @@ -782,7 +790,8 @@ _dyld_register_func_for_remove_image( void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL; + typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide); + static void (*p)(callback_t func) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_register_func_for_remove_image", (void**)&p); @@ -972,7 +981,8 @@ void _dyld_moninit( void (*monaddition)(char *lowpc, char *highpc)) { DYLD_LOCK_THIS_BLOCK; - static void (*p)(void (*monaddition)(char *lowpc, char *highpc)) = NULL; + typedef void (*monproc)(char *lowpc, char *highpc); + static void (*p)(monproc monaddition) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_moninit", (void**)&p); @@ -1067,7 +1077,7 @@ static void shared_cache_out_of_date() // the table passed to dyld containing thread helpers -static dyld::LibSystemHelpers sHelpers = { 6, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, +static dyld::LibSystemHelpers sHelpers = { 8, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, #if DYLD_SHARED_CACHE_SUPPORT &shared_cache_missing, &shared_cache_out_of_date, @@ -1076,14 +1086,17 @@ static dyld::LibSystemHelpers sHelpers = { 6, &dyldGlobalLockAcquire, &dyldGloba #endif NULL, NULL, &pthread_key_create, &pthread_setspecific, - &malloc_size }; + &malloc_size, + &pthread_getspecific, + &__cxa_finalize}; // // during initialization of libSystem this routine will run // and call dyld, registering the helper functions. // -extern "C" void _dyld_initializer() __attribute__((visibility("hidden"))); +extern "C" void tlv_initializer(); +extern "C" void _dyld_initializer(); void _dyld_initializer() { DYLD_LOCK_INITIALIZER; @@ -1093,6 +1106,8 @@ void _dyld_initializer() _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); if(p != NULL) p(&sHelpers); + + tlv_initializer(); } @@ -1187,7 +1202,6 @@ const struct dyld_all_image_infos* _dyld_get_all_image_infos() } #if !__arm__ -__attribute__((visibility("hidden"))) bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) { DYLD_NO_LOCK_THIS_BLOCK; @@ -1200,7 +1214,7 @@ bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) #endif -#if __i386__ || __x86_64__ +#if __i386__ || __x86_64__ || __arm__ __attribute__((visibility("hidden"))) void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) { @@ -1224,4 +1238,30 @@ const char* dyld_image_path_containing_address(const void* addr) return p(addr); } +#if __IPHONE_OS_VERSION_MIN_REQUIRED +bool dyld_shared_cache_some_image_overridden() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_shared_cache_some_image_overridden", (void**)&p); + return p(); +} +#endif + + +// SPI called __fork +void _dyld_fork_child() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_fork_child", (void**)&p); + return p(); +} + + + diff --git a/src/dyldExceptions.c b/src/dyldExceptions.c index 0cf1a18..cff7dbe 100644 --- a/src/dyldExceptions.c +++ b/src/dyldExceptions.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "mach-o/dyld_priv.h" #include "dyldLibSystemInterface.h" @@ -52,12 +53,57 @@ extern struct LibSystemHelpers* _ZN4dyld17gLibSystemHelpersE; #endif -#if __i386__ || __x86_64 || __ppc__ +// +// The standard versions of __cxa_get_globals*() from libstdc++-static.a cannot be used. +// On Mac OS X, they use keymgr which dyld does not implement. +// On iPhoneOS, they use pthread_key_create which dyld cannot use. +// +// Implement these ourselves to make upcalls into libSystem to malloc and create a pthread key +// +static pthread_key_t sCxaKey = 0; +static char sPreMainCxaGlobals[2*sizeof(long)]; + +// called by libstdc++.a +char* __cxa_get_globals() +{ + // if libSystem.dylib not yet initialized, or is old libSystem, use shared global + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) + return sPreMainCxaGlobals; + + if ( sCxaKey == 0 ) { + // create key + // we don't need a lock because only one thread can be in dyld at a time + _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free); + } + char* data = (char*)pthread_getspecific(sCxaKey); + if ( data == NULL ) { + data = calloc(2,sizeof(void*)); + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data); + } + return data; +} + +// called by libstdc++.a +char* __cxa_get_globals_fast() +{ + // if libSystem.dylib not yet initialized, or is old libSystem, use shared global + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) + return sPreMainCxaGlobals; + + return pthread_getspecific(sCxaKey); +} + + + + +#if __x86_64__ || __i386__ || __ppc__ +// +// The intel/ppc versions of dyld uses zero-cost exceptions which are handled by +// linking with a special copy of libunwind.a +// static struct dyld_unwind_sections sDyldInfo; static void* sDyldTextEnd; -static pthread_key_t sCxaKey = 0; -static char sPreMainCxaGlobals[2*sizeof(long)]; // called by dyldStartup.s very early void dyld_exceptions_init(struct mach_header* mh, intptr_t slide) @@ -102,225 +148,89 @@ bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) } } - - -// called by libstdc++.a -char* __cxa_get_globals() -{ - // if libSystem.dylib not yet initialized, or is old libSystem, use shared global - if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) - return sPreMainCxaGlobals; - - if ( sCxaKey == 0 ) { - // create key - // we don't need a lock because only one thread can be in dyld at a time - _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free); - } - char* data = (char*)pthread_getspecific(sCxaKey); - if ( data == NULL ) { - data = calloc(2,sizeof(void*)); - _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data); - } - return data; -} - -// called by libstdc++.a -char* __cxa_get_globals_fast() -{ - // if libSystem.dylib not yet initialized, or is old libSystem, use shared global - if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) - return sPreMainCxaGlobals; - - return pthread_getspecific(sCxaKey); -} - #if __ppc__ // the ppc version of _Znwm in libstdc++.a uses keymgr // need to override that void* _Znwm(size_t size) { return malloc(size); } #endif +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED +#if __arm__ +struct _Unwind_FunctionContext +{ + // next function in stack of handlers + struct _Unwind_FunctionContext* prev; -#else /* __i386__ || __x86_64 || __ppc__ */ - - - - - - -// -// BEGIN copy of code from libgcc.a source file unwind-dw2-fde-darwin.c -// -#define KEYMGR_API_MAJOR_GCC3 3 -/* ... with these keys. */ -#define KEYMGR_GCC3_LIVE_IMAGE_LIST 301 /* loaded images */ -#define KEYMGR_GCC3_DW2_OBJ_LIST 302 /* Dwarf2 object list */ -#define KEYMGR_EH_GLOBALS_KEY 13 - -/* Node of KEYMGR_GCC3_LIVE_IMAGE_LIST. Info about each resident image. */ -struct live_images { - unsigned long this_size; /* sizeof (live_images) */ - struct mach_header *mh; /* the image info */ - unsigned long vm_slide; - void (*destructor)(struct live_images *); /* destructor for this */ - struct live_images *next; - unsigned int examined_p; - void *fde; - void *object_info; - unsigned long info[2]; /* Future use. */ }; -// -// END copy of code from libgcc.a source file unwind-dw2-fde-darwin.c -// - -// -// dyld is built as stand alone executable with a static copy of libc, libstdc++, and libgcc. // -// In order for C++ exceptions to work within dyld, the C++ exception handling code -// must be able to find the exception handling frame data inside dyld. The standard -// exception handling code uses crt and keymgr to keep track of all images and calls -// getsectdatafromheader("__eh_frame") to find the EH data for each image. We implement -// our own copy of those functions below to enable exceptions within dyld. +// The ARM of dyld use SL-LJ based exception handling +// which does not require any initialization until libSystem is initialized. // -// Note: This exception handling is completely separate from any user code exception . -// handling which has its own keymgr (in libSystem). -// - - -static struct live_images sDyldImage; // zero filled -static void* sObjectList = NULL; -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4)) -static void* sEHGlobals = NULL; -#endif - - -// called by dyldStartup.s very early void dyld_exceptions_init(struct mach_header *mh, uintptr_t slide) { - sDyldImage.this_size = sizeof(struct live_images); - sDyldImage.mh = mh; - sDyldImage.vm_slide = slide; } +static pthread_key_t sThreadChainKey = 0; +static struct _Unwind_FunctionContext* sStaticThreadChain = NULL; - -// Hack for gcc 3.5's use of keymgr includes accessing __keymgr_global -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4)) -typedef struct Sinfo_Node { - uint32_t size; /* Size of this node. */ - uint16_t major_version; /* API major version. */ - uint16_t minor_version; /* API minor version. */ -} Tinfo_Node; -static const Tinfo_Node keymgr_info = { sizeof (Tinfo_Node), KEYMGR_API_MAJOR_GCC3, 0 }; -const Tinfo_Node* __keymgr_global[3] = { NULL, NULL, &keymgr_info }; -#endif - -static __attribute__((noreturn)) -void dyld_abort() -{ - //dyld::log("internal dyld error\n"); - _exit(1); -} - -void* _keymgr_get_and_lock_processwide_ptr(unsigned int key) +// +// When libSystem's initializers are run, they call back into dyld's +// registerThreadHelpers which creates a pthread key and calls +// __Unwind_SjLj_SetThreadKey(). +// +void __Unwind_SjLj_SetThreadKey(pthread_key_t key) { - // The C++ exception handling code uses two keys. No other keys should be seen - if ( key == KEYMGR_GCC3_LIVE_IMAGE_LIST ) { - return &sDyldImage; - } - else if ( key == KEYMGR_GCC3_DW2_OBJ_LIST ) { - return sObjectList; - } - dyld_abort(); + sThreadChainKey = key; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), sStaticThreadChain=%p\n", key, sStaticThreadChain); + // switch static chain to be per thread + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, sStaticThreadChain); + sStaticThreadChain = NULL; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), result=%d, new top=%p\n", key, result, pthread_getspecific(sThreadChainKey)); } -void _keymgr_set_and_unlock_processwide_ptr(unsigned int key, void* value) -{ - // The C++ exception handling code uses just this key. No other keys should be seen - if ( key == KEYMGR_GCC3_DW2_OBJ_LIST ) { - sObjectList = value; - return; - } - dyld_abort(); -} -void _keymgr_unlock_processwide_ptr(unsigned int key) -{ - // The C++ exception handling code uses just this key. No other keys should be seen - if ( key == KEYMGR_GCC3_LIVE_IMAGE_LIST ) { - return; - } - dyld_abort(); -} - -void* _keymgr_get_per_thread_data(unsigned int key) -{ -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4)) - // gcc 3.5 and later use this key - if ( key == KEYMGR_EH_GLOBALS_KEY ) - return sEHGlobals; -#endif +//static void printChain() +//{ +// _ZN4dyld3logEPKcz("chain: "); +// struct _Unwind_FunctionContext* start = sStaticThreadChain; +// if ( sThreadChainKey != 0 ) { +// start = (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey); +// } +// for (struct _Unwind_FunctionContext* p = start; p != NULL; p = p->prev) { +// _ZN4dyld3logEPKcz("%p -> ", p); +// } +// _ZN4dyld3logEPKcz("\n"); +//} - // used by std::termination which dyld does not use - dyld_abort(); -} -void _keymgr_set_per_thread_data(unsigned int key, void *keydata) +struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack() { -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4)) - // gcc 3.5 and later use this key - if ( key == KEYMGR_EH_GLOBALS_KEY ) { - sEHGlobals = keydata; - return; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_GetTopOfFunctionStack(), key=%d, ", sThreadChainKey); + //printChain(); + if ( sThreadChainKey != 0 ) { + return (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey); + } + else { + return sStaticThreadChain; } -#endif - // used by std::termination which dyld does not use - dyld_abort(); } - -// needed by C++ exception handling code to find __eh_frame section -const void* getsectdatafromheader(struct mach_header* mh, const char* segname, const char* sectname, unsigned long* size) +void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc) { - const struct load_command* cmd; - unsigned long i; - - cmd = (struct load_command*) ((char *)mh + sizeof(struct macho_header)); - for(i = 0; i < mh->ncmds; i++) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( strcmp(seg->segname, segname) == 0 ) { - const struct macho_section* sect = (struct macho_section*)( (char*)seg + sizeof(struct macho_segment_command) ); - unsigned long j; - for (j = 0; j < seg->nsects; j++) { - if ( strcmp(sect[j].sectname, sectname) == 0 ) { - *size = sect[j].size; - return (void*)(sect[j].addr); - } - } - } - } - cmd = (struct load_command*)( (char*)cmd + cmd->cmdsize ); - } - return NULL; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, prev=%p\n", + // fc, sThreadChainKey, (fc != NULL) ? fc->prev : NULL); + if ( sThreadChainKey != 0 ) + _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, fc); + else + sStaticThreadChain = fc; + //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, ", fc, sThreadChainKey); + //printChain(); } - -// Hack for transition of rdar://problem/3933738 -// Can be removed later. -// Allow C++ runtime to call getsectdatafromheader or getsectdatafromheader_64 -#if __LP64__ - #undef getsectdatafromheader - const void* getsectdatafromheader(struct mach_header* mh, const char* segname, const char* sectname, unsigned long* size) - { - return getsectdatafromheader_64(mh, segname, sectname, size); - } -#endif - #endif diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index a112d18..e40b1e8 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -112,6 +112,28 @@ static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, i } } + +// +// 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 ( strcmp(segCmd->segname, "__TEXT") == 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 @@ -186,136 +208,6 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide) } -// -// For some reason the kernel loads dyld with __TEXT and __LINKEDIT writable -// rdar://problem/3702311 -// -static void segmentProtectDyld(const struct macho_header* mh, intptr_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; - vm_address_t addr = seg->vmaddr + slide; - vm_size_t size = seg->vmsize; - const bool setCurrentPermissions = false; - vm_protect(mach_task_self(), addr, size, setCurrentPermissions, seg->initprot); - //dyld::log("dyld: segment %s, 0x%08X -> 0x%08X, set to %d\n", seg->segname, addr, addr+size-1, seg->initprot); - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - -} - - -// -// re-map the main executable to a new random address -// -static const struct macho_header* randomizeExecutableLoadAddress(const struct macho_header* orgMH, const char* envp[], uintptr_t* appsSlide) -{ -#if __ppc__ - // don't slide PIE programs running under rosetta - if ( dyld::isRosetta() ) - return orgMH; -#endif - // environment variable DYLD_NO_PIE can disable PIE - for(const char** p = envp; *p != NULL; p++) { - if ( strncmp(*p, "DYLD_NO_PIE=", 12) == 0 ) - return orgMH; - } - - // count segments - uint32_t segCount = 0; - const uint32_t cmd_count = orgMH->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)orgMH)+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; - // page-zero and custom stacks don't move - if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) - ++segCount; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // make copy of segment info - macho_segment_command segs[segCount]; - uint32_t index = 0; - uintptr_t highestAddressUsed = 0; - uintptr_t lowestAddressUsed = UINTPTR_MAX; - 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 ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) { - segs[index++] = *segCmd; - if ( (segCmd->vmaddr + segCmd->vmsize) > highestAddressUsed ) - highestAddressUsed = ((segCmd->vmaddr + segCmd->vmsize) + 4095) & -4096; - if ( segCmd->vmaddr < lowestAddressUsed ) - lowestAddressUsed = segCmd->vmaddr; - // do nothing if kernel has already randomized load address - if ( (strcmp(segCmd->segname, "__TEXT") == 0) && (segCmd->vmaddr != (uintptr_t)orgMH) ) - return orgMH; - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // choose a random new base address -#if __LP64__ - uintptr_t highestAddressPossible = highestAddressUsed + 0x100000000ULL; -#elif __arm__ - uintptr_t highestAddressPossible = 0x2fe00000; -#else - uintptr_t highestAddressPossible = 0x80000000; -#endif - uintptr_t sizeNeeded = highestAddressUsed-lowestAddressUsed; - if ( (highestAddressPossible-sizeNeeded) < highestAddressUsed ) { - // new and old segments will overlap - // need better algorithm for remapping - // punt and don't re-map - return orgMH; - } - uintptr_t possibleRange = (highestAddressPossible-sizeNeeded) - highestAddressUsed; - uintptr_t newBaseAddress = highestAddressUsed + ((arc4random() % possibleRange) & -4096); - - vm_address_t addr = newBaseAddress; - // reserve new address range - if ( vm_allocate(mach_task_self(), &addr, sizeNeeded, VM_FLAGS_FIXED) == KERN_SUCCESS ) { - // copy each segment to new address - for (uint32_t i = 0; i < segCount; ++i) { - uintptr_t newSegAddress = segs[i].vmaddr - lowestAddressUsed + newBaseAddress; - if ( (vm_copy(mach_task_self(), segs[i].vmaddr, segs[i].vmsize, newSegAddress) != KERN_SUCCESS) - #if !__arm__ // work around for - || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, true, segs[i].maxprot) != KERN_SUCCESS) - #endif - || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, false, segs[i].initprot) != KERN_SUCCESS) ) { - // can't copy so dealloc new region and run with original base address - vm_deallocate(mach_task_self(), newBaseAddress, sizeNeeded); - dyld::warn("could not relocate position independent executable\n"); - return orgMH; - } - } - // unmap original segments - vm_deallocate(mach_task_self(), lowestAddressUsed, highestAddressUsed-lowestAddressUsed); - - // run with newly mapped executable - *appsSlide = newBaseAddress - lowestAddressUsed; - return (const struct macho_header*)newBaseAddress; - } - - // can't get new range, so don't slide to random address - return orgMH; -} - - extern "C" void dyld_exceptions_init(const struct macho_header*, uintptr_t slide); // in dyldExceptions.cpp extern "C" void mach_init(); @@ -335,28 +227,27 @@ extern "C" { // 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) +uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], + intptr_t slide, const struct macho_header* dyldsMachHeader) { - // _mh_dylinker_header is magic symbol defined by static linker (ld), see - const struct macho_header* dyldsMachHeader = (const struct macho_header*)(((char*)&_mh_dylinker_header)+slide); - // 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); } - - uintptr_t appsSlide = 0; - + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // set pthread keys to dyld range + __pthread_tsd_first = 1; + _pthread_keys_init(); +#endif + // enable C++ exceptions to work inside dyld dyld_exceptions_init(dyldsMachHeader, slide); // allow dyld to use mach messaging mach_init(); - // set protection on segments (has to be done after mach_init) - segmentProtectDyld(dyldsMachHeader, slide); - // kernel sets up env pointer to be just past end of agv array const char** envp = &argv[argc+1]; @@ -367,12 +258,9 @@ uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* // run all C++ initializers inside dyld runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); - - // if main executable was linked -pie, then randomize its load address - if ( appsMachHeader->flags & MH_PIE ) - appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, envp, &appsSlide); - + // 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); } diff --git a/src/dyldLibSystemGlue.c b/src/dyldLibSystemGlue.c index 2628673..bbd8b01 100644 --- a/src/dyldLibSystemGlue.c +++ b/src/dyldLibSystemGlue.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Inc. All rights reserved. + * Copyright (c) 2009-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,28 +22,37 @@ * @APPLE_LICENSE_HEADER_END@ */ -#include +#include // -// Alter libdyld.a to not need libsystem to link with dylib1.o // This is the temporary private interface between libSystem.B.dylib and dyld // -#if __i386__ || __x86_64__ -// The compiler driver will continue to add -ldylib1.o to ppc and arm links -// so only i386 and x86_64 need this extra glue. - +// +// Long ago, the compiler driver added -ldylib1.o to every dylib which caused a +// __DATA,__dyld section to be added every dylib. The new LINKEDIT format no longer requires +// images to have a __DATA,__dyld section. But until libdyld.dylib and dyld update +// to some sort of vtable based interface, libdyld still needs a __DATA,__dyld section. +// The code below adds that section. +// struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); }; -static struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL }; +static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, 0 }; - -__attribute__((weak, visibility("hidden"))) int _dyld_func_lookup(const char* dyld_func_name, void **address) +#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED +// +// 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 +// so would break the libdyld build. Making _dyld_func_lookup weak,hidden means if +// dylib1.o is used, it overrides this, otherwise this implementation is used. +__attribute__((weak)) +#endif +__attribute__((visibility("hidden"))) +int _dyld_func_lookup(const char* dyld_func_name, void **address) { return (*myDyldSection.lookup)(dyld_func_name, address); } -#endif diff --git a/src/dyldLibSystemInterface.h b/src/dyldLibSystemInterface.h index 94062fc..874c497 100644 --- a/src/dyldLibSystemInterface.h +++ b/src/dyldLibSystemInterface.h @@ -53,10 +53,14 @@ namespace dyld { void (*acquireDyldInitializerLock)(); void (*releaseDyldInitializerLock)(); // added in version 5 - int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*)); - int (*pthread_setspecific)(pthread_key_t, const void*); + int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*)); + int (*pthread_setspecific)(pthread_key_t, const void*); // added in version 6 size_t (*malloc_size)(const void *ptr); + // added in version 7 + void* (*pthread_getspecific)(pthread_key_t); + // added in version 8 + void (*cxa_finalize)(const void*); }; #if __cplusplus }; diff --git a/src/dyldStartup.s b/src/dyldStartup.s index e1214f8..cf296ef 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -82,7 +82,7 @@ _dyld_all_image_infos: .long 0 .data __dyld_start_static_picbase: .long L__dyld_start_picbase - +Lmh: .long ___dso_handle .text .align 2 @@ -115,12 +115,16 @@ __dyld_start: movl %esp,%ebp # pointer to base of kernel frame andl $-16,%esp # force SSE alignment - # call dyldbootstrap::start(app_mh, argc, argv, slide) + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh) + subl $12,%esp call L__dyld_start_picbase L__dyld_start_picbase: popl %ebx # set %ebx to runtime value of picbase - movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax - subl %eax, %ebx # slide = L__dyld_start_picbase - [__dyld_start_static_picbase] + movl Lmh-L__dyld_start_picbase(%ebx), %ecx # ecx = prefered load address + movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax + subl %eax, %ebx # ebx = slide = L__dyld_start_picbase - [__dyld_start_static_picbase] + addl %ebx, %ecx # ecx = actual load address + pushl %ecx # param5 = actual load address pushl %ebx # param4 = slide lea 12(%ebp),%ebx pushl %ebx # param3 = argv @@ -128,7 +132,7 @@ L__dyld_start_picbase: pushl %ebx # param2 = argc movl 4(%ebp),%ebx pushl %ebx # param1 = mh - call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_ # clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer @@ -182,14 +186,15 @@ __dyld_start: movq %rsp,%rbp # pointer to base of kernel frame andq $-16,%rsp # force SSE alignment - # call dyldbootstrap::start(app_mh, argc, argv, slide) + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh) movq 8(%rbp),%rdi # param1 = mh into %rdi movl 16(%rbp),%esi # param2 = argc into %esi leaq 24(%rbp),%rdx # param3 = &argv[0] into %rdx movq __dyld_start_static(%rip), %r8 leaq __dyld_start(%rip), %rcx subq %r8, %rcx # param4 = slide into %rcx - call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + leaq ___dso_handle(%rip),%r8 # param5 = dyldsMachHeader + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_ # clean up stack and jump to result movq %rbp,%rsp # restore the unaligned stack pointer @@ -206,7 +211,8 @@ __dyld_start: .data .align 2 __dyld_start_static_picbase: - .g_long L__dyld_start_picbase + .g_long L__dyld_start_picbase +Lmh: .g_long ___dso_handle #if __ppc__ .set L_mh_offset,0 @@ -246,7 +252,7 @@ __dyld_start: stg r0,0(r1) ; terminate initial stack frame stgu r1,-SF_MINSIZE(r1); allocate minimal stack frame - ; call dyldbootstrap::start(app_mh, argc, argv, slide) + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh) lg r3,L_mh_offset(r26) ; r3 = mach_header lwz r4,L_argc_offset(r26) ; r4 = argc (int == 4 bytes) addi r5,r26,L_argv_offset ; r5 = argv @@ -256,7 +262,10 @@ L__dyld_start_picbase: addis r6,r31,ha16(__dyld_start_static_picbase-L__dyld_start_picbase) lg r6,lo16(__dyld_start_static_picbase-L__dyld_start_picbase)(r6) subf r6,r6,r31 ; r6 = slide - bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + addis r7,r31,ha16(Lmh-L__dyld_start_picbase) + lg r7,lo16(Lmh-L__dyld_start_picbase)(r7) + add r7,r6,r7 ; r7 = dyld_mh + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_ ; clean up stack and jump to result mtctr r3 ; Put entry point in count register @@ -296,10 +305,23 @@ _offset_to_dyld_all_image_infos: .space 16 + // Hack to make ___dso_handle work + // Without this local symbol, assembler will error out about in subtraction expression + // The real ___dso_handle (non-weak) sythesized by the linker + // Since this one is weak, the linker will throw this one away and use the real one instead. + .data + .globl ___dso_handle + .weak_definition ___dso_handle +___dso_handle: .long 0 + .text .align 2 __dyld_start: - // call dyldbootstrap::start(app_mh, argc, argv, slide) + mov r8, sp // save stack pointer + sub sp, #8 // make room for outgoing dyld_mh parameter + bic sp, sp, #7 // force 8-byte alignment + + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh) ldr r3, L__dyld_start_picbase_ptr L__dyld_start_picbase: @@ -307,23 +329,25 @@ L__dyld_start_picbase: ldr r3, [r0, r3] // load expected PC sub r3, r0, r3 // r3 = slide - ldr r0, [sp] // r0 = mach_header - ldr r1, [sp, #4] // r1 = argc - add r2, sp, #8 // r2 = argv + ldr r0, [r8] // r0 = mach_header + ldr r1, [r8, #4] // r1 = argc + add r2, r8, #8 // r2 = argv - mov r8, sp // save stack pointer - bic sp, sp, #7 // force 8-byte alignment + ldr r4, Lmh +L3: add r4, r4, pc // r4 = dyld_mh + str r4, [sp, #0] - bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_ // clean up stack and jump to result add sp, r8, #4 // remove the mach_header argument. bx r0 // jump to the program's entry point + .align 2 L__dyld_start_picbase_ptr: .long __dyld_start_static_picbase-L__dyld_start_picbase - +Lmh: .long ___dso_handle-L3-8 .text .align 2 @@ -366,7 +390,16 @@ _dyld_fatal_error: #error unknown architecture #endif - - +#if __arm__ + // work around for: gdb-1109: notifier in dyld does not work if it is in thumb + .text + .align 2 + .globl _gdb_image_notifier + .private_extern _gdb_image_notifier +_gdb_image_notifier: + bx lr +#endif + + diff --git a/src/dyld_debug.c b/src/dyld_debug.c index 632efab..9db993f 100644 --- a/src/dyld_debug.c +++ b/src/dyld_debug.c @@ -39,6 +39,7 @@ int dummy_dyld_symbol = 1; #include "mach-o/dyld_debug.h" #include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_priv.h" // global state set up by _dyld_debug_subscribe_to_events() and accessed by _dyld_debug_module_name() static const struct dyld_image_info* sImages = NULL; diff --git a/src/dyld_gdb.cpp b/src/dyld_gdb.cpp index 7d79aad..2afa79c 100644 --- a/src/dyld_gdb.cpp +++ b/src/dyld_gdb.cpp @@ -34,15 +34,22 @@ #include "mach-o/dyld_images.h" #include "ImageLoader.h" +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define INITIAL_UUID_IMAGE_COUNT 4 +#else + #define INITIAL_UUID_IMAGE_COUNT 32 +#endif static std::vector sImageInfos; +static std::vector sImageUUIDs; void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) { - // make initial size large enought that we probably won't need to re-alloc it + // make initial size large enough that we probably won't need to re-alloc it if ( sImageInfos.size() == 0 ) sImageInfos.reserve(INITIAL_IMAGE_COUNT); - + if ( sImageUUIDs.capacity() == 0 ) + sImageUUIDs.reserve(4); // set infoArray to NULL to denote it is in-use dyld_all_image_infos.infoArray = NULL; @@ -51,11 +58,35 @@ void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) sImageInfos.push_back(info[i]); dyld_all_image_infos.infoArrayCount = sImageInfos.size(); - // set infoArray back to base address of vector + // set infoArray back to base address of vector (other process can now read) dyld_all_image_infos.infoArray = &sImageInfos[0]; +} + +const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]) +{ // tell gdb that about the new images dyld_all_image_infos.notification(dyld_image_adding, infoCount, info); + // record initial count of images + // so CrashReporter can note which images were dynamically loaded + if ( dyld_all_image_infos.initialImageCount == 0 ) + dyld_all_image_infos.initialImageCount = infoCount; + return NULL; +} + + + +void addNonSharedCacheImageUUID(const dyld_uuid_info& info) +{ + // set uuidArray to NULL to denote it is in-use + dyld_all_image_infos.uuidArray = NULL; + + // append all new images + sImageUUIDs.push_back(info); + dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size(); + + // set uuidArray back to base address of vector (other process can now read) + dyld_all_image_infos.uuidArray = &sImageUUIDs[0]; } void removeImageFromAllImages(const struct mach_header* loadAddress) @@ -78,11 +109,30 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) // set infoArray back to base address of vector dyld_all_image_infos.infoArray = &sImageInfos[0]; + + // set uuidArrayCount to NULL to denote it is in-use + dyld_all_image_infos.uuidArray = NULL; + + // remove image from infoArray + for (std::vector::iterator it=sImageUUIDs.begin(); it != sImageUUIDs.end(); it++) { + if ( it->imageLoadAddress == loadAddress ) { + sImageUUIDs.erase(it); + break; + } + } + dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size(); + + // set infoArray back to base address of vector + dyld_all_image_infos.uuidArray = &sImageUUIDs[0]; + // tell gdb that about the new images dyld_all_image_infos.notification(dyld_image_removing, 1, &goingAway); } - +#if __arm__ +// work around for: gdb-1109: notifier in dyld does not work if it is in thumb +extern "C" void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]); +#else static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) { // do nothing @@ -93,6 +143,7 @@ static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, co //for (uint32_t i=0; i < dyld_all_image_infos.infoArrayCount; ++i) // dyld::log("dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath); } +#endif void setAlImageInfosHalt(const char* message, uintptr_t flags) { @@ -106,8 +157,11 @@ extern void* __dso_handle; #define XSTR(s) STR(s) struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) - = { 7, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, - XSTR(DYLD_VERSION) , NULL, 0, 0 }; + = { + 12, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, + XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos, + 0, dyld_error_kind_none, NULL, NULL, NULL, 0 + }; struct dyld_shared_cache_ranges dyld_shared_cache_ranges; diff --git a/src/dyld_stub_binder.s b/src/dyld_stub_binder.s index 6d7f182..0636dbd 100644 --- a/src/dyld_stub_binder.s +++ b/src/dyld_stub_binder.s @@ -125,6 +125,7 @@ dyld_stub_binder: movq %r8,R8_SAVE(%rsp) movq %r9,R9_SAVE(%rsp) movq %rax,RAX_SAVE(%rsp) +misaligned_stack_error_entering_dyld_stub_binder: movdqa %xmm0,XMMM0_SAVE(%rsp) movdqa %xmm1,XMMM1_SAVE(%rsp) movdqa %xmm2,XMMM2_SAVE(%rsp) @@ -133,6 +134,7 @@ dyld_stub_binder: movdqa %xmm5,XMMM5_SAVE(%rsp) movdqa %xmm6,XMMM6_SAVE(%rsp) movdqa %xmm7,XMMM7_SAVE(%rsp) +dyld_stub_binder_: movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) movq LP_PARAM_BP(%rbp),%rsi call __Z21_dyld_fast_stub_entryPvl @@ -160,4 +162,30 @@ dyld_stub_binder: #endif +#if __arm__ + /* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + + .text + .align 2 + .globl dyld_stub_binder +dyld_stub_binder: + stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers + add r7, sp, #16 // point FP to previous FP + + ldr r0, [sp, #24] // move address ImageLoader cache to 1st parameter + ldr r1, [sp, #28] // move lazy info offset 2nd parameter + + // call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov ip, r0 // move the symbol`s address into ip + + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers + add sp, sp, #8 // remove meta-parameters + + bx ip // jump to the symbol`s address that was bound + +#endif /* __arm__ */ diff --git a/src/glue.c b/src/glue.c index 399ad24..9f65555 100644 --- a/src/glue.c +++ b/src/glue.c @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2009 Apple Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,6 +29,7 @@ #include #include #include +#include // from _simple.h in libc typedef struct _SIMPLE* _SIMPLE_STRING; @@ -148,14 +149,46 @@ struct tm* localtime(const time_t* t) return (struct tm*)NULL; } +// malloc calls exit(-1) in case of errors... +void exit(int x) +{ + _ZN4dyld4haltEPKc("exit()"); +} + +// static initializers make calls to __cxa_atexit +void __cxa_atexit() +{ + // do nothing, dyld never terminates +} // // The stack protector routines in lib.c bring in too much stuff, so // make our own custom ones. // long __stack_chk_guard = 0; -static __attribute__((constructor)) void __guard_setup(void) +static __attribute__((constructor)) +void __guard_setup(int argc, const char* argv[], const char* envp[], const char* apple[]) { + for (const char** p = apple; *p != NULL; ++p) { + if ( strncmp(*p, "stack_guard=", 12) == 0 ) { + // kernel has provide a random value for us + for (const char* s = *p + 12; *s != '\0'; ++s) { + char c = *s; + long value = 0; + if ( (c >= 'a') && (c <= 'f') ) + value = c - 'a' + 10; + else if ( (c >= 'A') && (c <= 'F') ) + value = c - 'A' + 10; + else if ( (c >= '0') && (c <= '9') ) + value = c - '0'; + __stack_chk_guard <<= 4; + __stack_chk_guard |= value; + } + if ( __stack_chk_guard != 0 ) + return; + } + } + #if __LP64__ __stack_chk_guard = ((long)arc4random() << 32) | arc4random(); #else @@ -169,3 +202,62 @@ void __stack_chk_fail() } +// std::_throw_bad_alloc() +void _ZSt17__throw_bad_allocv() +{ + _ZN4dyld4haltEPKc("__throw_bad_alloc()"); +} + +// std::_throw_length_error(const char* x) +void _ZSt20__throw_length_errorPKc() +{ + _ZN4dyld4haltEPKc("_throw_length_error()"); +} + +// the libc.a version of this drags in ASL +void __chk_fail() +{ + _ZN4dyld4haltEPKc("__chk_fail()"); +} + + +// referenced by libc.a(pthread.o) but unneeded in dyld +void _init_cpu_capabilities() { } +void _cpu_capabilities() {} +void set_malloc_singlethreaded() {} +int PR_5243343_flag = 0; + + +// used by some pthread routines +char* mach_error_string(mach_error_t err) +{ + return (char *)"unknown error code"; +} +char* mach_error_type(mach_error_t err) +{ + return (char *)"(unknown/unknown)"; +} + +// _pthread_reap_thread calls fprintf(stderr). +// We map fprint to _simple_vdprintf and ignore FILE* stream, so ok for it to be NULL +#if !__ppc__ +FILE* __stderrp = NULL; +FILE* __stdoutp = NULL; +#endif + +// work with c++abi.a +void (*__cxa_terminate_handler)() = _ZSt9terminatev; +void (*__cxa_unexpected_handler)() = _ZSt10unexpectedv; + +void abort_message(const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); +} + +void __cxa_bad_typeid() +{ + _ZN4dyld4haltEPKc("__cxa_bad_typeid()"); +} diff --git a/src/threadLocalHelpers.s b/src/threadLocalHelpers.s new file mode 100644 index 0000000..b5bc9d5 --- /dev/null +++ b/src/threadLocalHelpers.s @@ -0,0 +1,126 @@ +/* + * 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@ + */ + + + +#if __x86_64__ + // returns address of TLV in %rax, all other registers preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + movq 8(%rdi),%rax // get key from descriptor + movq %gs:0x0(,%rax,8),%rax // get thread value + testq %rax,%rax // if NULL, lazily allocate + je LlazyAllocate + addq 16(%rdi),%rax // add offset from descriptor + ret +LlazyAllocate: + pushq %rbp + movq %rsp, %rbp + subq $592,%rsp + movq %rdi,-8(%rbp) + movq %rsi,-16(%rbp) + movq %rdx,-24(%rbp) + movq %rcx,-32(%rbp) + movq %r8,-40(%rbp) + movq %r9,-48(%rbp) + movq %r10,-56(%rbp) + movq %r11,-64(%rbp) + fxsave -592(%rbp) + movq 8(%rdi),%rdi // get key from descriptor + call _tlv_allocate_and_initialize_for_key + fxrstor -592(%rbp) + movq -64(%rbp),%r11 + movq -56(%rbp),%r10 + movq -48(%rbp),%r9 + movq -40(%rbp),%r8 + movq -32(%rbp),%rcx + movq -24(%rbp),%rdx + movq -16(%rbp),%rsi + movq -8(%rbp),%rdi + addq 16(%rdi),%rax // result = buffer + offset + addq $592,%rsp + popq %rbp + ret +#endif + + + +#if __i386__ + // returns address of TLV in %eax, all other registers (except %ecx) preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + movl 4(%eax),%ecx // get key from descriptor + movl %gs:0x0(,%ecx,4),%ecx // get thread value + testl %ecx,%ecx // if NULL, lazily allocate + je LlazyAllocate + movl 8(%eax),%eax // add offset from descriptor + addl %ecx,%eax + ret +LlazyAllocate: + pushl %ebp + movl %esp,%ebp + pushl %edx // save edx + subl $548,%esp + movl %eax,-8(%ebp) // save descriptor + lea -528(%ebp),%ecx // get 512 byte buffer in frame + and $-16, %ecx // 16-byte align buffer for fxsave + fxsave (%ecx) + movl 4(%eax),%ecx // get key from descriptor + movl %ecx,(%esp) // push key parameter, also leaves stack aligned properly + call _tlv_allocate_and_initialize_for_key + movl -8(%ebp),%ecx // get descriptor + movl 8(%ecx),%ecx // get offset from descriptor + addl %ecx,%eax // add offset to buffer + lea -528(%ebp),%ecx + and $-16, %ecx // 16-byte align buffer for fxrstor + fxrstor (%ecx) + addl $548,%esp + popl %edx // restore edx + popl %ebp + ret +#endif + + +#if 0 +#if __arm__ + // returns address of TLV in r0, all other registers preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + push {r1,r2,r3,r7,lr} + mov r7,r0 // save descriptor in r7 + ldr r0, [r7, #4] // get key from descriptor + bl _pthread_getspecific // get thread value + cmp r0, #0 + bne L2 // if NULL, lazily allocate + ldr r0, [r7, #4] // get key from descriptor + bl _tlv_allocate_and_initialize_for_key +L2: ldr r1, [r7, #8] // get offset from descriptor + add r0, r1, r0 // add offset into allocation block + pop {r1,r2,r3,r7,pc} +#endif +#endif + + diff --git a/src/threadLocalVariables.c b/src/threadLocalVariables.c new file mode 100644 index 0000000..2586455 --- /dev/null +++ b/src/threadLocalVariables.c @@ -0,0 +1,471 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_priv.h" + + +#if __LP64__ + typedef struct mach_header_64 macho_header; + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + typedef struct segment_command_64 macho_segment_command; + typedef struct section_64 macho_section; +#else + typedef struct mach_header macho_header; + #define LC_SEGMENT_COMMAND LC_SEGMENT + typedef struct segment_command macho_segment_command; + typedef struct section macho_section; +#endif + +#ifndef S_THREAD_LOCAL_REGULAR +#define S_THREAD_LOCAL_REGULAR 0x11 +#endif + +#ifndef S_THREAD_LOCAL_ZEROFILL +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLES +#define S_THREAD_LOCAL_VARIABLES 0x13 +#endif + +#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#endif + +#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 +#endif + +#ifndef MH_HAS_TLV_DESCRIPTORS + #define MH_HAS_TLV_DESCRIPTORS 0x800000 +#endif + +#if __i386__ || __x86_64__ + +typedef struct TLVHandler { + struct TLVHandler *next; + dyld_tlv_state_change_handler handler; + enum dyld_tlv_states state; +} TLVHandler; + +// lock-free prepend-only linked list +static TLVHandler * volatile tlv_handlers = NULL; + + +struct TLVDescriptor +{ + void* (*thunk)(struct TLVDescriptor*); + unsigned long key; + unsigned long offset; +}; +typedef struct TLVDescriptor TLVDescriptor; + + +// implemented in assembly +extern void* tlv_get_addr(TLVDescriptor*); + +struct TLVImageInfo +{ + pthread_key_t key; + const struct mach_header* mh; +}; +typedef struct TLVImageInfo TLVImageInfo; + +static TLVImageInfo* tlv_live_images = NULL; +static unsigned int tlv_live_image_alloc_count = 0; +static unsigned int tlv_live_image_used_count = 0; +static pthread_mutex_t tlv_live_image_lock = PTHREAD_MUTEX_INITIALIZER; + +static void tlv_set_key_for_image(const struct mach_header* mh, pthread_key_t key) +{ + pthread_mutex_lock(&tlv_live_image_lock); + if ( tlv_live_image_used_count == tlv_live_image_alloc_count ) { + unsigned int newCount = (tlv_live_images == NULL) ? 8 : 2*tlv_live_image_alloc_count; + struct TLVImageInfo* newBuffer = malloc(sizeof(TLVImageInfo)*newCount); + if ( tlv_live_images != NULL ) { + memcpy(newBuffer, tlv_live_images, sizeof(TLVImageInfo)*tlv_live_image_used_count); + free(tlv_live_images); + } + tlv_live_images = newBuffer; + tlv_live_image_alloc_count = newCount; + } + tlv_live_images[tlv_live_image_used_count].key = key; + tlv_live_images[tlv_live_image_used_count].mh = mh; + ++tlv_live_image_used_count; + pthread_mutex_unlock(&tlv_live_image_lock); +} + +static const struct mach_header* tlv_get_image_for_key(pthread_key_t key) +{ + const struct mach_header* result = NULL; + pthread_mutex_lock(&tlv_live_image_lock); + for(unsigned int i=0; i < tlv_live_image_used_count; ++i) { + if ( tlv_live_images[i].key == key ) { + result = tlv_live_images[i].mh; + break; + } + } + pthread_mutex_unlock(&tlv_live_image_lock); + return result; +} + + +static void +tlv_notify(enum dyld_tlv_states state, void *buffer) +{ + if (!tlv_handlers) return; + + // Always use malloc_size() to ensure allocated and deallocated states + // send the same size. tlv_free() doesn't have anything else recorded. + dyld_tlv_info info = { sizeof(info), buffer, malloc_size(buffer) }; + + for (TLVHandler *h = tlv_handlers; h != NULL; h = h->next) { + if (h->state == state && h->handler) { + h->handler(h->state, &info); + } + } +} + + +// called lazily when TLV is first accessed +__attribute__((visibility("hidden"))) +void* tlv_allocate_and_initialize_for_key(pthread_key_t key) +{ + const struct mach_header* mh = tlv_get_image_for_key(key); + // first pass, find size and template + uint8_t* start = NULL; + unsigned long size; + intptr_t slide = 0; + bool slideComputed = false; + bool hasInitializers = 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) { + if ( cmd->cmd == LC_SEGMENT_COMMAND) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( !slideComputed && (seg->filesize != 0) ) { + slide = (uintptr_t)mh - seg->vmaddr; + slideComputed = true; + } + 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) { + switch ( sect->flags & SECTION_TYPE ) { + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + hasInitializers = true; + break; + case S_THREAD_LOCAL_ZEROFILL: + case S_THREAD_LOCAL_REGULAR: + if ( start == NULL ) { + // first of N contiguous TLV template sections, record as if this was only section + start = (uint8_t*)(sect->addr + slide); + size = sect->size; + } + else { + // non-first of N contiguous TLV template sections, accumlate values + const uint8_t* newEnd = (uint8_t*)(sect->addr + slide + sect->size); + size = newEnd - start; + } + break; + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // allocate buffer and fill with template + void* buffer = malloc(size); + memcpy(buffer, start, size); + + // set this thread's value for key to be the new buffer. + pthread_setspecific(key, buffer); + + // send tlv state notifications + tlv_notify(dyld_tlv_state_allocated, buffer); + + // second pass, run initializers + if ( hasInitializers ) { + 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; + 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 ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_INIT_FUNCTION_POINTERS ) { + typedef void (*InitFunc)(void); + InitFunc* funcs = (InitFunc*)(sect->addr + slide); + const uint32_t count = sect->size / sizeof(uintptr_t); + for (uint32_t i=count; i > 0; --i) { + InitFunc func = funcs[i-1]; + func(); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + return buffer; +} + + +// pthread destructor for TLV storage +static void +tlv_free(void *storage) +{ + tlv_notify(dyld_tlv_state_deallocated, storage); + free(storage); +} + + +// called when image is loaded +static void tlv_initialize_descriptors(const struct mach_header* mh) +{ + pthread_key_t key = 0; + intptr_t slide = 0; + bool slideComputed = 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) { + if ( cmd->cmd == LC_SEGMENT_COMMAND) { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( !slideComputed && (seg->filesize != 0) ) { + slide = (uintptr_t)mh - seg->vmaddr; + slideComputed = true; + } + 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 ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_VARIABLES ) { + if ( sect->size != 0 ) { + // allocate pthread key when we first discover this image has TLVs + if ( key == 0 ) { + int result = pthread_key_create(&key, &tlv_free); + if ( result != 0 ) + abort(); + tlv_set_key_for_image(mh, key); + } + // initialize each descriptor + TLVDescriptor* start = (TLVDescriptor*)(sect->addr + slide); + TLVDescriptor* end = (TLVDescriptor*)(sect->addr + sect->size + slide); + for (TLVDescriptor* d=start; d < end; ++d) { + d->thunk = tlv_get_addr; + d->key = key; + //d->offset = d->offset; // offset unchanged + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +// called by dyld when a image is loaded +static const char* tlv_load_notification(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + // this is called on all images, even those without TLVs, so we want + // this to be fast. The linker sets MH_HAS_TLV_DESCRIPTORS so we don't + // have to search images just to find the don't have TLVs. + for (uint32_t i=0; i < infoCount; ++i) { + if ( info[i].imageLoadAddress->flags & MH_HAS_TLV_DESCRIPTORS ) + tlv_initialize_descriptors(info[i].imageLoadAddress); + } + return NULL; +} + + +void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) +{ + TLVHandler *h = malloc(sizeof(TLVHandler)); + h->state = state; + h->handler = Block_copy(handler); + + TLVHandler *old; + do { + old = tlv_handlers; + h->next = old; + } while (! OSAtomicCompareAndSwapPtrBarrier(old, h, (void * volatile *)&tlv_handlers)); +} + + +void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +{ + pthread_mutex_lock(&tlv_live_image_lock); + unsigned int count = tlv_live_image_used_count; + void *list[count]; + for (unsigned int i = 0; i < count; ++i) { + list[i] = pthread_getspecific(tlv_live_images[i].key); + } + pthread_mutex_unlock(&tlv_live_image_lock); + + for (unsigned int i = 0; i < count; ++i) { + if (list[i]) { + dyld_tlv_info info = { sizeof(info), list[i], malloc_size(list[i]) }; + handler(dyld_tlv_state_allocated, &info); + } + } +} + + +// +// thread_local terminators +// +// C++ 0x allows thread_local C++ objects which have constructors run +// on the thread before any use of the object and the object's destructor +// is run on the thread when the thread terminates. +// +// To support this libdyld gets a pthread key early in process start up and +// uses tlv_finalize and the key's destructor function. This key must be +// allocated before any thread local variables are instantiated because when +// a thread is terminated, the pthread package runs the destructor function +// on each key's storage values in key allocation order. Since we want +// C++ objects to be destructred before they are deallocated, we need the +// destructor key to come before the deallocation key. +// + +typedef void (*TermFunc)(void*); +struct TLVTerminatorListEntry +{ + TermFunc termFunc; + void* objAddr; +}; + +struct TLVTerminatorList +{ + uint32_t allocCount; + uint32_t useCount; + struct TLVTerminatorListEntry entries[1]; // variable length +}; + + +static pthread_key_t tlv_terminators_key = 0; + +void _tlv_atexit(TermFunc func, void* objAddr) +{ + // NOTE: this does not need locks because it only operates on current thread data + struct TLVTerminatorList* list = (struct TLVTerminatorList*)pthread_getspecific(tlv_terminators_key); + if ( list == NULL ) { + // handle first allocation + list = (struct TLVTerminatorList*)malloc(offsetof(struct TLVTerminatorList, entries[1])); + list->allocCount = 1; + list->useCount = 1; + list->entries[0].termFunc = func; + list->entries[0].objAddr = objAddr; + pthread_setspecific(tlv_terminators_key, list); + } + else { + if ( list->allocCount == list->allocCount ) { + // handle resizing allocation + uint32_t newAllocCount = list->allocCount * 2; + uint32_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); + struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)malloc(newAllocSize); + newlist->allocCount = newAllocCount; + newlist->useCount = list->useCount; + for(uint32_t i=0; i < list->useCount; ++i) + newlist->entries[i] = list->entries[i]; + pthread_setspecific(tlv_terminators_key, newlist); + free(list); + list = newlist; + } + // handle appending new entry + list->entries[list->useCount].termFunc = func; + list->entries[list->useCount].objAddr = objAddr; + list->useCount += 1; + } +} + +// called by pthreads when the current thread is going way and +// _tlv_atexit() has been called on the thread. +static void tlv_finalize(void* storage) +{ + struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage; + for(uint32_t i=0; i < list->useCount; ++i) { + struct TLVTerminatorListEntry* entry = &list->entries[i]; + if ( entry->termFunc != NULL ) { + (*entry->termFunc)(entry->objAddr); + } + } + free(storage); +} + + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ + // create pthread key to handle thread_local destructors + // NOTE: this key must be allocated before any keys for TLV + // so that _pthread_tsd_cleanup will run destructors before deallocation + (void)pthread_key_create(&tlv_terminators_key, &tlv_finalize); + + // register with dyld for notification when images are loaded + dyld_register_image_state_change_handler(dyld_image_state_bound, true, tlv_load_notification); +} + + +// linked images with TLV have references to this symbol, but it is never used at runtime +void _tlv_bootstrap() +{ + abort(); +} + + +// __i386__ || __x86_64__ +#else +// !(__i386__ || __x86_64__) + + +void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) +{ +} + +void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) +{ +} + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ +} + + +// !(__i386__ || __x86_64__) +#endif + + diff --git a/unit-tests/bin/build-results-filter.pl b/unit-tests/bin/build-results-filter.pl new file mode 100755 index 0000000..fa6d106 --- /dev/null +++ b/unit-tests/bin/build-results-filter.pl @@ -0,0 +1,114 @@ +#!/usr/bin/perl -w + +use strict; +use Data::Dumper; +use File::Find; +use Cwd; + +$Data::Dumper::Terse = 1; + +my $root = undef; +my $entry = ''; +my $pass_count = 0; +my $total_count = 0; + +# first match "root: " + +# a line starting with "cwd:" marks the beginning of a new test case +# call process_entry() on each test case +while(<>) +{ + if(m/^root:\s+(.*?)$/) + { + $root = $1; + } + elsif(m/^cwd:\s+(.*?)$/) + { + if(length($entry)) + { + &process_entry($root, $entry); + $entry = ''; + } + $entry .= $_; + } + else + { + $entry .= $_; + } +} +# don't forget last test case (no cwd: to mark end) +if(length($entry)) +{ + &process_entry($root, $entry); +} + +# show totals +my $percentage = $pass_count * 100 / $total_count; +printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; + + +sub process_entry +{ + my ($root, $lines) = @_; + + # build an associative array of keys to value(s) + my $lines_seq = [split /\n/, $lines]; + #print Dumper($lines_seq); + my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] }; + my $line; + foreach $line (@$lines_seq) + { + if($line =~ m/^(\w+):\s+(.*)$/) + { + my $key = $1; + my $val = $2; + if(!exists($$tbl{$key})) + { $$tbl{$key} = ''; } + + if($key eq 'stdout' || $key eq 'stderr') # if type is @array + { + push @{$$tbl{$key}}, $val; + } + else + { + $$tbl{$key} .= $val; + } + } + else + { + print "ERROR: $line"; + } + } + #print Dumper($tbl); + #return; + + my $test_name = $$tbl{cwd}; + if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|) + { + $test_name = $1; + } + + #if there was any output to stderr, mark this as a failure + my $some_errors = 0; + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + $some_errors = 1; + } + if ( $some_errors ) + { + return; + } + + #if make failed (exit was non-zero), mark this as a failure + if(0 ne $$tbl{exit}) + { + printf "%-40s FAIL Makefile failure\n", $test_name; + $total_count++; + return; + } + + $pass_count++; + $total_count++; +} diff --git a/unit-tests/bin/exit-non-zero-pass.pl b/unit-tests/bin/exit-non-zero-pass.pl index f29a1bc..7a09a1b 100755 --- a/unit-tests/bin/exit-non-zero-pass.pl +++ b/unit-tests/bin/exit-non-zero-pass.pl @@ -39,5 +39,6 @@ while() print $_; } close(OUT) || die("$!"); +unlink "/tmp/exit-non-zero.tmp"; exit 0; diff --git a/unit-tests/bin/exit-zero-pass.pl b/unit-tests/bin/exit-zero-pass.pl index 43de2a3..bc0a99d 100755 --- a/unit-tests/bin/exit-zero-pass.pl +++ b/unit-tests/bin/exit-zero-pass.pl @@ -38,5 +38,6 @@ while() print $_; } close(OUT) || die("$!"); +unlink "/tmp/exit-zero-pass.tmp"; exit 0; diff --git a/unit-tests/build-and-run-iPhoneOS-unit-tests b/unit-tests/build-and-run-iPhoneOS-unit-tests new file mode 100755 index 0000000..a572912 --- /dev/null +++ b/unit-tests/build-and-run-iPhoneOS-unit-tests @@ -0,0 +1,14 @@ +#!/bin/sh + +# need to be root to build test suite +sudo ./build-iPhoneOS-unit-tests + +# transfer to device +echo " * * * Transfering to device * * *" +rsync -a /tmp/unpack-and-run-all-tests /tmp/dyld-testing.cpgz rsync://root@localhost:10873/root/tmp + + +# running on device +echo " * * * Running on device * * *" +/Developer/Platforms/iPhoneOS.platform/usr/local/bin/PurpleExec /tmp/unpack-and-run-all-tests + diff --git a/unit-tests/build-iPhoneOS-unit-tests b/unit-tests/build-iPhoneOS-unit-tests new file mode 100755 index 0000000..21415cd --- /dev/null +++ b/unit-tests/build-iPhoneOS-unit-tests @@ -0,0 +1,52 @@ +#!/bin/sh + +# cd into test-cases directory +TEST_CASE_DIR=`echo "$0" | sed 's/build-iPhoneOS-unit-tests/test-cases/'` +cd ${TEST_CASE_DIR} +TEST_CASE_DIR=`pwd` + +# make clean +../bin/make-recursive.pl clean > /dev/null + +# clean staging area +rm -rf /var/root/testing +mkdir /var/root/testing + +# create scripts to run test cases on device +echo "#!/bin/sh" > /var/root/testing/run-all-tests +chmod +x /var/root/testing/run-all-tests + +# do every combination of OS version and architectures +for os in "4.3" "3.0" +do + for arch in armv6 thumb armv7 thumb2 + do + # make copy of tests + cp -r ${TEST_CASE_DIR}/../../unit-tests /var/root/testing/unit-tests-${arch}-${os} + # build but don't run test cases + echo " * * * Building all unit tests for ${arch} iPhoneOS ${os} * * *" + cd /var/root/testing/unit-tests-${arch}-${os}/test-cases + ../bin/make-recursive.pl ARCH=${arch} OS_VERSION=${os} OS_NAME=iPhoneOS all | ../bin/build-results-filter.pl + # update script + echo "cd /var/root/testing/unit-tests-${arch}-${os}" >> /var/root/testing/run-all-tests + echo "echo \" * * * Running all unit tests for ${arch} iPhoneOS ${os} * * *\"" >> /var/root/testing/run-all-tests + echo "bin/make-recursive.pl OS_NAME=iPhoneOS check | bin/result-filter.pl" >> /var/root/testing/run-all-tests + done +done + +# tar up all test cases +echo " * * * Making archive * * *" +cd /var/root +ditto -c -z testing /tmp/dyld-testing.cpgz + +# create script to unpack on device +echo "#!/bin/sh" > /tmp/unpack-and-run-all-tests +echo "echo \" * * * Unpacking test cases * * *\"" >> /tmp/unpack-and-run-all-tests +echo "/sbin/mount -u /private/var" >> /tmp/unpack-and-run-all-tests +echo "chmod +x /var/root" >> /tmp/unpack-and-run-all-tests +echo "cd /var/root" >> /tmp/unpack-and-run-all-tests +echo "rm -rf /var/root/testing" >> /tmp/unpack-and-run-all-tests +echo "ditto -x /tmp/dyld-testing.cpgz testing" >> /tmp/unpack-and-run-all-tests +echo "/var/root/testing/run-all-tests" >> /tmp/unpack-and-run-all-tests +chmod +x /tmp/unpack-and-run-all-tests + diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index f94abba..461ebb0 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -2,21 +2,66 @@ SHELL = /bin/sh -# set default to be host -ARCH ?= $(shell arch) +# set default OS to be current Mac OS X +OS_NAME ?= MacOSX +ifeq "$(OS_NAME)" "iPhoneOS" + OS_VERSION ?= 3.1 + ifeq "$(OS_VERSION)" "4.3" + OS_BAROLO_FEATURES = 1 + endif + ARCH ?= armv6 + VALID_ARCHS ?= armv6 +else + OS_VERSION ?= 10.7 + ifeq "$(OS_VERSION)" "10.7" + OS_BAROLO_FEATURES = 1 + endif + ARCH ?= $(shell arch) + VALID_ARCHS ?= "i386 x86_64" +endif -# set default to be all -VALID_ARCHS ?= "ppc i386 x86_64" +ifeq "$(OS_NAME)" "iPhoneOS" + CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.Internal.sdk + CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.Internal.sdk +# CC = gcc-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) +# CXX = g++-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) +else + CC = gcc-4.2 -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) + CXX = g++-4.2 -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) +endif -CC = gcc-4.2 -arch ${ARCH} -CCFLAGS = -Wall -std=c99 - -CXX = g++-4.2 -arch ${ARCH} -CXXFLAGS = -Wall +CCFLAGS = -Wall -std=c99 +CXXFLAGS = -Wall RM = rm RMFLAGS = -rf SAFE_RUN = ${TESTROOT}/bin/fail-if-non-zero.pl PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl +PASS_IFF_FAILURE = $(TESTROOT)/bin/exit-non-zero-pass.pl + +ifeq ($(ARCH),armv7) + CCFLAGS += -mno-thumb + CXXFLAGS += -mno-thumb + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif + +ifeq ($(ARCH),thumb) + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv6 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif +ifeq ($(ARCH),thumb2) + CCFLAGS += -mthumb + CXXFLAGS += -mthumb + override ARCH = armv7 + override FILEARCH = arm +else + FILEARCH = $(ARCH) +endif diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index 45f846f..91659ba 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -7,57 +7,32 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` CRSTATE=`defaults read com.apple.CrashReporter DialogType` defaults write com.apple.CrashReporter DialogType basic -echo "" -echo " * * * Running all unit tests for 32-bits * * *" - -# make clean -../bin/make-recursive.pl clean > /dev/null - -# build default architecture -../bin/make-recursive.pl | ../bin/result-filter.pl - -# if G5, then also run all test cases built for ppc64 -if [ `machine` = "ppc970" ] -then +# run test targeting different OS versions +for OSVERSION in 10.7 10.6 10.5 10.4 +do echo "" - echo " * * * Running all unit tests for 64-bits * * *" - + echo " * * * Running all unit tests i386 built for $OSVERSION * * *" + # make clean ../bin/make-recursive.pl clean > /dev/null - # build 64-bit architecture - ../bin/make-recursive.pl ARCH="ppc64" | ../bin/result-filter.pl -fi - -# if Intel, then also run all test cases under emulation -if [ "`sysctl -n hw.machine`" = "i386" ] -then - - if [ -x /usr/libexec/oah/translate ] - then - echo "" - echo " * * * Running all unit tests for emulated 32-bits * * *" - - # make clean - ../bin/make-recursive.pl clean > /dev/null - - # build ppc architecture - ../bin/make-recursive.pl ARCH="ppc" | ../bin/result-filter.pl - fi + # build default architecture + ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl # if 64-bit capable Intel, then also run all test cases for 64-bits if [ `sysctl -n hw.optional.x86_64` = "1" ] then echo "" - echo " * * * Running all unit tests for 64-bits * * *" + echo " * * * Running all unit tests x86_64 built for $OSVERSION * * *" # make clean ../bin/make-recursive.pl clean > /dev/null # build x86_64 architecture - ../bin/make-recursive.pl ARCH="x86_64" | ../bin/result-filter.pl + ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl fi -fi + +done # restore crash reporter state defaults write com.apple.CrashReporter DialogType ${CRSTATE} 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_LIBRARY_PATH-dyld_env/foo.c b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,47 @@ +/* + * 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_FRAMEWORK_PATH-basic/Makefile b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile new file mode 100644 index 0000000..36bf14e --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile @@ -0,0 +1,48 @@ +## +# 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: + ./main 10 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9" && ./main 10 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12 + +all: + mkdir -p Foo.framework alt11/Foo.framework alt9/Foo.framework alt12/Foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -install_name "${PWD}/Foo.framework/Foo" -o Foo.framework/Foo + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c Foo.framework/Foo + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Foo.framework/Foo" -o alt11/Foo.framework/Foo + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/Foo.framework/Foo" -o alt9/Foo.framework/Foo + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/Foo.framework/Foo" -o alt12/Foo.framework/Foo + + +clean: + ${RM} -rf main Foo.framework alt9 alt11 alt12 + diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} 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 new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c @@ -0,0 +1,47 @@ +/* + * 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-basic/Makefile b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile new file mode 100644 index 0000000..2be1439 --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile @@ -0,0 +1,48 @@ +## +# 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: + ./main 10 + export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt9" && ./main 10 + export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12 + +all: + mkdir -p alt11 alt9 alt12 + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c 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 + + +clean: + ${RM} -rf main libfoo.dylib alt9 alt11 alt12 + diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + 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 new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c @@ -0,0 +1,47 @@ +/* + * 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/Makefile b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile new file mode 100644 index 0000000..eeaef6e --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile @@ -0,0 +1,58 @@ +## +# 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 11 + ./main911b 11 + ./main1112 12 + ./main1112b 12 + ./main1211 12 + ./main1211b 12 + +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 -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main11 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_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_LIBRARY_PATH-dyld_env-restrict/foo.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c @@ -0,0 +1,5 @@ + +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 new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c @@ -0,0 +1,47 @@ +/* + * 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/Makefile b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/Makefile new file mode 100644 index 0000000..357a34e --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_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 11 + ./main911b 11 + ./main911c 11 + ./main1112 12 + ./main1112b 12 + ./main1211 12 + ./main1211b 12 + export DYLD_LIBRARY_PATH=./alt9 && ./main11 9 + +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_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911c main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@executable_path/alt9:@executable_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11 + + +clean: + ${RM} -rf libfoo.dylib alt9 alt11 alt12 main10 main11 main9 main911 main911b main911c main1112 main1112b main1211 main1211b + 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 new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +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 new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,47 @@ +/* + * 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/main.c b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c index 9442bf1..bcb2bea 100644 --- a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c +++ b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,19 +23,23 @@ #include #include #include - +#include + #include "test.h" /// rdar://problem/4058724 int main() { +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // test that image can be found via install path const struct mach_header * mh1 = NSAddImage("libbar.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME); const struct mach_header * mh2 = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME); - if ( mh2 != NULL ) - PASS("NSAddImage-MATCH_BY_INSTALLNAME"); - else + if ( mh2 == NULL ) FAIL("NSAddImage-MATCH_BY_INSTALLNAME"); + else +#endif + PASS("NSAddImage-MATCH_BY_INSTALLNAME"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c index e8a3320..3f84ea1 100644 --- a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c +++ b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" @@ -30,11 +31,14 @@ int main() { +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // test that NSAddImage() does not crash if image is not loaded const struct mach_header * mh = NSAddImage("/System/Library/Frameworks/Cocoa.framework/Cocoa", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); - if ( mh == NULL ) - PASS("NSAddImage-RETURN_ONLY_IF_LOADED"); - else + if ( mh != NULL ) FAIL("NSAddImage-RETURN_ONLY_IF_LOADED"); + else +#endif + PASS("NSAddImage-RETURN_ONLY_IF_LOADED"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/NSAddImage-leafname/main.c b/unit-tests/test-cases/NSAddImage-leafname/main.c index 28922af..f1ad630 100644 --- a/unit-tests/test-cases/NSAddImage-leafname/main.c +++ b/unit-tests/test-cases/NSAddImage-leafname/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" @@ -30,11 +31,14 @@ int main() { +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // test that NSAddImage() uses fallback path when given a leaf name const struct mach_header * mh = NSAddImage("libzzz.dylib", NSADDIMAGE_OPTION_WITH_SEARCHING); - if ( mh != NULL ) - PASS("NSAddImage-leafname"); - else + if ( mh == NULL ) FAIL("NSAddImage-leafname"); + else +#endif + PASS("NSAddImage-leafname"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c b/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c index 9ac5211..09fea73 100644 --- a/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c +++ b/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include // fprintf(), NULL #include // exit(), EXIT_SUCCESS #include +#include #include @@ -32,7 +33,10 @@ int main() { +// NSAddressOfSymbol is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSAddressOfSymbol(NULL); +#endif PASS("NSAddressOfSymbol-NULL"); return 0; diff --git a/unit-tests/test-cases/addend/main.c b/unit-tests/test-cases/addend/main.c index fd8099b..006adaa 100644 --- a/unit-tests/test-cases/addend/main.c +++ b/unit-tests/test-cases/addend/main.c @@ -57,4 +57,5 @@ int main() } PASS("addend"); + return 0; } 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-cache-slide/main.c b/unit-tests/test-cases/all_image_infos-cache-slide/main.c new file mode 100644 index 0000000..0a550e4 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-cache-slide/main.c @@ -0,0 +1,82 @@ +/* + * 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 +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static uintptr_t libSystemSlide() +{ + Dl_info info; + if ( dladdr(&malloc, &info) == 0 ) { + FAIL("all_image_infos-cache-slide: dladdr(&malloc, xx) failed"); + exit(0); + } + + const struct mach_header* mallocMh = (struct mach_header*)info.dli_fbase; + if ( (mallocMh->flags & 0x80000000) == 0 ) { + FAIL("all_image_infos-cache-slide: image containing _malloc not in shared cache"); + exit(0); + } + + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + if ( mallocMh == _dyld_get_image_header(i) ) { + return _dyld_get_image_vmaddr_slide(i); + } + } + + FAIL("all_image_infos-cache-slide: slide of image containing _malloc not found"); + exit(0); +} + + +int main(int argc, const char* argv[]) +{ + 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) ) { + FAIL("all_image_infos-cache-slide: task_info() failed"); + return 0; + } + struct dyld_all_image_infos* infos = (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; + + if ( infos->version < 12 ) { + FAIL("all_image_infos-cache-slide: version < 12"); + return 0; + } + + if ( libSystemSlide() != infos->sharedCacheSlide ) { + FAIL("all_image_infos-cache-slide: dyld infos sharedCacheSlide (0x%08X) does not match computed slide (0x%08X)", + infos->sharedCacheSlide, libSystemSlide()); + return 0; + } + + PASS("all_image_infos-cache-slide"); + return EXIT_SUCCESS; +} 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-duplicate/foo.c b/unit-tests/test-cases/all_image_infos-duplicate/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-duplicate/foo.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/all_image_infos-duplicate/main.c b/unit-tests/test-cases/all_image_infos-duplicate/main.c new file mode 100644 index 0000000..b70ac9c --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-duplicate/main.c @@ -0,0 +1,70 @@ +/* + * 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 +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + 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) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libfoodup.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoodup.dylib", dlerror()); + exit(0); + } + + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath); + if ( strlen(infos->infoArray[i].imageFilePath) < 5 ) { + FAIL("all_image_infos-duplicates: bad entry for address 0x%08X", infos->infoArray[i].imageLoadAddress); + return EXIT_SUCCESS; + } + } + + PASS("all_image_infos-duplicates"); + return EXIT_SUCCESS; +} 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-paths/foo.c b/unit-tests/test-cases/all_image_infos-paths/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-paths/foo.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/all_image_infos-paths/main.c b/unit-tests/test-cases/all_image_infos-paths/main.c new file mode 100644 index 0000000..7e38b8c --- /dev/null +++ b/unit-tests/test-cases/all_image_infos-paths/main.c @@ -0,0 +1,68 @@ +/* + * 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 +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + 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) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main(int argc, const char* argv[]) +{ + char path[1024]; + strcpy(path, argv[1]); + + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + strcpy(path, "bad"); + + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath); + if ( strcmp(infos->infoArray[i].imageFilePath, argv[1]) == 0 ) { + PASS("all_image_infos-path"); + return EXIT_SUCCESS; + } + } + + FAIL("all_image_infos-path"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/all_image_infos/main.c b/unit-tests/test-cases/all_image_infos/main.c index 30a8427..efff2b8 100644 --- a/unit-tests/test-cases/all_image_infos/main.c +++ b/unit-tests/test-cases/all_image_infos/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,35 +25,12 @@ #include #include #include +#include #include "test.h" extern struct mach_header __dso_handle; -#ifndef DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET - #define DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET 0x1010 -#endif - - -#if __i386__ || __ppc__ - #define DYLD_BASE_ADDRESS 0x8fe00000 -#elif __x86_64__ || __ppc64__ - #define DYLD_BASE_ADDRESS 0x7fff5fc00000 -#elif __arm__ - #define DYLD_BASE_ADDRESS 0x2fe00000 -#endif - -struct dyld_all_image_infos* getImageInfos() -{ - uint32_t offset = *((uint32_t*)(DYLD_BASE_ADDRESS+DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET)); - if ( offset > 300000 ) { - FAIL("all_image_infos: offset appears to be outside dyld"); - exit(0); - } - uintptr_t addr = DYLD_BASE_ADDRESS + offset; - return (struct dyld_all_image_infos*)addr; -} - struct dyld_all_image_infos* getImageInfosFromKernel() { @@ -71,9 +48,9 @@ struct dyld_all_image_infos* getImageInfosFromKernel() int main() { - struct dyld_all_image_infos* infos = getImageInfos(); - if ( infos->version != 7 ) { - FAIL("all_image_infos: dyld_all_image_infos is not version 7"); + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + if ( infos->version < 9 ) { + FAIL("all_image_infos: dyld_all_image_infos is not version >= 9, is %d", infos->version); exit(0); } @@ -82,15 +59,25 @@ main() exit(0); } + //for( int i=0; i < infos->infoArrayCount; ++i) { + // fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + //} + if ( infos->infoArray[0].imageLoadAddress != &__dso_handle ) { - FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong"); + FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong, infos->infoArray[0].imageLoadAddress=0x%08lX vs 0x%08X", + infos->infoArray[0].imageLoadAddress, &__dso_handle); exit(0); } - if ( getImageInfosFromKernel() != infos ) { - FAIL("all_image_infos: task_info and dyld disagree"); - exit(0); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + for (const struct dyld_image_info* p = infos->infoArray; p < &infos->infoArray[infos->infoArrayCount]; ++p) { + //fprintf(stderr, "addr=0x%p, mTime=0x%lX, path=%s\n", p->imageLoadAddress, p->imageFileModDate, p->imageFilePath); + if ( (strncmp(p->imageFilePath, "/usr/lib", 8) == 0) && (p->imageFileModDate != 0) ) { + FAIL("all_image_infos, mTime not zero for cache image %s", p->imageFilePath); + exit(0); + } } +#endif PASS("all_image_infos"); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/always-libSystem/Makefile b/unit-tests/test-cases/always-libSystem/Makefile index 80ee137..287a47b 100644 --- a/unit-tests/test-cases/always-libSystem/Makefile +++ b/unit-tests/test-cases/always-libSystem/Makefile @@ -27,13 +27,16 @@ 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 -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c diff --git a/unit-tests/test-cases/big-stack/Makefile b/unit-tests/test-cases/big-stack/Makefile index b6f9489..4f8264a 100644 --- a/unit-tests/test-cases/big-stack/Makefile +++ b/unit-tests/test-cases/big-stack/Makefile @@ -33,7 +33,7 @@ ifeq "ppc" "$(ARCH)" endif -ifeq "armv6" "$(ARCH)" +ifeq "iPhoneOS" "$(OS_NAME)" STACK_SIZE = 0x20000000 endif 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/branch-islands/extra.c b/unit-tests/test-cases/branch-islands/extra.c new file mode 100644 index 0000000..c90affa --- /dev/null +++ b/unit-tests/test-cases/branch-islands/extra.c @@ -0,0 +1,8 @@ +#include + + +bool test1() +{ + return false; +} + diff --git a/unit-tests/test-cases/branch-islands/main.c b/unit-tests/test-cases/branch-islands/main.c new file mode 100644 index 0000000..9a265a0 --- /dev/null +++ b/unit-tests/test-cases/branch-islands/main.c @@ -0,0 +1,26 @@ +#include +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// returns false on success +extern bool test1(); + +const char* str = "hello"; + + +int main() +{ + if ( test1() ) + FAIL("branch-islands: test1"); + + if ( strncmp(str, "he", 2) != 0 ) + FAIL("branch-islands: strncmp stub"); + + + PASS("branch-islands"); + return EXIT_SUCCESS; +} + diff --git a/unit-tests/test-cases/branch-islands/space.s b/unit-tests/test-cases/branch-islands/space.s new file mode 100644 index 0000000..4fdd630 --- /dev/null +++ b/unit-tests/test-cases/branch-islands/space.s @@ -0,0 +1,77 @@ + +#if __ppc__ + + .text + +_prejunk: + mr r3,r5 + mr r3,r4 + blr + + +_space1: + .space 15*1024*1024 + 2 + + .align 5 +_junk: + mr r3,r5 + mr r3,r4 + blr + + +_space2: + .space 2*1024*1024 + +#endif + + +#if __arm__ + + .text +_prejunk: + mov r0, #1 + nop + +#if __thumb2__ + // thumb2 branches are +/- 16MB +_space1: + .space 12*1024*1024 +_space2: + .space 12*1024*1024 +_space3: + .space 12*1024*1024 + + +#elif __thumb__ + // thumb1 branches are +/- 4MB +_space1: + .space 3*1024*1024 +_space2: + .space 3*1024*1024 +_space3: + .space 3*1024*1024 + +#else + + // ARM branches are +/- 32MB +_space1: + .space 24*1024*1024 +_space2: + .space 24*1024*1024 +_space3: + .space 24*1024*1024 + +#endif + + .align 5 +_junk: + mov r0, #1 + nop + + +_space4: + .space 2*1024*1024 +#endif + + .subsections_via_symbols + \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-basic/main.c b/unit-tests/test-cases/bundle-basic/main.c index 27f7635..49d30c3 100644 --- a/unit-tests/test-cases/bundle-basic/main.c +++ b/unit-tests/test-cases/bundle-basic/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -30,6 +31,8 @@ typedef bool (*CheckFunc)(); int main() { +// these APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -63,7 +66,7 @@ int main() FAIL("NSDestroyObjectFileImage failed"); return 1; } - +#endif PASS("bundle-basic"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-dont-gc/main.c b/unit-tests/test-cases/bundle-dont-gc/main.c index 69e2f2c..c79afe0 100644 --- a/unit-tests/test-cases/bundle-dont-gc/main.c +++ b/unit-tests/test-cases/bundle-dont-gc/main.c @@ -34,6 +34,8 @@ int main(int argc, const char* argv[]) { +// NSObjectFile* APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // load foo.bundle with old API NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("foo.bundle", &ofi) != NSObjectFileImageSuccess ) { @@ -84,8 +86,8 @@ int main(int argc, const char* argv[]) FAIL("NSUnLinkModule failed"); return 1; } - - +#endif + PASS("bundle-dont-gc"); return EXIT_SUCCESS; } 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/pie-DYLD_NO_PIE/main.c b/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c similarity index 82% rename from unit-tests/test-cases/pie-DYLD_NO_PIE/main.c rename to unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c index 3a6571d..64232df 100644 --- a/unit-tests/test-cases/pie-DYLD_NO_PIE/main.c +++ b/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -20,14 +20,13 @@ * * @APPLE_LICENSE_HEADER_END@ */ -#include #include -int main() +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() { - //int local; - - printf("&main=%p\n", &main); - //printf("&local=%p\n",&local); - return 0; + return ( mydata[500000] == 0 ); } diff --git a/unit-tests/test-cases/bundle-memory-load-all-infos/main.c b/unit-tests/test-cases/bundle-memory-load-all-infos/main.c new file mode 100644 index 0000000..87eacca --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-all-infos/main.c @@ -0,0 +1,126 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + 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) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int main() +{ +// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 0; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 0; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + // look for he_he string in list of images loaded + struct dyld_all_image_infos* infos = getImageInfosFromKernel(); + + if ( infos->infoArrayCount < 2 ) { + FAIL("bundle-memory-load-all-images: dyld_all_image_infos.infoArrayCount is < 2"); + return 0; + } + + bool found = false; + for( int i=0; i < infos->infoArrayCount; ++i) { + //fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + if ( infos->infoArray[i].imageFilePath == NULL ) { + FAIL("bundle-memory-load-all-images: NULL image path found"); + exit(0); + } + if ( strcmp(infos->infoArray[i].imageFilePath, "he_he") == 0 ) + found = true; + } + + if ( !found ) { + FAIL("bundle-memory-load-all-images: loaded memory bundle 'he_he' nout found"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-all-images"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-memory-load-bad/main.c b/unit-tests/test-cases/bundle-memory-load-bad/main.c index fe100da..7e8c93c 100644 --- a/unit-tests/test-cases/bundle-memory-load-bad/main.c +++ b/unit-tests/test-cases/bundle-memory-load-bad/main.c @@ -28,11 +28,14 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() int main() { +// NSAddImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED int fd = open("test.bundle", O_RDONLY, 0); if ( fd == -1 ) { FAIL("open() failed"); @@ -55,10 +58,11 @@ int main() // we are using a file not of type MH_BUNDLE, so NSCreateObjectFileImageFromMemory should fail NSObjectFileImage ofi; - if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) - PASS("bundle-memory-load-bad"); - else + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) FAIL("bundle-memory-load-bad"); + else +#endif + PASS("bundle-memory-load-bad"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-memory-load-fat/main.c b/unit-tests/test-cases/bundle-memory-load-fat/main.c index 7169e32..05ef670 100644 --- a/unit-tests/test-cases/bundle-memory-load-fat/main.c +++ b/unit-tests/test-cases/bundle-memory-load-fat/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -35,6 +36,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSObjectFileImage APIs only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED int fd = open("test.bundle", O_RDONLY, 0); if ( fd == -1 ) { FAIL("open() failed"); @@ -90,6 +93,7 @@ int main() } // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif PASS("bundle-memory-load-fat"); return 0; diff --git a/unit-tests/test-cases/bundle-memory-load-malloc/main.c b/unit-tests/test-cases/bundle-memory-load-malloc/main.c index d00fbde..47e8792 100644 --- a/unit-tests/test-cases/bundle-memory-load-malloc/main.c +++ b/unit-tests/test-cases/bundle-memory-load-malloc/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -30,6 +30,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -38,6 +39,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED int fd = open("test.bundle", O_RDONLY, 0); if ( fd == -1 ) { FAIL("open() failed"); @@ -111,9 +114,8 @@ int main() return 1; } - - // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif PASS("bundle-memory-load-malloc"); return 0; diff --git a/unit-tests/test-cases/bundle-memory-load/main.c b/unit-tests/test-cases/bundle-memory-load/main.c index 8f212f1..7af9854 100644 --- a/unit-tests/test-cases/bundle-memory-load/main.c +++ b/unit-tests/test-cases/bundle-memory-load/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -35,6 +36,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED int fd = open("test.bundle", O_RDONLY, 0); if ( fd == -1 ) { FAIL("open() failed"); @@ -90,6 +93,7 @@ int main() } // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif PASS("bundle-memory-load"); return 0; diff --git a/unit-tests/test-cases/bundle-multi-link/main.c b/unit-tests/test-cases/bundle-multi-link/main.c index 10d9fce..c15bd03 100644 --- a/unit-tests/test-cases/bundle-multi-link/main.c +++ b/unit-tests/test-cases/bundle-multi-link/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,6 +22,7 @@ */ #include #include +#include #include "test.h" // PASS(), FAIL() @@ -37,6 +38,8 @@ typedef int (*getter)(void); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -173,7 +176,8 @@ int main() FAIL("NSDestroyObjectFileImage failed"); return 0; } - +#endif + PASS("bundle-multi-link"); return 0; } diff --git a/unit-tests/test-cases/bundle-multi-load/main.c b/unit-tests/test-cases/bundle-multi-load/main.c index b9eb8b3..ab0382a 100644 --- a/unit-tests/test-cases/bundle-multi-load/main.c +++ b/unit-tests/test-cases/bundle-multi-load/main.c @@ -22,6 +22,7 @@ */ #include #include +#include #include "test.h" // PASS(), FAIL() @@ -32,6 +33,8 @@ int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -147,7 +150,7 @@ int main() FAIL("3rd NSDestroyObjectFileImage failed"); return 0; } - +#endif PASS("bundle-multi-load"); return 0; } diff --git a/unit-tests/test-cases/bundle-name-ownership/main.c b/unit-tests/test-cases/bundle-name-ownership/main.c index 4244489..15c5563 100644 --- a/unit-tests/test-cases/bundle-name-ownership/main.c +++ b/unit-tests/test-cases/bundle-name-ownership/main.c @@ -31,6 +31,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED const char* path = "test.bundle"; NSObjectFileImage ofi; @@ -77,7 +79,7 @@ int main() FAIL("NSDestroyObjectFileImage failed"); return 0; } - +#endif PASS("bundle-name-ownership"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-private/main.c b/unit-tests/test-cases/bundle-private/main.c index 86eac29..2a35990 100644 --- a/unit-tests/test-cases/bundle-private/main.c +++ b/unit-tests/test-cases/bundle-private/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -30,6 +31,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -89,7 +92,7 @@ int main() FAIL("NSDestroyObjectFileImage failed"); return 1; } - +#endif PASS("bundle-private"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-reload/main.c b/unit-tests/test-cases/bundle-reload/main.c index 1f9c3d7..9f0f0c1 100644 --- a/unit-tests/test-cases/bundle-reload/main.c +++ b/unit-tests/test-cases/bundle-reload/main.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -36,6 +37,8 @@ typedef void (*fooProc)(); // test.bundle void doit() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -66,14 +69,17 @@ void doit() FAIL("NSDestroyObjectFileImage failed"); exit(0); } +#endif } static void myRemoveImage(const struct mach_header *mh, intptr_t vmaddr_slide) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED // calling _dyld_get_image_header_containing_address() during the remove image hook // could cause dyld to not flush the address->image cache _dyld_get_image_header_containing_address(mh); +#endif } diff --git a/unit-tests/test-cases/bundle-unlinkable/main.c b/unit-tests/test-cases/bundle-unlinkable/main.c index 93af268..bccdf8d 100644 --- a/unit-tests/test-cases/bundle-unlinkable/main.c +++ b/unit-tests/test-cases/bundle-unlinkable/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -30,6 +31,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -61,7 +64,7 @@ int main() return 1; } } - +#endif PASS("bundle-unlinkable"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-unload-keep-mapped/main.c b/unit-tests/test-cases/bundle-unload-keep-mapped/main.c index c79b0fd..018ee9e 100644 --- a/unit-tests/test-cases/bundle-unload-keep-mapped/main.c +++ b/unit-tests/test-cases/bundle-unload-keep-mapped/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() @@ -30,6 +31,8 @@ typedef bool (*CheckFunc)(); int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { FAIL("NSCreateObjectFileImageFromFile failed"); @@ -66,7 +69,7 @@ int main() // call function again, even though bundle is unloaded func(); - +#endif PASS("bundle-basic"); return 0; diff --git a/unit-tests/test-cases/bundle-v-dylib/main.c b/unit-tests/test-cases/bundle-v-dylib/main.c index 8caba38..aea5c6f 100644 --- a/unit-tests/test-cases/bundle-v-dylib/main.c +++ b/unit-tests/test-cases/bundle-v-dylib/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,9 +29,15 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL() +extern void bar(); + + +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED void loadAsBundleFromMemory(const char* path) { @@ -79,10 +85,11 @@ void loadAsDylib(const char* path) } } -extern void bar(); +#endif int main() { +#if __MAC_OS_X_VERSION_MIN_REQUIRED int dummy; // verify that NSAddImage fails to load MH_BUNDLE @@ -102,7 +109,7 @@ int main() // verify that dyld data structures are not wanked by scanning all images _dyld_get_image_header_containing_address(&dummy); - +#endif PASS("bundle-v-dylib"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/coreSymbolication-notify/Makefile b/unit-tests/test-cases/coreSymbolication-notify/Makefile new file mode 100644 index 0000000..60cedcd --- /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/coreSymbolication-notify/bar.c b/unit-tests/test-cases/coreSymbolication-notify/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/unit-tests/test-cases/coreSymbolication-notify/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/unit-tests/test-cases/coreSymbolication-notify/foo.c b/unit-tests/test-cases/coreSymbolication-notify/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/coreSymbolication-notify/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/dlopen_preflight-shared-cache/main.c b/unit-tests/test-cases/coreSymbolication-notify/main.c similarity index 65% rename from unit-tests/test-cases/dlopen_preflight-shared-cache/main.c rename to unit-tests/test-cases/coreSymbolication-notify/main.c index 6c69905..56ca5f4 100644 --- a/unit-tests/test-cases/dlopen_preflight-shared-cache/main.c +++ b/unit-tests/test-cases/coreSymbolication-notify/main.c @@ -32,33 +32,22 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() - int main(int argc, const char* argv[]) { - // test that libbar which links with libz.dylib preflights - if ( ! dlopen_preflight("./libbar.dylib") ) { - FAIL("dlopen_preflight-basic libbar.dylib should be loadable, but got: %s", dlerror()); - exit(0); - } - // test that libz.dylib itself preflights - if ( ! dlopen_preflight("/usr/lib/libz.dylib") ) { - FAIL("dlopen_preflight-basic /usr/lib/libz.dylib should be loadable, but got: %s", dlerror()); + if ( ! dlopen_preflight("foo.bundle") ) { + FAIL("coreSymbolication-notify foo.bundle should not be loadable"); exit(0); } - // verify libbar and libz are no longer loaded - uint32_t count = _dyld_image_count(); - for (uint32_t i=0; i < count; ++i) { - const char* path = _dyld_get_image_name(i); - if ( strstr(path, "libz.") != NULL ) { - FAIL("dlopen_preflight-shared-cache: %s is loaded", path); - exit(0); - } + void* h = dlopen("foo.bundle", RTLD_LAZY); + if ( h == NULL ) { + FAIL("coreSymbolication-notify foo.bundle failed: %s", dlerror()); + exit(0); } + dlclose(h); - PASS("dlopen_preflight-shared-cache"); - + return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/crt-apple/Makefile b/unit-tests/test-cases/crt-apple/Makefile index 893e25b..78e43d5 100644 --- a/unit-tests/test-cases/crt-apple/Makefile +++ b/unit-tests/test-cases/crt-apple/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,30 +30,27 @@ include ${TESTROOT}/include/common.makefile all-check: all check check: - ./main-10.4 ./main-10.4 - ./main-10.5 ./main-10.5 - ./main-10.4.stripped ./main-10.4.stripped - ./main-10.5.stripped ./main-10.5.stripped - `pwd`/main-10.4 `pwd`/main-10.4 - `pwd`/main-10.5 `pwd`/main-10.5 - `pwd`/main-10.4.stripped `pwd`/main-10.4.stripped - `pwd`/main-10.5.stripped `pwd`/main-10.5.stripped + ./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 -all: main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped +all: main main.stripped main-setuid -main-10.4: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 -w +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -w -main-10.4.stripped: main-10.4 - strip main-10.4 -o main-10.4.stripped - -main-10.5: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 -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 -main-10.5.stripped: main-10.5 - strip main-10.5 -o main-10.5.stripped clean: - ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + ${RM} ${RMFLAGS} *~ main main.stripped main-setuid diff --git a/unit-tests/test-cases/crt-apple/main.c b/unit-tests/test-cases/crt-apple/main.c index 05959bb..0d888ad 100644 --- a/unit-tests/test-cases/crt-apple/main.c +++ b/unit-tests/test-cases/crt-apple/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,12 +28,48 @@ /// +/// verify parameters passed to initializer are same as passed to main() /// verify that apple[0] parameter is correct by comparing to argv[1] /// +static int initializer_argc = 0; +static const char** initializer_argv = NULL; +static const char** initializer_env = NULL; +static const char** initializer_apple = NULL; + + +__attribute__((constructor)) +void init(int argc, const char* argv[], const char* env[], const char* apple[]) +{ + initializer_argc = argc; + initializer_argv = argv; + initializer_env = env; + initializer_apple = apple; +} + int main(int argc, const char* argv[], const char* env[], const char* apple[]) { + if ( argc != initializer_argc ) { + FAIL("crt-apple argc changed"); + exit(EXIT_SUCCESS); + } + + if ( argv != initializer_argv ) { + FAIL("crt-apple argv changed"); + exit(EXIT_SUCCESS); + } + + if ( env != initializer_env ) { + FAIL("crt-apple envp changed"); + exit(EXIT_SUCCESS); + } + + if ( apple != initializer_apple ) { + FAIL("crt-apple apple changed"); + exit(EXIT_SUCCESS); + } + if ( strcmp(apple[0], argv[1]) == 0 ) PASS("crt-apple %s", argv[0]); else diff --git a/unit-tests/test-cases/crt-argv-NULL/Makefile b/unit-tests/test-cases/crt-argv-NULL/Makefile index 9d8dd59..f1865bb 100644 --- a/unit-tests/test-cases/crt-argv-NULL/Makefile +++ b/unit-tests/test-cases/crt-argv-NULL/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -30,19 +30,16 @@ include ${TESTROOT}/include/common.makefile all-check: all check check: - ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main-10.4" "crt-argv-NULL main-10.4" ./main-10.4 - ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main-10.5" "crt-argv-NULL main-10.5" ./main-10.5 + ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main" "crt-argv-NULL main" ./main -all: main-10.4 main-10.5 +all: main -main-10.4: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 -main-10.5: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c clean: - ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/crt-custom/Makefile b/unit-tests/test-cases/crt-custom/Makefile index 491d480..ee12f10 100644 --- a/unit-tests/test-cases/crt-custom/Makefile +++ b/unit-tests/test-cases/crt-custom/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -31,20 +31,16 @@ include ${TESTROOT}/include/common.makefile all-check: all check check: - ./main-10.4 - ./main-10.5 + ./main -all: main-10.4 main-10.5 +all: main -main-10.4: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 mystart.s main.c -mmacosx-version-min=10.4 -e _mystart - -main-10.5: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 mystart.s main.c -mmacosx-version-min=10.5 -e _mystart +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main mystart.s main.c -e _mystart clean: - ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/crt-custom/main.c b/unit-tests/test-cases/crt-custom/main.c index 0986fd0..29c79cf 100644 --- a/unit-tests/test-cases/crt-custom/main.c +++ b/unit-tests/test-cases/crt-custom/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,6 +23,7 @@ #include // fprintf(), NULL #include // exit(), EXIT_SUCCESS #include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -34,10 +35,15 @@ int flag = 0; // for 64-bit binaries initializers are always called before entry point #define ENTRY_BEFORE_INIT 0 #else - // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - #define ENTRY_BEFORE_INIT 1 + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers + #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5 + #define ENTRY_BEFORE_INIT 1 + #else + #define ENTRY_BEFORE_INIT 0 + #endif #else + // for iPhoneOS, initializers are always called before entry point #define ENTRY_BEFORE_INIT 0 #endif #endif diff --git a/unit-tests/test-cases/crt-custom/mystart.s b/unit-tests/test-cases/crt-custom/mystart.s index b4cb9be..a95e0b5 100644 --- a/unit-tests/test-cases/crt-custom/mystart.s +++ b/unit-tests/test-cases/crt-custom/mystart.s @@ -1,3 +1,7 @@ +# Built output for (null) +# Generated at (null) +# Using (null) configuration, (null) architecture for (null) target of (null) project + @@ -5,7 +9,10 @@ .globl _mystart _mystart: #if __i386__ - movl $2, _flag + call L1 +L1: popl %eax + movl L_flag$non_lazy_ptr-L1(%eax), %eax + movl $2, (%eax) jmp start #elif __x86_64__ movl $2, _flag(%rip) @@ -15,6 +22,22 @@ _mystart: lis r2,ha16(_flag) stw r0,lo16(_flag)(r2) b start +#elif __arm__ + ldr r3, L4 + mov r2, #2 + add r3, pc + ldr r3, [r3] + str r2, [r3, #0] + b start +L4: .long L_flag$non_lazy_ptr-(L4-8) +#endif + +#if __i386__ || __arm__ + .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers + .align 2 +L_flag$non_lazy_ptr: +.indirect_symbol _flag + .long 0 #endif diff --git a/unit-tests/test-cases/crt-libSystem/Makefile b/unit-tests/test-cases/crt-libSystem/Makefile index 6f30df5..aab6ce6 100644 --- a/unit-tests/test-cases/crt-libSystem/Makefile +++ b/unit-tests/test-cases/crt-libSystem/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -28,37 +28,23 @@ include ${TESTROOT}/include/common.makefile # the mechanism for 10.4 and 10.5 is different # + all-check: all check check: - ./main-10.4 - ./main-10.5 - ./main-10.6 - ./main-10.4.stripped - ./main-10.5.stripped - ./main-10.6.stripped - -all: main-10.4 main-10.5 main-10.6 main-10.4.stripped main-10.5.stripped main-10.6.stripped - -main-10.4: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 + ./main + ./main.stripped -main-10.4.stripped: main-10.4 - strip main-10.4 -o main-10.4.stripped - -main-10.5: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 +all: main main.stripped -main-10.5.stripped: main-10.5 - strip main-10.5 -o main-10.5.stripped -main-10.6: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.6 main.c -mmacosx-version-min=10.6 -nostdlib -lcrt1.10.6.o -lSystem +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include $(CRTLIB) -o main main.c -main-10.6.stripped: main-10.6 - strip main-10.6 -o main-10.6.stripped +main.stripped: main + strip main -o main.stripped clean: - ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.6 main-10.4.stripped main-10.5.stripped main-10.6.stripped + ${RM} ${RMFLAGS} *~ main main.stripped diff --git a/unit-tests/test-cases/crt-result/Makefile b/unit-tests/test-cases/crt-result/Makefile index 0e4bd7b..a0cf8ed 100644 --- a/unit-tests/test-cases/crt-result/Makefile +++ b/unit-tests/test-cases/crt-result/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -31,27 +31,20 @@ include ${TESTROOT}/include/common.makefile all-check: all check check: - ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.4" "crt-result good-10.4" ./good-10.4 - ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.5" "crt-result good-10.5" ./good-10.5 - ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.4" "crt-result bad-10.4" ./bad-10.4 - ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.5" "crt-result bad-10.5" ./bad-10.5 + ${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-10.4 good-10.5 bad-10.4 bad-10.5 +all: good bad -good-10.4: good.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.4 good.c -mmacosx-version-min=10.4 -good-10.5: good.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.5 good.c -mmacosx-version-min=10.5 +good: good.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good good.c -bad-10.4: bad.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.4 bad.c -mmacosx-version-min=10.4 - -bad-10.5: bad.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.5 bad.c -mmacosx-version-min=10.5 +bad: bad.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad bad.c clean: - ${RM} ${RMFLAGS} *~ good-10.4 good-10.5 bad-10.4 bad-10.5 + ${RM} ${RMFLAGS} *~ good bad diff --git a/unit-tests/test-cases/cxa_finalize/foo.cxx b/unit-tests/test-cases/cxa_finalize/foo.cxx index 0399be1..e073170 100644 --- a/unit-tests/test-cases/cxa_finalize/foo.cxx +++ b/unit-tests/test-cases/cxa_finalize/foo.cxx @@ -21,12 +21,13 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include class A { public: A() { f = 10; } - ~A() { f = 0; } + ~A() { if ( f == 0 ) abort(); f = 0; } int get() { return f; } private: int f; diff --git a/unit-tests/test-cases/deadlock/main.c b/unit-tests/test-cases/deadlock/main.c index 9727ccb..21e6446 100644 --- a/unit-tests/test-cases/deadlock/main.c +++ b/unit-tests/test-cases/deadlock/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,6 +25,7 @@ #include #include #include +#include #include "test.h" @@ -40,6 +41,7 @@ extern void foo(); +#if __MAC_OS_X_VERSION_MIN_REQUIRED static volatile int sBarrier = 0; static pthread_mutex_t sBarrierMutex; @@ -90,9 +92,11 @@ static void myImageHandler(const struct mach_header *mh, intptr_t vmaddr_slide) pthread_mutex_unlock(&sMyLock); } } +#endif int main() { +#if __MAC_OS_X_VERSION_MIN_REQUIRED pthread_mutex_init(&sBarrierMutex, NULL); pthread_cond_init(&sBarrierFree, NULL); pthread_mutex_init(&sMyLock, NULL); @@ -111,6 +115,8 @@ int main() // thread 1 _dyld_register_func_for_add_image(&myImageHandler); NSAddImage("bar.dylib", 0); +#endif PASS("deadlock"); + return 0; } diff --git a/unit-tests/test-cases/dladdr/Makefile b/unit-tests/test-cases/dladdr/Makefile index 3a61220..fb05db1 100644 --- a/unit-tests/test-cases/dladdr/Makefile +++ b/unit-tests/test-cases/dladdr/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -29,7 +29,7 @@ check: ./main all: - ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c clean: diff --git a/unit-tests/test-cases/dladdr/main.c b/unit-tests/test-cases/dladdr/main.c index ab88475..c75c621 100644 --- a/unit-tests/test-cases/dladdr/main.c +++ b/unit-tests/test-cases/dladdr/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -66,10 +67,12 @@ static void verifybar() FAIL("dladdr()->dli_saddr is not &bar"); exit(0); } +#if __MAC_OS_X_VERSION_MIN_REQUIRED if ( info.dli_fbase != _dyld_get_image_header_containing_address(&bar) ) { FAIL("dladdr()->dli_fbase is not image that contains &bar"); exit(0); } +#endif } // checks local symbol @@ -88,10 +91,12 @@ static void verifyfoo() FAIL("dladdr()->dli_saddr is not &foo"); exit(0); } +#if __MAC_OS_X_VERSION_MIN_REQUIRED if ( info.dli_fbase != _dyld_get_image_header_containing_address(&foo) ) { FAIL("dladdr()->dli_fbase is not image that contains &foo"); exit(0); } +#endif } // checks hidden symbol @@ -110,10 +115,12 @@ static void verifyhide() FAIL("dladdr()->dli_saddr is not &hide"); exit(0); } +#if __MAC_OS_X_VERSION_MIN_REQUIRED if ( info.dli_fbase != _dyld_get_image_header_containing_address(&hide) ) { FAIL("dladdr()->dli_fbase is not image that contains &hide"); exit(0); } +#endif } diff --git a/unit-tests/test-cases/dlopen-error/Makefile b/unit-tests/test-cases/dlopen-error/Makefile index 797e0c4..6515da4 100644 --- a/unit-tests/test-cases/dlopen-error/Makefile +++ b/unit-tests/test-cases/dlopen-error/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2008 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2008-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,15 +23,21 @@ 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: - ./main + 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 - chmod -r 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 index a3afc96..2fc2da1 100644 --- a/unit-tests/test-cases/dlopen-executable/Makefile +++ b/unit-tests/test-cases/dlopen-executable/Makefile @@ -35,7 +35,7 @@ main : main.c foo.exe : foo.c - ${CC} ${CCFLAGS} foo.c -o foo.exe + ${CC} ${CCFLAGS} foo.c -o foo.exe -Wl,-no_pie foo.pie : foo.c ${CC} ${CCFLAGS} foo.c -o foo.pie -Wl,-pie diff --git a/unit-tests/test-cases/dlopen-from-anonymous-code/main.c b/unit-tests/test-cases/dlopen-from-anonymous-code/main.c index 8dfe72f..e208e14 100644 --- a/unit-tests/test-cases/dlopen-from-anonymous-code/main.c +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/main.c @@ -26,6 +26,7 @@ #include #include // sys_icache_invalidate #include // for mprotext +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -36,18 +37,33 @@ void* calldlopen(const char* path, int mode, void* (*dlopen_proc)(const char* pa return (*dlopen_proc)(path, mode); } +#if __thumb__ + #define START_OF_FUNC(x) ((void*)((long)x & (-2))) + #define ADDR_FROM_BLOCK(x) ((void*)((long)x | 1)) +#else + #define START_OF_FUNC(x) (x) + #define ADDR_FROM_BLOCK(x) (x) +#endif + // // try calling dlopen() from code not owned by dyld // int main() { - void* codeBlock = malloc(4096); - memcpy(codeBlock, &calldlopen, 4096); + // now try to create a page where foo() was + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + FAIL("vm_allocate returned %d", r); + return 0; + } + void* codeBlock = (void*)(addr); + memcpy(codeBlock, START_OF_FUNC(calldlopen), 4096); sys_icache_invalidate(codeBlock, 4096); mprotect(codeBlock, 4096, PROT_READ | PROT_EXEC); //fprintf(stderr, "codeBlock=%p\n", codeBlock); - void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = codeBlock; + void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = ADDR_FROM_BLOCK(codeBlock); void* handle = (*caller)("foo.bundle", RTLD_LAZY, &dlopen); if ( handle == NULL ) { diff --git a/unit-tests/test-cases/dlopen-leak/bar.c b/unit-tests/test-cases/dlopen-leak/bar.c index b72a1a5..a48d74f 100644 --- a/unit-tests/test-cases/dlopen-leak/bar.c +++ b/unit-tests/test-cases/dlopen-leak/bar.c @@ -1,3 +1,6 @@ +#include + void bar() { + strcpy(NULL, NULL); } diff --git a/unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile b/unit-tests/test-cases/dlopen-non-canonical-path/Makefile similarity index 86% rename from unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile rename to unit-tests/test-cases/dlopen-non-canonical-path/Makefile index 8a5e93a..e933867 100644 --- a/unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile +++ b/unit-tests/test-cases/dlopen-non-canonical-path/Makefile @@ -23,20 +23,15 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile + + all-check: all check check: ./main -all: main - -main: main.c libbar.dylib +all: ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -libbar.dylib : bar.c - ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib -lz - - clean: - ${RM} ${RMFLAGS} *~ main libbar.dylib - + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/dlopen-non-canonical-path/main.c b/unit-tests/test-cases/dlopen-non-canonical-path/main.c new file mode 100644 index 0000000..023c370 --- /dev/null +++ b/unit-tests/test-cases/dlopen-non-canonical-path/main.c @@ -0,0 +1,60 @@ +/* + * 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +static void tryPath(const char* path) +{ + if ( dlopen_preflight(path) ) { + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-non-canonical-path: dlopen(%s)", path); + exit(0); + } + } + else { + FAIL("dlopen-non-canonical-path: dlopen_preflight(%s)", path); + exit(0); + } +} + +// +// dlopen() not opening frameworks with non-canonical paths +// + +int main() +{ + tryPath("//usr/lib/libSystem.B.dylib"); + tryPath("/usr/bin/../lib/libSystem.B.dylib"); + tryPath("/usr/lib/./libSystem.B.dylib"); + tryPath("/usr/lib//libSystem.B.dylib"); + + PASS("dlopen-non-canonical-path"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/dlopen-notify-bind/Makefile b/unit-tests/test-cases/dlopen-notify-bind/Makefile index 5844be2..484ef57 100644 --- a/unit-tests/test-cases/dlopen-notify-bind/Makefile +++ b/unit-tests/test-cases/dlopen-notify-bind/Makefile @@ -39,7 +39,7 @@ check: all: main main : main.c libfoo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.4 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wno-deprecated-declarations -o main main.c libfoo.dylib : foo.c diff --git a/unit-tests/test-cases/dlopen-notify-bind/main.c b/unit-tests/test-cases/dlopen-notify-bind/main.c index d99436a..630ba97 100644 --- a/unit-tests/test-cases/dlopen-notify-bind/main.c +++ b/unit-tests/test-cases/dlopen-notify-bind/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,6 +24,7 @@ #include // exit(), EXIT_SUCCESS #include #include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -33,8 +34,11 @@ typedef void* (*fooProc)(); static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) { +// NSLookupSymbolInImage is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED //fprintf(stderr, "mh=%p\n", mh); NSLookupSymbolInImage(mh, "_bar", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); +#endif } 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..2a361da --- /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 | grep '0 leaks for 0 total leaked bytes' > /dev/null && echo "PASS dlopen-search-leak" + +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-search-leak/foo.c b/unit-tests/test-cases/dlopen-search-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen-search-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen-search-leak/main.c b/unit-tests/test-cases/dlopen-search-leak/main.c new file mode 100644 index 0000000..7a4f090 --- /dev/null +++ b/unit-tests/test-cases/dlopen-search-leak/main.c @@ -0,0 +1,47 @@ +/* + * 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@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "leaks %u\n", getpid()); + system(cmd); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c b/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c deleted file mode 100644 index 8b13789..0000000 --- a/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c +++ /dev/null @@ -1 +0,0 @@ - diff --git a/unit-tests/test-cases/dtrace-static-probes/Makefile b/unit-tests/test-cases/dtrace-static-probes/Makefile index 3c40c6f..f800ae4 100644 --- a/unit-tests/test-cases/dtrace-static-probes/Makefile +++ b/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -27,10 +27,16 @@ 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 dtrace -q -n 'Foo$$target:::count { printf("%u\n", arg0); } ' -c ./main | wc -w` == 5 ]; \ + 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 \ diff --git a/unit-tests/test-cases/dyld-func-lookup/Makefile b/unit-tests/test-cases/dyld-func-lookup/Makefile index 00ad156..c2a8e2e 100644 --- a/unit-tests/test-cases/dyld-func-lookup/Makefile +++ b/unit-tests/test-cases/dyld-func-lookup/Makefile @@ -31,14 +31,14 @@ check: all: main test.bundle test.dylib main : main.c foo.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c test.bundle : foo.c - ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle test.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib diff --git a/unit-tests/test-cases/dyld-func-lookup/foo.c b/unit-tests/test-cases/dyld-func-lookup/foo.c index 00b5af3..bc232a2 100644 --- a/unit-tests/test-cases/dyld-func-lookup/foo.c +++ b/unit-tests/test-cases/dyld-func-lookup/foo.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,6 +22,11 @@ */ #include +#include + + +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) extern bool _dyld_func_lookup(const char* dyld_func_name, void** address); @@ -42,3 +47,5 @@ bool check_dyld_func_lookup() // looks good return true; } + +#endif diff --git a/unit-tests/test-cases/dyld-func-lookup/main.c b/unit-tests/test-cases/dyld-func-lookup/main.c index 270e343..35bbd9f 100644 --- a/unit-tests/test-cases/dyld-func-lookup/main.c +++ b/unit-tests/test-cases/dyld-func-lookup/main.c @@ -24,9 +24,13 @@ #include // exit(), EXIT_SUCCESS #include #include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) extern bool check_dyld_func_lookup(); typedef bool (*proc)(void); @@ -53,17 +57,20 @@ static void trySO(const char* path) dlclose(handle); } - +#endif int main() { +// _dyld_func_lookup is only available in 10.5 and earlier +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) if ( ! check_dyld_func_lookup() ) FAIL("check_dyld_func_lookup failed for main executable"); trySO("test.bundle"); trySO("test.dylib"); - +#endif + PASS("dyld-func-lookup"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dyld-launched-prebound/main.c b/unit-tests/test-cases/dyld-launched-prebound/main.c index a64d5a2..aa6ab80 100644 --- a/unit-tests/test-cases/dyld-launched-prebound/main.c +++ b/unit-tests/test-cases/dyld-launched-prebound/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,12 +23,15 @@ #include // fprintf(), NULL #include // exit(), EXIT_SUCCESS #include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() int main(int argc, const char* argv[]) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED _dyld_launched_prebound(); +#endif return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dyld-slide/Makefile b/unit-tests/test-cases/dyld-slide/Makefile index d17bbaa..aa3debf 100644 --- a/unit-tests/test-cases/dyld-slide/Makefile +++ b/unit-tests/test-cases/dyld-slide/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,15 +24,23 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile STACK_BASE = 0x8fe01000 +STACK_SIZE = 0x00100000 ifeq "armv6" "$(ARCH)" STACK_BASE = 0x2fe01000 + STACK_SIZE = 0x00100000 +endif +ifeq "armv7" "$(ARCH)" + STACK_BASE = 0x2fe01000 + STACK_SIZE = 0x00100000 endif ifeq "ppc64" "$(ARCH)" - STACK_BASE = 0x7fff5fc00000 + STACK_BASE = 0x7fff5ff00000 + STACK_SIZE = 0x00400000 endif ifeq "x86_64" "$(ARCH)" - STACK_BASE = 0x7fff5fc00000 + STACK_BASE = 0x7fff5ff00000 + STACK_SIZE = 0x00400000 endif @@ -43,7 +51,7 @@ check: ${TESTROOT}/bin/exit-zero-pass.pl "dyld did slide" "dyld did not slide" ./main all: - ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -Wl,-stack_addr,${STACK_BASE} -Wl,-stack_size,0x00100000 + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -Wl,-stack_addr,${STACK_BASE} -Wl,-stack_size,${STACK_SIZE} clean: ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/dyld-slide/main.c b/unit-tests/test-cases/dyld-slide/main.c index 2bd67ba..a689f63 100644 --- a/unit-tests/test-cases/dyld-slide/main.c +++ b/unit-tests/test-cases/dyld-slide/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,7 +21,8 @@ * @APPLE_LICENSE_HEADER_END@ */ #include // EXIT_SUCCESS -#include +#include +#include #include "test.h" @@ -39,9 +40,9 @@ int main() { - // call a dyld function that will internally throw an exception to test dyld slide properly - NSAddImage("/foo/bar", NSADDIMAGE_OPTION_RETURN_ON_ERROR); - + // call a dyld function that will execute lots of code and bus error dyld was not slid + dlsym(RTLD_DEFAULT, "foobar"); + return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/fallback-with-suid/Makefile b/unit-tests/test-cases/fallback-with-suid/Makefile index af3f2b9..dccb78c 100644 --- a/unit-tests/test-cases/fallback-with-suid/Makefile +++ b/unit-tests/test-cases/fallback-with-suid/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -20,13 +20,20 @@ # # @APPLE_LICENSE_HEADER_END@ ## -TESTROOT = ../.. +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: - ${TESTROOT}/bin/exit-non-zero-pass.pl "fallback-with-suid" "fallback-with-suid" ./main-suid + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "fallback-with-suid" "fallback-with-suid" $(PWD)/main-suid all: main-suid diff --git a/unit-tests/test-cases/flat-prebound/Makefile b/unit-tests/test-cases/flat-prebound/Makefile index caf93e9..6fe230b 100644 --- a/unit-tests/test-cases/flat-prebound/Makefile +++ b/unit-tests/test-cases/flat-prebound/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -35,11 +35,11 @@ main : main.c libfoo.dylib libfoo.dylib : foo.c libbar.dylib - ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 libbar.dylib : bar.c - ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 diff --git a/unit-tests/test-cases/framework-fallback/main.c b/unit-tests/test-cases/framework-fallback/main.c index 904aeb3..fdaa893 100644 --- a/unit-tests/test-cases/framework-fallback/main.c +++ b/unit-tests/test-cases/framework-fallback/main.c @@ -37,14 +37,16 @@ int main(int argc, const char* argv[]) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED const struct mach_header *image; image = NSAddImage("AppKit.framework/AppKit", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_WITH_SEARCHING); - if ( image != NULL ) - PASS("AppKit loaded"); - else + if ( image == NULL ) FAIL("Could not load AppKit"); + else +#endif + PASS("AppKit loaded"); return 0; } 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-count/foo.c b/unit-tests/test-cases/image-count/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/unit-tests/test-cases/image-count/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/unit-tests/test-cases/image-count/main.c b/unit-tests/test-cases/image-count/main.c new file mode 100644 index 0000000..06740b9 --- /dev/null +++ b/unit-tests/test-cases/image-count/main.c @@ -0,0 +1,83 @@ +/* + * 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 // EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" + +extern struct mach_header __dso_handle; + + +struct dyld_all_image_infos* getImageInfosFromKernel() +{ + 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) ) { + FAIL("all_image_infos: task_info() failed"); + exit(0); + } + return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr; +} + + +int +main() +{ + struct dyld_all_image_infos* info = getImageInfosFromKernel(); + if ( info->version < 10 ) { + FAIL("image-count: dyld_all_image_infos is < 10"); + exit(0); + } + + if ( info->infoArrayCount != info->initialImageCount ) { + FAIL("image-count: dyld_all_image_infos.infoArrayCount != dyld_all_image_infos.initialImageCount"); + exit(0); + } + + void* h = dlopen("libfoo.dylib", RTLD_LAZY); + if ( h == NULL ) { + FAIL("image-count: dyld_all_image_infos is < 10"); + exit(0); + } + + if ( info->infoArrayCount != (info->initialImageCount+1) ) { + FAIL("image-count: infoArrayCount did not increment when libfoo.dylib was loaded"); + exit(0); + } + + dlclose(h); + + if ( info->infoArrayCount != info->initialImageCount ) { + FAIL("image-count: infoArrayCount did not decrement when libfoo.dylib was unloaded"); + exit(0); + } + + PASS("image-count"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/image-state-deny-OFI/main.c b/unit-tests/test-cases/image-state-deny-OFI/main.c index 3e1f341..37eb8d7 100644 --- a/unit-tests/test-cases/image-state-deny-OFI/main.c +++ b/unit-tests/test-cases/image-state-deny-OFI/main.c @@ -31,6 +31,9 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + static bool doneRegistering = false; // @@ -92,10 +95,13 @@ static void load(const char* name) } } } +#endif int main(int argc, const char* argv[]) { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // tell dyld we want to know when images successfully loaded dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); doneRegistering = true; @@ -125,6 +131,7 @@ int main(int argc, const char* argv[]) } // dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); +#endif PASS("image-state-deny"); diff --git a/unit-tests/test-cases/image-suffix/main.c b/unit-tests/test-cases/image-suffix/main.c index 79edd75..28b4c8e 100644 --- a/unit-tests/test-cases/image-suffix/main.c +++ b/unit-tests/test-cases/image-suffix/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,7 +33,7 @@ int main(int argc, const char* argv[]) } uint32_t imageCount = _dyld_image_count(); - const struct mach_header* mh = NSAddImage(argv[1], 0); + dlopen(argv[1], 0); if ( imageCount != _dyld_image_count() ) { FAIL("image count changed"); return EXIT_SUCCESS; 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..aa35765 --- /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: + ./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/initializer-bounds-check/bar.c b/unit-tests/test-cases/initializer-bounds-check/bar.c new file mode 100644 index 0000000..12846f3 --- /dev/null +++ b/unit-tests/test-cases/initializer-bounds-check/bar.c @@ -0,0 +1,7 @@ + +int bar = 0; + +void altSecondInit() +{ + bar = 1; +} diff --git a/unit-tests/test-cases/initializer-bounds-check/foo1.c b/unit-tests/test-cases/initializer-bounds-check/foo1.c new file mode 100644 index 0000000..5333718 --- /dev/null +++ b/unit-tests/test-cases/initializer-bounds-check/foo1.c @@ -0,0 +1,14 @@ +#include + +extern void* initializers[] __asm__("section$start$__DATA$__mod_init_func"); + +extern void altSecondInit(); + + +__attribute__((constructor)) +void firstInit() +{ + // slam second initializer to be pointer into another dylib + initializers[1] = &altSecondInit; +} + diff --git a/unit-tests/test-cases/initializer-bounds-check/foo2.c b/unit-tests/test-cases/initializer-bounds-check/foo2.c new file mode 100644 index 0000000..ea8cf54 --- /dev/null +++ b/unit-tests/test-cases/initializer-bounds-check/foo2.c @@ -0,0 +1,12 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__attribute__((constructor)) +void secondInit() +{ + FAIL("initializer-bounds-check, second init called"); + exit(0); +} + diff --git a/unit-tests/test-cases/initializer-bounds-check/main.c b/unit-tests/test-cases/initializer-bounds-check/main.c new file mode 100644 index 0000000..f466342 --- /dev/null +++ b/unit-tests/test-cases/initializer-bounds-check/main.c @@ -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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int bar; + +int +main() +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( bar == 1 ) + FAIL("initializer-bounds-check, out of bounds initializer called"); + else + PASS("initializer-bounds-check"); +#else + PASS("initializer-bounds-check"); +#endif + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/insert-libraries-with-suid/Makefile b/unit-tests/test-cases/insert-libraries-with-suid/Makefile index 1380ba9..b270917 100644 --- a/unit-tests/test-cases/insert-libraries-with-suid/Makefile +++ b/unit-tests/test-cases/insert-libraries-with-suid/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,10 +23,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = `pwd` + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + all-check: all check check: - export DYLD_INSERT_LIBRARIES="/usr/lib/libldap.dylib:/usr/lib/libpcap.dylib" && ./main + ${RUN_AS_USER} $(PWD)/main-with-env all: main @@ -34,7 +42,11 @@ 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 + ${RM} ${RMFLAGS} *~ main main-with-env diff --git a/unit-tests/test-cases/loader_path/main.c b/unit-tests/test-cases/loader_path/main.c index 141931f..f8600b4 100644 --- a/unit-tests/test-cases/loader_path/main.c +++ b/unit-tests/test-cases/loader_path/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,14 +23,21 @@ #include #include #include +#include +#include #include "test.h" int main() { +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5) NSAddImage("@loader_path/hide/libfoo3.dylib", 0); - - PASS("loader_path"); +#else + if ( dlopen("@loader_path/hide/libfoo3.dylib", 0) == NULL ) + FAIL("loader_path"); + else +#endif + PASS("loader_path"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/partial-library-load/main.c b/unit-tests/test-cases/partial-library-load/main.c index 0c81554..9993ac4 100644 --- a/unit-tests/test-cases/partial-library-load/main.c +++ b/unit-tests/test-cases/partial-library-load/main.c @@ -29,6 +29,8 @@ int main() { +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED // load bundle which indirectly loads libfoo and libbar NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { @@ -48,7 +50,7 @@ int main() if ( mh != NULL ) { return 1; } - +#endif #if 0 // find foo NSSymbol sym = NSLookupSymbolInImage(mh, "_foo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND); diff --git a/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile b/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile deleted file mode 100644 index f3fd51e..0000000 --- a/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile +++ /dev/null @@ -1,34 +0,0 @@ - -TESTROOT = ../.. -include ${TESTROOT}/include/common.makefile - - -# -# there should be some way to temporarily turn off -pie -# -# run a PIE four times and verify its load address was the same every time -# - -all-check: all check - -check: - export DYLD_NO_PIE=1 && ./main > main.out - export DYLD_NO_PIE=1 && ./main >> main.out - export DYLD_NO_PIE=1 && ./main >> main.out - export DYLD_NO_PIE=1 && ./main >> main.out - if [ `sort main.out -u | wc -l` == 1 ]; \ - then \ - echo "PASS pie-DYLD_NO_PIE"; \ - else \ - echo "FAIL pie-DYLD_NO_PIE"; \ - 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-basic/Makefile b/unit-tests/test-cases/pie-basic/Makefile index 0c6c455..bac7b71 100644 --- a/unit-tests/test-cases/pie-basic/Makefile +++ b/unit-tests/test-cases/pie-basic/Makefile @@ -42,7 +42,7 @@ check: all: main main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c clean: diff --git a/unit-tests/test-cases/pie-big/Makefile b/unit-tests/test-cases/pie-big/Makefile index b295994..e8a5fd0 100644 --- a/unit-tests/test-cases/pie-big/Makefile +++ b/unit-tests/test-cases/pie-big/Makefile @@ -42,7 +42,7 @@ check: all: main main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c clean: diff --git a/unit-tests/test-cases/pie-big/main.c b/unit-tests/test-cases/pie-big/main.c index f7e3b88..49d9a88 100644 --- a/unit-tests/test-cases/pie-big/main.c +++ b/unit-tests/test-cases/pie-big/main.c @@ -22,9 +22,17 @@ */ #include #include +#include -char bigarray[1500000000]; - +#if __IPHONE_OS_VERSION_MIN_REQUIRED +char bigarray[0x10000000]; // 0.25GB +#else + #if __LP64__ + char bigarray[0xF0000000]; // 4GB + #else + char bigarray[0x30000000]; // 0.75GB + #endif +#endif int main() { diff --git a/unit-tests/test-cases/pie-custom-stack/Makefile b/unit-tests/test-cases/pie-custom-stack/Makefile index 7c68dbc..e795c80 100644 --- a/unit-tests/test-cases/pie-custom-stack/Makefile +++ b/unit-tests/test-cases/pie-custom-stack/Makefile @@ -42,7 +42,7 @@ check: all: main main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 -Wl,-stack_size,0x10000000 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -Wl,-stack_size,0x10000000 clean: diff --git a/unit-tests/test-cases/pie-text-reloc/Makefile b/unit-tests/test-cases/pie-text-reloc/Makefile index 497174e..a83adcd 100644 --- a/unit-tests/test-cases/pie-text-reloc/Makefile +++ b/unit-tests/test-cases/pie-text-reloc/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -41,15 +41,15 @@ check: ./main >> main.out if [ `sort main.out -u | wc -l` == 4 ]; \ then \ - echo "PASS pie-basic"; \ + echo "PASS pie-text-reloc"; \ else \ - echo "FAIL pie-basic"; \ + echo "FAIL pie-text-reloc"; \ fi; \ all: main main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c clean: diff --git a/unit-tests/test-cases/prebased-performance/Makefile b/unit-tests/test-cases/prebased-performance/Makefile index a3edccc..ce0d7d9 100644 --- a/unit-tests/test-cases/prebased-performance/Makefile +++ b/unit-tests/test-cases/prebased-performance/Makefile @@ -43,10 +43,10 @@ check: all: main main: main.c libfoo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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} -mmacosx-version-min=10.5 + ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c -seg1addr ${DYLIB_BASES_ADDRESS} clean: diff --git a/unit-tests/test-cases/re-export-dylib/Makefile b/unit-tests/test-cases/re-export-dylib/Makefile index da9f97a..5cea7a1 100644 --- a/unit-tests/test-cases/re-export-dylib/Makefile +++ b/unit-tests/test-cases/re-export-dylib/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -27,38 +27,28 @@ PWD = `pwd` # -# Test that the 10.4 and 10.5 ways to re-export a dylib work +# Test that dylib re-exports work # all-check: all check check: - ./main4 - ./main5 + ./main -all: main4 main5 +all: main -libbar4.dylib : bar.c - ${CC} bar.c -dynamiclib -o $(PWD)/libbar4.dylib -mmacosx-version-min=10.4 -libfoo4.dylib : foo.c libbar4.dylib - ${CC} foo.c -dynamiclib libbar4.dylib -sub_library libbar4 -install_name $(PWD)/libfoo4.dylib -o libfoo4.dylib -mmacosx-version-min=10.4 -main4 : main.c libfoo4.dylib - ${CC} main.c -I${TESTROOT}/include -o main4 libfoo4.dylib -mmacosx-version-min=10.4 +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 - -libbar5.dylib : bar.c - ${CC} bar.c -dynamiclib -o $(PWD)/libbar5.dylib -mmacosx-version-min=10.5 - -libfoo5.dylib : foo.c libbar5.dylib - ${CC} foo.c -dynamiclib libbar5.dylib -sub_library libbar5 -install_name $(PWD)/libfoo5.dylib -o libfoo5.dylib -mmacosx-version-min=10.5 - -main5 : main.c libfoo5.dylib - ${CC} main.c -I${TESTROOT}/include -o main5 libfoo5.dylib -mmacosx-version-min=10.5 +main : main.c libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib clean: - rm -rf main4 main5 libfoo4.dylib libfoo5.dylib libbar4.dylib libbar5.dylib + 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 index 7082b05..9e5cea6 100644 --- a/unit-tests/test-cases/re-export-framework/Makefile +++ b/unit-tests/test-cases/re-export-framework/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -27,44 +27,32 @@ PWD = `pwd` # -# Test that the 10.4 and 10.5 ways to re-export a framework work +# Test re-exported frameworks work # all-check: all check check: - ./main4 - ./main5 + ./main -all: main4 main5 +all: main -Bar4.framework/Bar4 : bar.c - mkdir -p Bar4.framework - ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar4.framework/Bar4 -o Bar4.framework/Bar4 -mmacosx-version-min=10.4 -Foo4.framework/Foo4 : foo.c Bar4.framework/Bar4 - mkdir -p Foo4.framework - ${CC} foo.c -dynamiclib Bar4.framework/Bar4 -sub_umbrella Bar4 -install_name $(PWD)/Foo4.framework/Foo4 -o Foo4.framework/Foo4 -mmacosx-version-min=10.4 -main4 : main.c Foo4.framework/Foo4 - ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 +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 - -Bar5.framework/Bar5 : bar.c - mkdir -p Bar5.framework - ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar5.framework/Bar5 -o Bar5.framework/Bar5 -mmacosx-version-min=10.5 - -Foo5.framework/Foo5 : foo.c Bar5.framework/Bar5 - mkdir -p Foo5.framework - ${CC} foo.c -dynamiclib Bar5.framework/Bar5 -sub_umbrella Bar5 -install_name $(PWD)/Foo5.framework/Foo5 -o Foo5.framework/Foo5 -mmacosx-version-min=10.5 - -main5 : main.c Foo5.framework/Foo5 - ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. clean: - rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework + 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 index c2f76e7..d77a607 100644 --- a/unit-tests/test-cases/re-export-sub-framework/Makefile +++ b/unit-tests/test-cases/re-export-sub-framework/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -27,42 +27,30 @@ PWD = `pwd` # -# Test that the 10.4 and 10.5 ways to re-export a sub framework work +# Test that the ways to re-export a sub framework work # all-check: all check check: - ./main4 - ./main5 + ./main -all: main4 main5 +all: main -Bar4.framework/Bar4 : bar.c - mkdir -p Bar4.framework - ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar4.framework/Bar4 -o Bar4.framework/Bar4 -umbrella Foo4 -mmacosx-version-min=10.4 -Foo4.framework/Foo4 : foo.c Bar4.framework/Bar4 - mkdir -p Foo4.framework - ${CC} foo.c -dynamiclib Bar4.framework/Bar4 -install_name $(PWD)/Foo4.framework/Foo4 -o Foo4.framework/Foo4 -mmacosx-version-min=10.4 +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 -main4 : main.c Foo4.framework/Foo4 - ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 +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 - -Bar5.framework/Bar5 : bar.c - mkdir -p Bar5.framework - ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar5.framework/Bar5 -o Bar5.framework/Bar5 -umbrella Foo5 -mmacosx-version-min=10.5 - -Foo5.framework/Foo5 : foo.c Bar5.framework/Bar5 - mkdir -p Foo5.framework - ${CC} foo.c -dynamiclib Bar5.framework/Bar5 -install_name $(PWD)/Foo5.framework/Foo5 -o Foo5.framework/Foo5 -mmacosx-version-min=10.5 - -main5 : main.c Foo5.framework/Foo5 - ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. clean: - rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework + rm -rf main Foo.framework Bar.framework \ No newline at end of file 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..3be2415 --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +all: all_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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/re-export-symbol/bar.c b/unit-tests/test-cases/re-export-symbol/bar.c new file mode 100644 index 0000000..f5837c1 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 10; +} diff --git a/unit-tests/test-cases/re-export-symbol/foo.c b/unit-tests/test-cases/re-export-symbol/foo.c new file mode 100644 index 0000000..9232d4c --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 10; +} diff --git a/unit-tests/test-cases/re-export-symbol/foo.exp b/unit-tests/test-cases/re-export-symbol/foo.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/unit-tests/test-cases/re-export-symbol/foo2.exp b/unit-tests/test-cases/re-export-symbol/foo2.exp new file mode 100644 index 0000000..d55b748 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/foo2.exp @@ -0,0 +1,2 @@ +_foo +_mybar diff --git a/unit-tests/test-cases/re-export-symbol/main1.c b/unit-tests/test-cases/re-export-symbol/main1.c new file mode 100644 index 0000000..cd2bab7 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/main1.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); +extern int bar(); + +int (*pbar)() = &bar; + + +int main() +{ + if ( foo() != 10 ) + FAIL("re-export-symbol: foo() returned wrong value"); + if ( bar() != 10 ) + FAIL("re-export-symbol: bar() returned wrong value"); + if ( (*pbar)() != 10 ) + FAIL("re-export-symbol: (*pbar)() returned wrong value"); + PASS("re-export-symbol"); + return 0; +} diff --git a/unit-tests/test-cases/re-export-symbol/main2.c b/unit-tests/test-cases/re-export-symbol/main2.c new file mode 100644 index 0000000..7a11c92 --- /dev/null +++ b/unit-tests/test-cases/re-export-symbol/main2.c @@ -0,0 +1,26 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); +extern int mybar(); + +int (*pmybar)() = &mybar; + +int main() +{ + if ( foo() != 10 ) + FAIL("re-export-symbol: foo() returned wrong value"); + if ( mybar() != 10 ) + FAIL("re-export-symbol: mybar() returned wrong value"); + if ( (*pmybar)() != 10 ) + FAIL("re-export-symbol: (*pmybar)() returned wrong value"); + PASS("re-export-symbol"); + return 0; +} + diff --git a/unit-tests/test-cases/read-only-import-shared-cache-override/foo.c b/unit-tests/test-cases/read-only-import-shared-cache-override/foo.c deleted file mode 100644 index a5b10db..0000000 --- a/unit-tests/test-cases/read-only-import-shared-cache-override/foo.c +++ /dev/null @@ -1,19 +0,0 @@ - -#include -#include - -#include "test.h" - -void __attribute__((constructor)) init() -{ - uintptr_t libSysFuncAddr = (uintptr_t)&strcmp; - uintptr_t cfFuncAddr = (uintptr_t)&CFStringGetTypeID; - //fprintf(stderr, "libSysFuncAddr=0x%0lX, cfFuncAddr=0x%0lX\n", libSysFuncAddr, cfFuncAddr); - if ( cfFuncAddr - libSysFuncAddr < 256*1024*1024 ) - FAIL("read-only-import-shared-cache-override"); - else - PASS("read-only-import-shared-cache-override"); -} - - - diff --git a/unit-tests/test-cases/read-only-import-shared-cache-override/main.c b/unit-tests/test-cases/read-only-import-shared-cache-override/main.c deleted file mode 100644 index 9b3996c..0000000 --- a/unit-tests/test-cases/read-only-import-shared-cache-override/main.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include "test.h" - -int main() -{ -#if __arm__ - // no shared cache on iPhone, so skip test - PASS("read-only-import-shared-cache-override"); -#else - // dynamically load libfoo.dylib which depends on libstdc++.dylib - // being re-bound to libfoo's operator new. - dlopen("libfoo.dylib", RTLD_LAZY); -#endif - return 0; -} - diff --git a/unit-tests/test-cases/read-only-stubs/foo.c b/unit-tests/test-cases/read-only-stubs/foo.c index c5f12e7..68fb1fb 100644 --- a/unit-tests/test-cases/read-only-stubs/foo.c +++ b/unit-tests/test-cases/read-only-stubs/foo.c @@ -74,7 +74,10 @@ static void* getStubAddr() #elif __x86_64__ return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; #elif __arm__ - return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide; + void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size); + if ( p != NULL ) + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide; + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide; #else #error unknown arch #endif diff --git a/unit-tests/test-cases/read-only-stubs/main.c b/unit-tests/test-cases/read-only-stubs/main.c index 45f6d8f..fdadfc7 100644 --- a/unit-tests/test-cases/read-only-stubs/main.c +++ b/unit-tests/test-cases/read-only-stubs/main.c @@ -71,7 +71,10 @@ static void* getStubAddr() #elif __x86_64__ return getsectdata("__TEXT", "__symbol_stub1", &size); #elif __arm__ - return getsectdata("__TEXT", "__symbol_stub4", &size); + void* p = getsectdata("__TEXT", "__symbol_stub4", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbolstub1", &size); #else #error unknown arch #endif diff --git a/unit-tests/test-cases/restrict-environ/Makefile b/unit-tests/test-cases/restrict-environ/Makefile index a383d1a..8aa6bea 100644 --- a/unit-tests/test-cases/restrict-environ/Makefile +++ b/unit-tests/test-cases/restrict-environ/Makefile @@ -23,16 +23,29 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +PWD = `pwd` + all-check: all check check: - export DYLD_LIBRARY_PATH=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main + ${RUN_AS_USER} $(PWD)/main-with-env 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 + ${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 index b222951..ceb2e3b 100644 --- a/unit-tests/test-cases/restrict-executable_path/Makefile +++ b/unit-tests/test-cases/restrict-executable_path/Makefile @@ -20,11 +20,17 @@ # # @APPLE_LICENSE_HEADER_END@ ## -TESTROOT = ../.. +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 @@ -35,10 +41,9 @@ SHELL = bash # use bash shell so we can redirect just stderr all-check: all check check: - ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path @executable_path" "restrict-executable_path @executable_path" ./main_exe "restrict-executable_path" - ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path @loader_path" "restrict-executable_path @loader_path" ./main_loader "restrict-executable_path" - ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path relative path" "restrict-executable_path relative path" ./main_rel "restrict-executable_path" - + ${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 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-rm-executable/foo.c b/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c @@ -0,0 +1,25 @@ +/* + * 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@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c b/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c new file mode 100644 index 0000000..d4f0b33 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c @@ -0,0 +1,50 @@ +/* + * 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@ + */ +#include +#include +#include +#include + +#include "test.h" + + +int main() +{ + char buf[2048]; + uint32_t bufSize = sizeof(buf); + if ( _NSGetExecutablePath(buf, &bufSize) ) { + FAIL("rpath-dlopen-rm-exectuable: _NSGetExecutablePath()"); + return EXIT_SUCCESS; + } + + unlink(buf); + + void* handle = dlopen("libz.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-rm-exectuable: %s", dlerror()); + return EXIT_SUCCESS; + } + + PASS("rpath-dlopen-rm-exectuable"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-indirect-suid/Makefile b/unit-tests/test-cases/rpath-indirect-suid/Makefile index fa534f2..6f96b7e 100644 --- a/unit-tests/test-cases/rpath-indirect-suid/Makefile +++ b/unit-tests/test-cases/rpath-indirect-suid/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2007-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -20,10 +20,15 @@ # # @APPLE_LICENSE_HEADER_END@ ## -TESTROOT = ../.. +PWD = $(shell pwd) +TESTROOT = $(PWD)/../.. include ${TESTROOT}/include/common.makefile -PWD = $(shell pwd) +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 @@ -35,9 +40,9 @@ all-check: all check check: ./main || echo "FAIL rpath-indirect-suid absolute path" - ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @loader_path path" "rpath-indirect-suid @loader_path path" ./main_bad1 - ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid relative path" "rpath-indirect-suid relative path" ./main_bad2 - ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @rpath spoof" "rpath-indirect-suid @rpath spoof" ./main_bad3 + ${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 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-install-name/bar.c b/unit-tests/test-cases/rpath-install-name/bar.c new file mode 100644 index 0000000..a27d788 --- /dev/null +++ b/unit-tests/test-cases/rpath-install-name/bar.c @@ -0,0 +1,29 @@ +/* + * 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@ + */ + +extern void stuff(); + +void bar() +{ + stuff(); +} diff --git a/unit-tests/test-cases/rpath-install-name/main.c b/unit-tests/test-cases/rpath-install-name/main.c new file mode 100644 index 0000000..87e0ae4 --- /dev/null +++ b/unit-tests/test-cases/rpath-install-name/main.c @@ -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@ + */ +#include +#include +#include + +#include "test.h" + + +int main() +{ + void* h1 = dlopen("./libstuff_better.dylib", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + + void* handle = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + typedef void (*BarProc)(void); + BarProc pBar = (BarProc)dlsym(handle, "bar"); + if ( pBar == NULL ) { + FAIL("rpath-install-name: %s", dlerror()); + return EXIT_SUCCESS; + } + + (*pBar)(); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-install-name/stuff.c b/unit-tests/test-cases/rpath-install-name/stuff.c new file mode 100644 index 0000000..6b60cb2 --- /dev/null +++ b/unit-tests/test-cases/rpath-install-name/stuff.c @@ -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@ + */ +#include +#include +#include + +#include "test.h" + + +void stuff() +{ +#if BETTER + PASS("rpath-install-name"); +#else + FAIL("rpath-install-name"); +#endif +} diff --git a/unit-tests/test-cases/shared-cache-symlink/main.c b/unit-tests/test-cases/shared-cache-symlink/main.c index ab1c6b6..fc038c1 100644 --- a/unit-tests/test-cases/shared-cache-symlink/main.c +++ b/unit-tests/test-cases/shared-cache-symlink/main.c @@ -55,22 +55,11 @@ int main() return EXIT_SUCCESS; } - // walk images to get slide - uint32_t count = _dyld_image_count(); - for(uint32_t i=0; i < count; ++i) { - if ( _dyld_get_image_header(i) == info.dli_fbase ) { - if ( _dyld_get_image_vmaddr_slide(i) == 0 ) { - // images in shared cache have a slide of zero - PASS("shared-cache-symlink"); - return EXIT_SUCCESS; - } - else { - FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache"); - return EXIT_SUCCESS; - } - } - } + const struct mach_header* mh = (struct mach_header*)info.dli_fbase; + if ( mh->flags & 0x80000000 ) + PASS("shared-cache-symlink"); + else + FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache"); - FAIL("shared-cache-symlink libz.dylib not found"); return EXIT_SUCCESS; } 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/shared-region-overlap/main.c b/unit-tests/test-cases/shared-region-overlap/main.c new file mode 100644 index 0000000..83dfd8a --- /dev/null +++ b/unit-tests/test-cases/shared-region-overlap/main.c @@ -0,0 +1,12 @@ +#include // EXIT_SUCCESS + +#include "test.h" + + +int +main() +{ + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/suid-environ/Makefile b/unit-tests/test-cases/suid-environ/Makefile index 85aa954..7797d2e 100644 --- a/unit-tests/test-cases/suid-environ/Makefile +++ b/unit-tests/test-cases/suid-environ/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,10 +23,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +PWD = `pwd` + all-check: all check check: - export DYLD_INSERT_LIBRARIES=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main + ${RUN_AS_USER} $(PWD)/main-with-env all: main @@ -34,7 +42,12 @@ 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 + ${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 index 8769082..aab4ca2 100644 --- a/unit-tests/test-cases/suid-executable_path/Makefile +++ b/unit-tests/test-cases/suid-executable_path/Makefile @@ -20,11 +20,17 @@ # # @APPLE_LICENSE_HEADER_END@ ## -TESTROOT = ../.. +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 @@ -36,11 +42,11 @@ all-check: all check check: ./main_exe "setuid-executable_path" || echo "FAIL setuid-executable_path @executable_path not setuid" - ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" ./main_exe-suid "setuid-executable_path" ./main_loader "setuid-executable_path" || echo "FAIL setuid-executable_path @loader_path not setuid" - ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" ./main_loader-suid "setuid-executable_path" ./main_rel "setuid-executable_path" || echo "FAIL setuid-executable_path relative path not setuid" - ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path relative path" "setuid-executable_path relative path" ./main_rel-suid "setuid-executable_path" + ${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 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..cfc8ac0 --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +all: all_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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-basic/foo.c b/unit-tests/test-cases/symbol-resolver-basic/foo.c new file mode 100644 index 0000000..f7b6b4b --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/foo.c @@ -0,0 +1,47 @@ +/* + * 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 + + +static int foo_ten() +{ + return 10; +} + +static int foo_zero() +{ + return 0; +} + + +// This foo is a "resolver" function that return the actual address of "foo" +void* foo() +{ + __asm__(".symbol_resolver _foo"); // magic until we have compiler support + if ( getenv("TEN") != NULL ) + return &foo_ten; + else + return &foo_zero; +} + diff --git a/unit-tests/test-cases/symbol-resolver-basic/foo2.c b/unit-tests/test-cases/symbol-resolver-basic/foo2.c new file mode 100644 index 0000000..3c80d3f --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/foo2.c @@ -0,0 +1,33 @@ +/* + * 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 + +extern int foo(); + +// test that calls to resolver based function in same dylib work +int fooPlusOne() +{ + return foo() + 1; +} + diff --git a/unit-tests/test-cases/symbol-resolver-basic/main.c b/unit-tests/test-cases/symbol-resolver-basic/main.c new file mode 100644 index 0000000..670984e --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-basic/main.c @@ -0,0 +1,53 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); +extern int fooPlusOne(); + + +int main() +{ + if ( getenv("TEN") != NULL ) { + if ( foo() != 10 ) + FAIL("symbol-resolver-basic: foo() != 10"); + else if ( fooPlusOne() != 11 ) + FAIL("symbol-resolver-basic: fooPlusOne() != 11"); + else + PASS("symbol-resolver-basic"); + } + else { + if ( foo() != 0 ) + FAIL("symbol-resolver-basic: foo() != 0"); + else if ( fooPlusOne() != 1 ) + FAIL("symbol-resolver-basic: fooPlusOne() != 1"); + else + PASS("symbol-resolver-basic"); + } + + return EXIT_SUCCESS; +} 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..9143671 --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +all: all_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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/symbol-resolver-pointer/foo.c b/unit-tests/test-cases/symbol-resolver-pointer/foo.c new file mode 100644 index 0000000..71d67bc --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-pointer/foo.c @@ -0,0 +1,64 @@ +/* + * 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 + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int resolverCallCount = 0; +int realTestCallCount = 0; + + +void test$FOO(); +void test$FOO() { + //printf("test\n"); + ++realTestCallCount; +} + +void* test_chooser() __asm__("_test"); +void* test_chooser() { + __asm__(".symbol_resolver _test"); + //printf("resolver\n"); + ++resolverCallCount; + return test$FOO; +} + +void test(); +static void (*t)(void) = test; + +void check() { + t(); // call through initialized pointer + t = test; // re-assign pointer via non-lazy-poitner + t(); // call agin through pointer + test(); // call through stub + if ( resolverCallCount != 1 ) + FAIL("symbol-resolver-pointer: resolved called %d times", resolverCallCount); + else if ( realTestCallCount != 3 ) + FAIL("symbol-resolver-pointer: real test function called %d times", realTestCallCount); + else + PASS("symbol-resolver-pointer"); +} + + + + diff --git a/unit-tests/test-cases/symbol-resolver-pointer/main.c b/unit-tests/test-cases/symbol-resolver-pointer/main.c new file mode 100644 index 0000000..9e7a73b --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-pointer/main.c @@ -0,0 +1,35 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void check(); + + +int main() +{ + check(); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile b/unit-tests/test-cases/text-relocs-perms/Makefile similarity index 72% rename from unit-tests/test-cases/read-only-import-shared-cache-override/Makefile rename to unit-tests/test-cases/text-relocs-perms/Makefile index e0e6bcb..2e1ec0f 100644 --- a/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile +++ b/unit-tests/test-cases/text-relocs-perms/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2007 Apple Inc. All rights reserved. +# Copyright (c) 2010 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,24 +23,22 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile - all-check: all check check: - cp /usr/lib/libSystem.B.dylib ./libSystem.B.dylib - export DYLD_LIBRARY_PATH=`pwd` && ./main + ./main + +all: main -all: main main: main.c libfoo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main -libfoo.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c -framework CoreFoundation +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib -seg1addr 0x20000000 clean: - ${RM} ${RMFLAGS} *~ main libSystem.B.dylib libfoo.dylib + ${RM} ${RMFLAGS} main libfoo.dylib - diff --git a/unit-tests/test-cases/text-relocs-perms/foo.c b/unit-tests/test-cases/text-relocs-perms/foo.c new file mode 100644 index 0000000..6a03b28 --- /dev/null +++ b/unit-tests/test-cases/text-relocs-perms/foo.c @@ -0,0 +1,101 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ +#if __LP64__ + uint64_t size; +#else + uint32_t size; +#endif + uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address +#if __i386__ + return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__symbol_stub", &size) + slide; +#elif __ppc__ + return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide; +#elif __ppc64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide; +#elif __x86_64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; +#elif __arm__ + void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size); + if ( p != NULL ) + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide; + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide; +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + //fprintf(stderr, "perm=0x%02X\n", perm); + if ( (perm == 0) || ((perm & VM_PROT_EXECUTE) == 0) ) { + FAIL("text-reloc-perms: missing exec permission 0x%0X at address %p", perm, addr); + exit(0); + } +} + + +void foo() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); +} + + diff --git a/unit-tests/test-cases/text-relocs-perms/main.c b/unit-tests/test-cases/text-relocs-perms/main.c new file mode 100644 index 0000000..b4994b0 --- /dev/null +++ b/unit-tests/test-cases/text-relocs-perms/main.c @@ -0,0 +1,109 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ + unsigned long size; +#if __i386__ + return getsectdata("__IMPORT", "__jump_table", &size); +#elif __ppc__ + void* p = getsectdata("__TEXT", "__picsymbolstub1", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __ppc64__ + return getsectdata("__TEXT", "__picsymbolstub1", &size); +#elif __x86_64__ + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __arm__ + void* p = getsectdata("__TEXT", "__symbol_stub4", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbolstub1", &size); +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + + +int main() +{ + foo(); + + void* stubAddr = getStubAddr(); +#if __i386__ + if ( stubAddr != NULL ) +#endif + { + checkStubs(stubAddr); + checkStubs(stubAddr); + } + PASS("text-relocs-perms"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/text-relocs/Makefile b/unit-tests/test-cases/text-relocs/Makefile index cfadeda..a024a28 100644 --- a/unit-tests/test-cases/text-relocs/Makefile +++ b/unit-tests/test-cases/text-relocs/Makefile @@ -24,7 +24,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile ### -### This test case is to verify __TEXT reliocations work in dylibs +### This test case is to verify __TEXT relocations work in dylibs ### ### @@ -35,10 +35,6 @@ ifeq "ppc64" "$(ARCH)" # ppc64 does not support text relocs TEXT_RELOC_FLAGS = endif -ifeq "armv6" "$(ARCH)" - # arm does not support text relocs - TEXT_RELOC_FLAGS = -endif all-check: all check @@ -46,14 +42,12 @@ 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 - ${CC} ${CCFLAGS} -dynamiclib -o libbar.dylib bar.c -Os ${TEXT_RELOC_FLAGS} +all: + ${CC} ${CCFLAGS} -dynamiclib bar.c space.s -Os -o libbar.dylib ${TEXT_RELOC_FLAGS} + ${CC} ${CCFLAGS} bind.c -static -Os -c -o bind.o + ${CC} ${CCFLAGS} -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 + ${RM} ${RMFLAGS} *~ main libbar.dylib libbind.dylib bind.o diff --git a/unit-tests/test-cases/text-relocs/bar.c b/unit-tests/test-cases/text-relocs/bar.c index acf643c..a87566e 100644 --- a/unit-tests/test-cases/text-relocs/bar.c +++ b/unit-tests/test-cases/text-relocs/bar.c @@ -24,8 +24,14 @@ #include // exit(), EXIT_SUCCESS #include +int y = 0; + static int x = 0; int getx() { return x; } void setx(int a) { x = a; } +void bar() +{ + printf("hello\n"); +} diff --git a/unit-tests/test-cases/text-relocs/bind.c b/unit-tests/test-cases/text-relocs/bind.c new file mode 100644 index 0000000..fa0dd81 --- /dev/null +++ b/unit-tests/test-cases/text-relocs/bind.c @@ -0,0 +1,7 @@ + +extern int y; + +int test() +{ + return y; +} diff --git a/unit-tests/test-cases/text-relocs/space.s b/unit-tests/test-cases/text-relocs/space.s new file mode 100644 index 0000000..2767b5d --- /dev/null +++ b/unit-tests/test-cases/text-relocs/space.s @@ -0,0 +1,2 @@ + .text +_junk: .space 1024*1024 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-flat-lookup/client.c b/unit-tests/test-cases/threaded-flat-lookup/client.c new file mode 100644 index 0000000..72ba218 --- /dev/null +++ b/unit-tests/test-cases/threaded-flat-lookup/client.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); +extern void baz(); + +void doit() +{ + foo(); + bar(); + baz(); +} diff --git a/unit-tests/test-cases/threaded-flat-lookup/foo.c b/unit-tests/test-cases/threaded-flat-lookup/foo.c new file mode 100644 index 0000000..3f2cbaf --- /dev/null +++ b/unit-tests/test-cases/threaded-flat-lookup/foo.c @@ -0,0 +1,4 @@ + +void foo() {} +void bar() {} +void baz() {} diff --git a/unit-tests/test-cases/threaded-flat-lookup/main.c b/unit-tests/test-cases/threaded-flat-lookup/main.c new file mode 100644 index 0000000..09ee55f --- /dev/null +++ b/unit-tests/test-cases/threaded-flat-lookup/main.c @@ -0,0 +1,95 @@ +/* + * 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 + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +void* h1 = NULL; +void* h2 = NULL; + +static void* work(void* ignore) +{ + for (int i=0; i < 10000; ++i) { + h1 = dlopen("libfoo1.dylib", 0); + if ( h1 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(h2); + h2 = dlopen("libfoo2.dylib", 0); + if ( h2 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(h1); + } + + //fprintf(stderr, "done with foos\n"); + return NULL; +} + + +int main() +{ + h2 = dlopen("libfoo2.dylib", 0); + if ( h2 == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + + // other thread dlopens and closes libfoo.dylib 500 times + pthread_t other; + if ( pthread_create(&other, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + // this thread looks up a symbol 10,000 times + for (int i=0; i < 15000; ++i) { + void* handle = dlopen("libclient.dylib", 0); + if ( handle == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + typedef void (*proc_t)(); + proc_t proc = dlsym(handle, "doit"); + if ( proc == NULL ) { + FAIL("dlsym failed: %s", dlerror()); + exit(0); + } + (*proc)(); + dlclose(handle); + } + //fprintf(stderr, "done with libclient\n"); + + void* result; + pthread_join(other, &result); + + PASS("threaded-flat-lookup"); + return 0; +} diff --git a/unit-tests/test-cases/tlv-basic/Makefile b/unit-tests/test-cases/tlv-basic/Makefile new file mode 100644 index 0000000..977b5a8 --- /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 + clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} main + diff --git a/unit-tests/test-cases/tlv-basic/main.c b/unit-tests/test-cases/tlv-basic/main.c new file mode 100644 index 0000000..0fa5240 --- /dev/null +++ b/unit-tests/test-cases/tlv-basic/main.c @@ -0,0 +1,82 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +__thread int a; +__thread int b = 5; +__thread static int c; +__thread static int d = 5; + + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( a != 0 ) { + FAIL("tlv-basic: get_a() non-zero"); + exit(0); + } + if ( b != 5 ) { + FAIL("tlv-basic: get_b() not five"); + exit(0); + } + if ( c != 0 ) { + FAIL("tlv-basic: get_c() non-zero"); + exit(0); + } + if ( d != 5 ) { + FAIL("tlv-basic: get_d() not five"); + exit(0); + } + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(NULL); + + PASS("tlv-basic"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/tlv-dylib/Makefile b/unit-tests/test-cases/tlv-dylib/Makefile new file mode 100644 index 0000000..ff8103e --- /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: + clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib + clang -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-dylib/foo.c b/unit-tests/test-cases/tlv-dylib/foo.c new file mode 100644 index 0000000..4a47edb --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/foo.c @@ -0,0 +1,8 @@ + + + +__thread int a; +__thread int b = 5; + + +int getB() { return b; } diff --git a/unit-tests/test-cases/tlv-dylib/main.c b/unit-tests/test-cases/tlv-dylib/main.c new file mode 100644 index 0000000..117a646 --- /dev/null +++ b/unit-tests/test-cases/tlv-dylib/main.c @@ -0,0 +1,82 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern __thread int a; +extern __thread int b; +__thread static int c; +__thread static int d = 5; + + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( a != 0 ) { + FAIL("tlv-basic: get_a() non-zero"); + exit(0); + } + if ( b != 5 ) { + FAIL("tlv-basic: get_b() not five"); + exit(0); + } + if ( c != 0 ) { + FAIL("tlv-basic: get_c() non-zero"); + exit(0); + } + if ( d != 5 ) { + FAIL("tlv-basic: get_d() not five"); + exit(0); + } + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(NULL); + + PASS("tlv-basic"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/tlv-initializer/Makefile b/unit-tests/test-cases/tlv-initializer/Makefile new file mode 100644 index 0000000..62cd36a --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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-initializer/get.s b/unit-tests/test-cases/tlv-initializer/get.s new file mode 100644 index 0000000..5116395 --- /dev/null +++ b/unit-tests/test-cases/tlv-initializer/get.s @@ -0,0 +1,99 @@ + + # _a is zerofill global TLV + .tbss _a$tlv$init,4,2 + + # _b is an initialized global TLV + .tdata +_b$tlv$init: + .long 5 + + +#if __x86_64__ + + # _a is global TLV + .tlv + .globl _a +_a: .quad __tlv_bootstrap + .quad 0 + .quad _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .quad __tlv_bootstrap + .quad 0 + .quad _b$tlv$init + + # _myinit sets up TLV content + .thread_init_func + .quad _myinit + + + .text + .globl _get_a +_get_a: + pushq %rbp + movq %rsp, %rbp + movq _a@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + + .globl _get_b +_get_b: + pushq %rbp + movq %rsp, %rbp + movq _b@TLVP(%rip), %rdi + call *(%rdi) + popq %rbp + ret + +#endif + +#if __i386__ + + # _a is global TLV + .tlv + .globl _a +_a: .long __tlv_bootstrap + .long 0 + .long _a$tlv$init + + # _b is a global TLV + .tlv + .globl _b +_b: .long __tlv_bootstrap + .long 0 + .long _b$tlv$init + + # _myinit sets up TLV content + .thread_init_func + .long _myinit + + + .text + .globl _get_a +_get_a: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _a@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + + .globl _get_b +_get_b: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl _b@TLVP, %eax + call *(%eax) + movl %ebp, %esp + popl %ebp + ret + +#endif + +.subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-initializer/main.c b/unit-tests/test-cases/tlv-initializer/main.c new file mode 100644 index 0000000..af11a86 --- /dev/null +++ b/unit-tests/test-cases/tlv-initializer/main.c @@ -0,0 +1,77 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int* get_a(); // initially 0 +extern int* get_b(); // initially 5 + +void myinit() +{ + *get_a() = 11; + *get_b() = 42; +} + + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( *get_a() != 11 ) { + FAIL("tlv-initializer: get_a() not initialized to 11"); + exit(0); + } + if ( *get_b() != 42 ) { + FAIL("tlv-initializer: get_b() not initialized to 42"); + exit(0); + } + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(NULL); + + PASS("tlv-initializer"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/tlv-terminators/Makefile b/unit-tests/test-cases/tlv-terminators/Makefile new file mode 100644 index 0000000..f27c54e --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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/tlv-terminators/init.s b/unit-tests/test-cases/tlv-terminators/init.s new file mode 100644 index 0000000..9bb901c --- /dev/null +++ b/unit-tests/test-cases/tlv-terminators/init.s @@ -0,0 +1,10 @@ + + # _myinit sets up TLV content + .thread_init_func + #if __LP64__ + .quad _myinit + #else + .long _myinit + #endif + +.subsections_via_symbols diff --git a/unit-tests/test-cases/tlv-terminators/main.c b/unit-tests/test-cases/tlv-terminators/main.c new file mode 100644 index 0000000..94d1d2a --- /dev/null +++ b/unit-tests/test-cases/tlv-terminators/main.c @@ -0,0 +1,154 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +struct thread_var_info +{ + pthread_t th; + int* a_addr; + int* b_addr; + bool a_terminated; + bool b_terminated; +}; + +struct thread_var_info threadmain; +struct thread_var_info thread1; +struct thread_var_info thread2; + + +__thread int a; // statically, initially 0 +__thread int b = 5; // statically, initially 5 + +extern void _tlv_atexit(void (*termfunc)(void* objAddr), void* objAddr); + +void myinit() +{ + a = 11; // dynamically initialized to 11 + b = 42; // dynamically initialized to 42 +} + +void myterm(void* objAddr) +{ + pthread_t self = pthread_self(); + //fprintf(stderr, "myterm(%p), self=%p\n", objAddr, self); + if ( thread1.th == self && thread1.a_addr == objAddr ) + thread1.a_terminated = true; + else if ( thread1.th == self && thread1.b_addr == objAddr ) + thread1.b_terminated = true; + else if ( thread2.th == self && thread2.a_addr == objAddr ) + thread2.a_terminated = true; + else if ( thread2.th == self && thread2.b_addr == objAddr ) + thread2.b_terminated = true; + else if ( threadmain.th == self && threadmain.a_addr == objAddr ) + threadmain.a_terminated = true; + else if ( threadmain.th == self && threadmain.b_addr == objAddr ) + threadmain.b_terminated = true; +} + +static void* work(void* arg) +{ + if ( a != 11 ) { + FAIL("tlv-terminators: a not initialized to 11"); + exit(0); + } + if ( b != 42 ) { + FAIL("tlv-terminators: b not initialized to 42"); + exit(0); + } + struct thread_var_info* s = (struct thread_var_info*)arg; + s->th = pthread_self(); + s->a_addr = &a; + s->b_addr = &b; + s->a_terminated = false; + s->b_terminated = false; + //fprintf(stderr, "self=%p, arg=%p, &a=%p, &b=%p\n", s->th, arg, s->a_addr , s->b_addr); + + _tlv_atexit(myterm, &a); + _tlv_atexit(myterm, &b); + + return NULL; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, &thread1) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, &thread2) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + + work(&threadmain); + + //fprintf(stderr, "thread1: &a=%p, &b=%p\n", thread1.a_addr, thread1.b_addr); + //fprintf(stderr, "thread2: &a=%p, &b=%p\n", thread2.a_addr, thread2.b_addr); + //fprintf(stderr, "threadm: &a=%p, &b=%p\n", threadmain.a_addr, threadmain.b_addr); + + if ( ! thread1.a_terminated ) { + FAIL("tlv-terminators: terminator for a on thread 1 not run"); + exit(0); + } + if ( ! thread1.b_terminated ) { + FAIL("tlv-terminators: terminator for b on thread 1 not run"); + exit(0); + } + + if ( ! thread2.a_terminated ) { + FAIL("tlv-terminators: terminator for a on thread 2 not run"); + exit(0); + } + if ( ! thread2.b_terminated ) { + FAIL("tlv-terminators: terminator for b on thread 2 not run"); + exit(0); + } + + if ( threadmain.a_terminated ) { + FAIL("tlv-terminators: terminator for a on main thread run early"); + exit(0); + } + if ( threadmain.b_terminated ) { + FAIL("tlv-terminators: terminator for b on main thread run early"); + exit(0); + } + + + PASS("tlv-terminators"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/trie-symbol-overrun/main.c b/unit-tests/test-cases/trie-symbol-overrun/main.c index 34d1a63..e3d07f1 100644 --- a/unit-tests/test-cases/trie-symbol-overrun/main.c +++ b/unit-tests/test-cases/trie-symbol-overrun/main.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -49,8 +51,11 @@ int main() // call a dyld API that uses the string // if dyld reads past the end of the string, it will crash // trie parser can read past end of input symbol name +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5) _dyld_lookup_and_bind(sym, NULL, NULL); - +#else + dlsym(RTLD_DEFAULT, sym); +#endif PASS("trie-symbol-overrun"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/unloadable-library-residue/main.c b/unit-tests/test-cases/unloadable-library-residue/main.c index fc39c25..d1bab84 100644 --- a/unit-tests/test-cases/unloadable-library-residue/main.c +++ b/unit-tests/test-cases/unloadable-library-residue/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,19 +23,28 @@ #include #include #include +#include +#include #include "test.h" // PASS(), FAIL() int main() { +#if __MAC_OS_X_VERSION_MIN_REQUIRED // load libfoo which depends on libbar const struct mach_header* mh = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR); if ( mh != NULL ) { FAIL("library-cant-be-bound: NSAddImage should have failed"); return 1; } - +#else + if ( dlopen("libfoo.dylib", RTLD_LAZY) != NULL ){ + FAIL("library-cant-be-bound: dlopen should have failed"); + return 1; + } +#endif + uint32_t count = _dyld_image_count(); for(uint32_t i=0; i < count; ++i) { const char* name = _dyld_get_image_name(i); diff --git a/unit-tests/test-cases/upward-dylib/Makefile b/unit-tests/test-cases/upward-dylib/Makefile new file mode 100644 index 0000000..9488bac --- /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_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES) + +all: all_$(OS_BAROLO_FEATURES) + +check: check_$(OS_BAROLO_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 libdown.dylib libup.dylib -o main2 + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libup.dylib libdown.dylib -o main1 + + +clean: + ${RM} ${RMFLAGS} *~ main1 main2 libup.dylib libdown.dylib libup.stub + diff --git a/unit-tests/test-cases/upward-dylib/down.c b/unit-tests/test-cases/upward-dylib/down.c new file mode 100644 index 0000000..c5624bd --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/down.c @@ -0,0 +1,22 @@ +#include +#include "up.h" + +static int state = 0; + + +// should run second because down.dylib is lower than up.dylib +static __attribute__((constructor)) void myInit3() +{ + //fprintf(stderr, "myInit3()\n"); + state = 1; +} + +int getdown() +{ + return state; +} + +void other() +{ + whatsup(); +} \ No newline at end of file diff --git a/unit-tests/test-cases/upward-dylib/down.h b/unit-tests/test-cases/upward-dylib/down.h new file mode 100644 index 0000000..534eb92 --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/down.h @@ -0,0 +1,2 @@ +extern int getdown(); + diff --git a/unit-tests/test-cases/upward-dylib/main.c b/unit-tests/test-cases/upward-dylib/main.c new file mode 100644 index 0000000..06b272b --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/main.c @@ -0,0 +1,19 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "up.h" +#include "down.h" + +int main() +{ + if ( whatsup() ) + PASS("upward-dylib"); + else + FAIL("upward-dylib"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/upward-dylib/up.c b/unit-tests/test-cases/upward-dylib/up.c new file mode 100644 index 0000000..2451d74 --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/up.c @@ -0,0 +1,21 @@ +#include +#include "down.h" + +static int state = 0; + +#ifndef STUB +// should run second because up.dylib is higher than down.dylib +static __attribute__((constructor)) void myInit1() +{ + //fprintf(stderr, "myInit1()\n"); + if ( getdown() ) { + state = 1; + } +} +#endif + +int whatsup() +{ + return state; +} + diff --git a/unit-tests/test-cases/upward-dylib/up.h b/unit-tests/test-cases/upward-dylib/up.h new file mode 100644 index 0000000..9ed804c --- /dev/null +++ b/unit-tests/test-cases/upward-dylib/up.h @@ -0,0 +1,2 @@ + +extern int whatsup(); 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..03cb107 --- /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-stubs/bar.c b/unit-tests/test-cases/weak-coalesce-stubs/bar.c new file mode 100644 index 0000000..344a82c --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-stubs/bar.c @@ -0,0 +1,6 @@ +void bar() {} + +void foo() __attribute__((weak)); +void foo() +{ +} \ No newline at end of file diff --git a/unit-tests/test-cases/weak-coalesce-stubs/foo.c b/unit-tests/test-cases/weak-coalesce-stubs/foo.c new file mode 100644 index 0000000..348cf65 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-stubs/foo.c @@ -0,0 +1,61 @@ + + +void foo() __attribute__((weak)); + +void foo() +{ +} + +void abcdefghijklmnopqrstuvwxzy() {} +void abcde3fghijklmnopqrstuvwxzy() {} +void abcdef4ghijklmnopqrstuvwxzy() {} +void abcdefgh5ijklmnopqrstuvwxzy() {} +void abcdefghij6klmnopqrstuvwxzy() {} +void abcdefghijk7lmnopqrstuvwxzy() {} +void abcdefghijklm8nopqrstuvwxzy() {} +void abcdefghijklmn9opqrstuvwxzy() {} +void a1bcdefghijklmnopqrstuvwxzy() {} +void a2bcdefghijklmnopqrstuvwxzy() {} +void a3bcdefghijklmnopqrstuvwxzy() {} +void a4bcdefghijklmnopqrstuvwxzy() {} +void a5bcdefghijklmnopqrstuvwxzy() {} +void a6bcdefghijklmnopqrstuvwxzy() {} +void a7bcdefghijklmnopqrstuvwxzy() {} +void a8bcdefghijklmnopqrstuvwxzy() {} +void a9bcdefghijklmnopqrstuvwxzy() {} +void ab1cdefghijklmnopqrstuvwxzy() {} +void ab2cdefghijklmnopqrstuvwxzy() {} +void ab3cdefghijklmnopqrstuvwxzy() {} +void ab4cdefghijklmnopqrstuvwxzy() {} +void ab5cdefghijklmnopqrstuvwxzy() {} +void ab6cdefghijklmnopqrstuvwxzy() {} +void ab7cdefghijklmnopqrstuvwxzy() {} +void ab8cdefghijklmnopqrstuvwxzy() {} +void ab9cdefghijklmnopqrstuvwxzy() {} +void abc1defghijklmnopqrstuvwxzy() {} +void abc2defghijklmnopqrstuvwxzy() {} +void abc3defghijklmnopqrstuvwxzy() {} +void abc4defghijklmnopqrstuvwxzy() {} +void abc5defghijklmnopqrstuvwxzy() {} +void abc6defghijklmnopqrstuvwxzy() {} +void abc7defghijklmnopqrstuvwxzy() {} +void abc8defghijklmnopqrstuvwxzy() {} +void abc9defghijklmnopqrstuvwxzy() {} +void abcd1efghijklmnopqrstuvwxzy() {} +void abcd2efghijklmnopqrstuvwxzy() {} +void abcd3efghijklmnopqrstuvwxzy() {} +void abcd4efghijklmnopqrstuvwxzy() {} +void abcd5efghijklmnopqrstuvwxzy() {} +void abcd6efghijklmnopqrstuvwxzy() {} +void abcd7efghijklmnopqrstuvwxzy() {} +void abcd8efghijklmnopqrstuvwxzy() {} +void abcd9efghijklmnopqrstuvwxzy() {} +void abcde1fghijklmn9opqrstuvwxzy() {} +void abcde2fghijklmn9opqrstuvwxzy() {} +void abcde3fghijklmn9opqrstuvwxzy() {} +void abcde4fghijklmn9opqrstuvwxzy() {} +void abcde5fghijklmn9opqrstuvwxzy() {} +void abcde6fghijklmn9opqrstuvwxzy() {} +void abcde7fghijklmn9opqrstuvwxzy() {} +void abcde8fghijklmn9opqrstuvwxzy() {} +void abcde9fghijklmn9opqrstuvwxzy() {} diff --git a/unit-tests/test-cases/weak-coalesce-stubs/main.c b/unit-tests/test-cases/weak-coalesce-stubs/main.c new file mode 100644 index 0000000..11a4c89 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-stubs/main.c @@ -0,0 +1,22 @@ +#include +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// Loading MH_DYLIB_STUB causing coalescable miscount + +int main() +{ + // try to load stub many times + for (int i=0; i < 10; ++i) { + void* handle = dlopen("libstub.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("weak-coalesce-stubs: load of libstub.dylib unexpectedly succeeded"); + } + } + // try to load real dylib + dlopen("libbar.dylib", RTLD_LAZY); + PASS("weak-coalesce-stubs"); + return 0; +} -- 2.45.2