From 39a8cd101b922f08058746122efff58c14b57605 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 2 Sep 2009 00:37:08 +0000 Subject: [PATCH] dyld-132.13.tar.gz --- doc/man/man1/dyld.1 | 66 +- doc/man/man1/update_dyld_shared_cache.1 | 51 +- doc/man/man1/update_prebinding.1 | 1 - doc/man/man3/dlopen.3 | 9 +- doc/man/man3/dlsym.3 | 35 +- doc/man/man3/dyld.3 | 4 +- dyld.xcodeproj/project.pbxproj | 439 ++- include/dlfcn.h | 7 +- include/mach-o/dyld-interposing.h | 8 +- include/mach-o/dyld-update-prebinding.h | 19 - include/mach-o/dyld.h | 108 +- include/mach-o/dyld_images.h | 37 +- include/mach-o/dyld_priv.h | 68 +- launch-cache/Architectures.hpp | 27 +- launch-cache/FileAbstraction.hpp | 6 + launch-cache/MachOBinder.hpp | 327 +- launch-cache/MachOFileAbstraction.hpp | 223 +- launch-cache/MachOLayout.hpp | 336 +- launch-cache/MachORebaser.hpp | 351 +- launch-cache/MachOTrie.hpp | 320 ++ launch-cache/ObjCLegacyAbstraction.hpp | 234 ++ launch-cache/ObjCModernAbstraction.hpp | 265 ++ launch-cache/com.apple.dyld.plist | 24 - launch-cache/dsc_iterator.cpp | 107 + launch-cache/dsc_iterator.h | 42 + launch-cache/dyld_cache_format.h | 4 +- launch-cache/dyld_shared_cache.defs | 19 - launch-cache/update_dyld_shared_cache.cpp | 1672 ++++++--- src/ImageLoader.cpp | 665 ++-- src/ImageLoader.h | 316 +- src/ImageLoaderMachO.cpp | 3268 +++++------------ src/ImageLoaderMachO.h | 240 +- src/ImageLoaderMachOClassic.cpp | 1979 ++++++++++ src/ImageLoaderMachOClassic.h | 134 + src/ImageLoaderMachOCompressed.cpp | 1370 +++++++ src/ImageLoaderMachOCompressed.h | 135 + src/ImageLoaderPE.cpp | 24 - src/dyld.cpp | 961 +++-- src/dyld.exp | 20 +- src/dyld.h | 15 +- src/dyld64.exp | 39 - src/dyldAPIs.cpp | 565 ++- src/dyldAPIsInLibSystem.cpp | 178 +- src/dyldExceptions.c | 141 +- src/dyldInitialization.cpp | 30 +- src/dyldLibSystemGlue.c | 49 + src/dyldLibSystemInterface.h | 13 +- src/dyldLock.cpp | 9 +- src/dyldNew.cpp | 67 +- src/dyldStartup.s | 128 +- src/dyld_gdb.cpp | 153 +- src/dyld_stub_binder.s | 163 + src/glue.c | 98 +- src/strip.exp | 4 - src/stub_binding_helper.s | 34 + unit-tests/bin/make-recursive.pl | 1 + unit-tests/bin/pass-iff-exit-zero.pl | 23 + unit-tests/include/common.makefile | 8 +- unit-tests/run-all-unit-tests | 17 +- .../NSAddImage-MATCH_BY_INSTALLNAME/Makefile | 6 +- .../NSAddImage-RETURN_ONLY_IF_LOADED/Makefile | 4 +- .../test-cases/NSAddImage-leafname/Makefile | 4 +- .../NSAddressOfSymbol-NULL/Makefile | 19 + .../test-cases/NSAddressOfSymbol-NULL/main.c | 39 + unit-tests/test-cases/addend/Makefile | 23 + unit-tests/test-cases/addend/foo.c | 7 + unit-tests/test-cases/addend/main.c | 60 + .../test-cases/all_image_infos/Makefile | 38 + unit-tests/test-cases/all_image_infos/foo.c | 2 + unit-tests/test-cases/all_image_infos/main.c | 99 + .../test-cases/always-libSystem/Makefile | 42 + unit-tests/test-cases/always-libSystem/main.c | 49 + unit-tests/test-cases/big-jump-table/Makefile | 12 +- unit-tests/test-cases/big-jump-table/funcs.c | 8 +- unit-tests/test-cases/big-stack/Makefile | 23 +- unit-tests/test-cases/big-stack/main.c | 8 +- unit-tests/test-cases/bundle-basic/Makefile | 4 +- unit-tests/test-cases/bundle-dont-gc/Makefile | 4 +- .../bundle-memory-load-bad/Makefile | 4 +- .../bundle-memory-load-fat/Makefile | 4 +- .../bundle-memory-load-malloc/Makefile | 41 + .../bundle-memory-load-malloc/bundle.c | 32 + .../bundle-memory-load-malloc/main.c | 120 + .../test-cases/bundle-memory-load/Makefile | 4 +- .../test-cases/bundle-multi-link/Makefile | 4 +- .../test-cases/bundle-multi-load/Makefile | 4 +- .../test-cases/bundle-name-ownership/Makefile | 4 +- unit-tests/test-cases/bundle-private/Makefile | 4 +- unit-tests/test-cases/bundle-reload/Makefile | 4 +- .../test-cases/bundle-unlinkable/Makefile | 4 +- .../bundle-unload-keep-mapped/Makefile | 4 +- unit-tests/test-cases/bundle-v-dylib/Makefile | 4 +- unit-tests/test-cases/bundle-weak/Makefile | 4 +- unit-tests/test-cases/bundle-weak/main.c | 2 +- unit-tests/test-cases/crt-apple/Makefile | 4 +- unit-tests/test-cases/crt-argv-NULL/Makefile | 4 +- unit-tests/test-cases/crt-custom/Makefile | 4 +- unit-tests/test-cases/crt-libSystem/Makefile | 16 +- unit-tests/test-cases/crt-result/Makefile | 4 +- unit-tests/test-cases/cxa_finalize/Makefile | 44 + unit-tests/test-cases/cxa_finalize/foo.cxx | 41 + unit-tests/test-cases/cxa_finalize/main.c | 43 + unit-tests/test-cases/deadlock/Makefile | 4 +- .../test-cases/dladdr-stripped/Makefile | 38 + unit-tests/test-cases/dladdr-stripped/main.c | 52 + unit-tests/test-cases/dladdr/Makefile | 9 +- unit-tests/test-cases/dladdr/main.c | 48 +- unit-tests/test-cases/dlclose-basic/Makefile | 4 +- .../test-cases/dlclose-bundle-unload/Makefile | 4 +- .../test-cases/dlclose-dylib-unload/Makefile | 4 +- .../dlclose-terminator-dlclose/Makefile | 4 +- .../test-cases/dlclose-unload-c++/Makefile | 4 +- unit-tests/test-cases/dlclose-unmap/Makefile | 47 + unit-tests/test-cases/dlclose-unmap/foo.c | 2 + unit-tests/test-cases/dlclose-unmap/main.c | 74 + unit-tests/test-cases/dlerror-clear/Makefile | 4 +- unit-tests/test-cases/dlerror/Makefile | 4 +- .../Makefile | 31 + .../dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c | 6 + .../dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c | 42 + .../dlopen-DYLD_LIBRARY_PATH/Makefile | 14 +- .../dlopen-LD_LIBRARY_PATH/Makefile | 19 +- .../dlopen-NULL-RTLD_FIRST/Makefile | 4 +- .../test-cases/dlopen-RTLD_FIRST/Makefile | 4 +- .../test-cases/dlopen-RTLD_GLOBAL/Makefile | 4 +- .../dlopen-RTLD_LOCAL-ignore/Makefile | 4 +- .../dlopen-RTLD_LOCAL-weak/Makefile | 31 + .../test-cases/dlopen-RTLD_LOCAL-weak/bar.c | 5 + .../test-cases/dlopen-RTLD_LOCAL-weak/foo.c | 5 + .../test-cases/dlopen-RTLD_LOCAL-weak/main.c | 77 + .../test-cases/dlopen-RTLD_LOCAL/Makefile | 4 +- .../test-cases/dlopen-RTLD_NODELETE/Makefile | 4 +- .../dlopen-RTLD_NOLOAD-fallback/Makefile | 4 +- .../Makefile | 4 +- .../dlopen-RTLD_NOLOAD-symlink/Makefile | 9 +- .../test-cases/dlopen-RTLD_NOLOAD/Makefile | 4 +- .../test-cases/dlopen-RTLD_NOW/Makefile | 4 +- unit-tests/test-cases/dlopen-basic/Makefile | 4 +- .../test-cases/dlopen-dyld-locking/Makefile | 4 +- .../test-cases/dlopen-dyld-locking/base.c | 4 +- .../test-cases/dlopen-dyld-locking/main.c | 2 +- unit-tests/test-cases/dlopen-error/Makefile | 38 + unit-tests/test-cases/dlopen-error/foo.c | 2 + unit-tests/test-cases/dlopen-error/main.c | 59 + .../test-cases/dlopen-executable/Makefile | 47 + unit-tests/test-cases/dlopen-executable/foo.c | 10 + .../test-cases/dlopen-executable/main.c | 66 + .../dlopen-from-anonymous-code/Makefile | 4 +- .../test-cases/dlopen-in-initializer/Makefile | 4 +- .../dlopen-init-dlopen-notify/Makefile | 62 + .../dlopen-init-dlopen-notify/bar.c | 1 + .../dlopen-init-dlopen-notify/foo.c | 31 + .../dlopen-init-dlopen-notify/foo1.c | 38 + .../dlopen-init-dlopen-notify/foo2.c | 37 + .../dlopen-init-dlopen-notify/main.cxx | 82 + .../test-cases/dlopen-init-dlopen-up/Makefile | 4 +- .../test-cases/dlopen-init-dlopen/Makefile | 4 +- unit-tests/test-cases/dlopen-init-up/Makefile | 4 +- .../test-cases/dlopen-initializer/Makefile | 4 +- .../test-cases/dlopen-leak-threaded/Makefile | 61 + .../test-cases/dlopen-leak-threaded/main.c | 84 + unit-tests/test-cases/dlopen-leak/Makefile | 68 + unit-tests/test-cases/dlopen-leak/bar.c | 3 + unit-tests/test-cases/dlopen-leak/foo.c | 3 + unit-tests/test-cases/dlopen-leak/main.c | 48 + .../dlopen-local-and-global/Makefile | 4 +- unit-tests/test-cases/dlopen-multi/Makefile | 4 +- .../test-cases/dlopen-notify-bind/Makefile | 51 + .../test-cases/dlopen-notify-bind/foo.c | 32 + .../test-cases/dlopen-notify-bind/main.c | 68 + unit-tests/test-cases/dlopen-zero/Makefile | 4 +- unit-tests/test-cases/dlopen-zero/main.c | 1 + .../dlopen_preflight-basic/Makefile | 4 +- .../Makefile | 63 + .../foo.c | 3 + .../main.c | 61 + .../test-cases/dlopen_preflight-leak/Makefile | 65 + .../test-cases/dlopen_preflight-leak/bar.c | 3 + .../test-cases/dlopen_preflight-leak/foo.c | 3 + .../test-cases/dlopen_preflight-leak/main.c | 45 + .../dlopen_preflight-shared-cache/Makefile | 42 + .../dlopen_preflight-shared-cache/bar.c | 1 + .../dlopen_preflight-shared-cache/main.c | 64 + .../test-cases/dlsym-RTLD_DEFAULT/Makefile | 4 +- .../test-cases/dlsym-RTLD_MAIN_ONLY/Makefile | 22 + .../foo.s => dlsym-RTLD_MAIN_ONLY/foo.c} | 15 +- .../test-cases/dlsym-RTLD_MAIN_ONLY/main.c | 51 + .../dlsym-RTLD_NEXT-missing/Makefile | 4 +- .../test-cases/dlsym-RTLD_NEXT/Makefile | 4 +- .../test-cases/dlsym-RTLD_SELF/Makefile | 4 +- unit-tests/test-cases/dlsym-error/Makefile | 4 +- unit-tests/test-cases/dlsym-indirect/Makefile | 4 +- .../test-cases/dtrace-static-probes/Makefile | 52 + .../test-cases/dtrace-static-probes/foo.d | 7 + .../test-cases/dtrace-static-probes/main.c | 13 + .../test-cases/dyld-func-lookup/Makefile | 12 +- .../dyld-launched-prebound/Makefile | 4 +- unit-tests/test-cases/dyld-slide/Makefile | 20 +- unit-tests/test-cases/dyld-slide/main.c | 9 +- .../test-cases/dynamic_cast-basic/Makefile | 24 + .../test-cases/dynamic_cast-basic/foo.cxx | 15 + .../test-cases/dynamic_cast-basic/foo.h | 32 + .../test-cases/dynamic_cast-basic/main.cxx | 10 + .../dynamic_cast-basic/realmain.cxx | 24 + .../env-DYLD_FALLBACK_LIBRARY_PATH/Makefile | 44 + .../env-DYLD_FALLBACK_LIBRARY_PATH/compress.c | 4 + .../env-DYLD_FALLBACK_LIBRARY_PATH/main.c | 24 + .../executable-image-index/Makefile | 4 +- .../fallback-non-unique-leaf-names/Makefile | 24 +- .../test-cases/fallback-with-suid/Makefile | 4 +- unit-tests/test-cases/flat-data/Makefile | 4 +- unit-tests/test-cases/flat-insert/Makefile | 4 +- unit-tests/test-cases/flat-insert/main.c | 2 +- unit-tests/test-cases/flat-prebound/Makefile | 8 +- .../test-cases/flat-private-extern/Makefile | 4 +- .../test-cases/framework-fallback/Makefile | 6 +- .../test-cases/framework-fallback/main.c | 6 +- .../test-cases/ignore-bad-files/Makefile | 4 +- .../image-remove-notification/Makefile | 4 +- unit-tests/test-cases/image-slide/Makefile | 38 + unit-tests/test-cases/image-slide/foo.c | 2 + unit-tests/test-cases/image-slide/main.c | 69 + .../test-cases/image-state-change/Makefile | 4 +- .../test-cases/image-state-change/main.c | 20 +- .../test-cases/image-state-deny-OFI/Makefile | 4 +- .../image-state-deny-dlclose/Makefile | 4 +- .../test-cases/image-state-deny/Makefile | 4 +- .../Makefile | 4 +- unit-tests/test-cases/image-suffix/Makefile | 67 +- .../image_path_containing_address/Makefile | 38 + .../image_path_containing_address/foo.c | 1 + .../image_path_containing_address/main.c | 65 + .../test-cases/init-libSystem-first/Makefile | 43 + .../test-cases/init-libSystem-first/foo.c | 44 + .../test-cases/init-libSystem-first/main.c | 41 + unit-tests/test-cases/init-order/Makefile | 4 +- .../test-cases/initializer-args/Makefile | 4 +- .../insert-libraries-weak-symbols/Makefile | 49 + .../insert-libraries-weak-symbols/insert.c | 37 + .../insert-libraries-weak-symbols/main.c | 42 + .../Makefile | 4 +- .../insert-libraries-with-suid/Makefile | 4 +- .../interpose-basic-prebound/Makefile | 4 +- .../test-cases/interpose-basic/Makefile | 4 +- .../test-cases/interpose-chained/Makefile | 4 +- .../test-cases/interpose-dlsym/Makefile | 4 +- .../interpose-shared-cache/Makefile | 43 + .../test-cases/interpose-shared-cache/main.c | 40 + .../interpose-shared-cache/mymalloc.c | 36 + .../jump-table-dynamic-lookup/Makefile | 4 +- .../test-cases/jump-table-race/Makefile | 4 +- .../lazy-binding-reg-params/Makefile | 4 +- .../test-cases/lazy-binding-reg-params/foo.c | 5 + .../test-cases/lazy-binding-reg-params/foo.h | 15 +- .../test-cases/lazy-binding-reg-params/main.c | 11 +- .../test-cases/lazy-dylib-init-order/Makefile | 42 + .../lazy-dylib-init-order/expected.out | 6 + .../test-cases/lazy-dylib-init-order/foo.c | 21 + .../test-cases/lazy-dylib-init-order/main.c | 25 + .../lazy-dylib-missing-dylib/Makefile | 43 + .../test-cases/lazy-dylib-missing-dylib/foo.c | 6 + .../lazy-dylib-missing-dylib/main.c | 64 + .../lazy-dylib-missing-symbol/Makefile | 44 + .../lazy-dylib-missing-symbol/foo.c | 15 + .../lazy-dylib-missing-symbol/main.c | 71 + .../test-cases/lazy-pointer-binding/Makefile | 4 +- .../test-cases/lib-name-overload/Makefile | 11 +- .../test-cases/loader_path-dup/Makefile | 10 +- unit-tests/test-cases/loader_path/Makefile | 4 +- .../Makefile | 4 +- unit-tests/test-cases/non-lazy-slide/Makefile | 43 + unit-tests/test-cases/non-lazy-slide/bar.c | 3 + unit-tests/test-cases/non-lazy-slide/foo.c | 7 + unit-tests/test-cases/non-lazy-slide/main.c | 23 + unit-tests/test-cases/non-lazy-weak/Makefile | 43 + unit-tests/test-cases/non-lazy-weak/bar.c | 3 + unit-tests/test-cases/non-lazy-weak/foo.c | 7 + unit-tests/test-cases/non-lazy-weak/main.c | 20 + .../test-cases/non-weak-library/Makefile | 10 +- .../test-cases/operator-new-dylib/Makefile | 33 + .../test-cases/operator-new-dylib/foo.cxx | 13 + .../test-cases/operator-new-dylib/main.cxx | 31 + unit-tests/test-cases/operator-new/Makefile | 4 +- unit-tests/test-cases/operator-new/main.cxx | 3 - .../test-cases/partial-library-load/Makefile | 4 +- .../test-cases/pie-DYLD_NO_PIE/Makefile | 34 + unit-tests/test-cases/pie-DYLD_NO_PIE/main.c | 33 + unit-tests/test-cases/pie-basic/Makefile | 4 +- unit-tests/test-cases/pie-big/Makefile | 4 +- .../test-cases/pie-custom-stack/Makefile | 4 +- unit-tests/test-cases/pie-dylib/Makefile | 41 + unit-tests/test-cases/pie-dylib/foo.c | 7 + unit-tests/test-cases/pie-dylib/main.c | 10 + unit-tests/test-cases/pie-text-reloc/Makefile | 57 + .../test-cases/pie-text-reloc/main.c | 12 +- .../test-cases/prebased-performance/Makefile | 4 +- unit-tests/test-cases/progname/Makefile | 4 +- unit-tests/test-cases/pthread-keys/Makefile | 5 +- .../test-cases/re-export-dylib/Makefile | 18 +- .../test-cases/re-export-framework/Makefile | 18 +- .../re-export-sub-framework/Makefile | 18 +- .../Makefile | 4 +- .../Makefile | 4 +- .../Makefile | 13 +- .../foo.c | 3 +- .../main.c | 6 + .../test-cases/read-only-stubs/Makefile | 4 +- unit-tests/test-cases/read-only-stubs/foo.c | 2 + unit-tests/test-cases/read-only-stubs/main.c | 7 + .../test-cases/restrict-environ/Makefile | 38 + unit-tests/test-cases/restrict-environ/main.c | 63 + .../restrict-executable_path/Makefile | 71 + .../test-cases/restrict-executable_path/foo.c | 5 + .../restrict-executable_path/main.c | 14 + .../rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile | 4 +- .../rpath-DYLD_LIBRARY_PATH/Makefile | 3 +- .../test-cases/rpath-DYLD_ROOT_PATH/Makefile | 28 + .../test-cases/rpath-DYLD_ROOT_PATH/foo.c | 4 + .../test-cases/rpath-DYLD_ROOT_PATH/main.c | 16 + .../test-cases/rpath-LD_LIBRARY_PATH/Makefile | 3 +- unit-tests/test-cases/rpath-basic/Makefile | 6 +- .../test-cases/rpath-dlopen-in-dylib/Makefile | 6 +- .../test-cases/rpath-dlopen-indirect/Makefile | 6 +- .../test-cases/rpath-dlopen-leak/Makefile | 3 +- unit-tests/test-cases/rpath-dlopen/Makefile | 6 +- .../test-cases/rpath-executable_path/Makefile | 3 +- .../test-cases/rpath-indirect-suid/Makefile | 8 +- .../rpath-loader_path-dlopen/Makefile | 3 +- .../test-cases/rpath-loader_path/Makefile | 3 +- unit-tests/test-cases/rpath-nesting/Makefile | 8 +- .../test-cases/shared-cache-symlink/Makefile | 4 +- unit-tests/test-cases/suid-environ/Makefile | 4 +- .../test-cases/suid-executable_path/Makefile | 4 +- unit-tests/test-cases/sym-link-load/Makefile | 16 +- unit-tests/test-cases/template/Makefile | 5 +- unit-tests/test-cases/text-relocs/Makefile | 23 +- unit-tests/test-cases/text-relocs/bar.c | 12 +- unit-tests/test-cases/text-relocs/main.c | 17 +- .../test-cases/threaded-lazy-bind/Makefile | 41 + .../test-cases/threaded-lazy-bind/foo.c | 1001 +++++ .../test-cases/threaded-lazy-bind/gen.c | 19 + .../test-cases/threaded-lazy-bind/main.c | 2060 +++++++++++ .../test-cases/trie-symbol-overrun/Makefile | 43 + .../test-cases/trie-symbol-overrun/foo.c | 12 + .../test-cases/trie-symbol-overrun/main.c | 56 + .../unloadable-library-residue/Makefile | 3 +- .../test-cases/weak-coalesce-c++/Makefile | 5 +- unit-tests/test-cases/weak-coalesce/Makefile | 14 +- .../weak-external-reloc-flat/Makefile | 34 + .../test-cases/weak-external-reloc-flat/bar.c | 8 + .../test-cases/weak-external-reloc-flat/baz.c | 8 + .../test-cases/weak-external-reloc-flat/foo.c | 14 + .../weak-external-reloc-flat/main.c | 41 + .../test-cases/weak-external-reloc/Makefile | 30 + .../test-cases/weak-external-reloc/bar.c | 6 + .../test-cases/weak-external-reloc/baz.c | 8 + .../test-cases/weak-external-reloc/foo.c | 10 + .../test-cases/weak-external-reloc/main.c | 10 + .../test-cases/weak-external-reloc/realmain.c | 39 + unit-tests/test-cases/weak-in-dylib/Makefile | 21 + unit-tests/test-cases/weak-in-dylib/foo.c | 7 + unit-tests/test-cases/weak-in-dylib/main.c | 22 + .../test-cases/weak-lazy-slidable/Makefile | 31 + .../test-cases/weak-lazy-slidable/bar.c | 12 + .../test-cases/weak-lazy-slidable/bar3.c | 9 + .../test-cases/weak-lazy-slidable/foo.c | 29 + .../test-cases/weak-lazy-slidable/main.c | 24 + .../test-cases/weak-lazy-slidable/other.c | 7 + unit-tests/test-cases/weak-library/Makefile | 14 +- unit-tests/test-cases/weak-non-lazy/Makefile | 30 + unit-tests/test-cases/weak-non-lazy/bar.c | 6 + unit-tests/test-cases/weak-non-lazy/baz.c | 9 + unit-tests/test-cases/weak-non-lazy/foo.c | 15 + unit-tests/test-cases/weak-non-lazy/main.c | 12 + .../test-cases/weak-non-lazy/realmain.c | 36 + unit-tests/test-cases/weak-override/Makefile | 3 +- unit-tests/test-cases/weak-override/foo.c | 7 + unit-tests/test-cases/weak-override/main.c | 12 +- .../test-cases/weak-symbol-flat/Makefile | 10 +- unit-tests/test-cases/weak-symbol/Makefile | 7 +- unit-tests/test-cases/weak_import/Makefile | 50 + unit-tests/test-cases/weak_import/foo.c | 19 + unit-tests/test-cases/weak_import/foo.h | 16 + unit-tests/test-cases/weak_import/main.c | 59 + .../test-cases/zero-fill-segment/Makefile | 3 +- .../test-cases/zero-length-segment/Makefile | 5 +- 386 files changed, 19240 insertions(+), 5653 deletions(-) delete mode 100644 doc/man/man1/update_prebinding.1 delete mode 100644 include/mach-o/dyld-update-prebinding.h create mode 100644 launch-cache/MachOTrie.hpp create mode 100644 launch-cache/ObjCLegacyAbstraction.hpp create mode 100644 launch-cache/ObjCModernAbstraction.hpp delete mode 100644 launch-cache/com.apple.dyld.plist create mode 100644 launch-cache/dsc_iterator.cpp create mode 100644 launch-cache/dsc_iterator.h delete mode 100644 launch-cache/dyld_shared_cache.defs create mode 100644 src/ImageLoaderMachOClassic.cpp create mode 100644 src/ImageLoaderMachOClassic.h create mode 100644 src/ImageLoaderMachOCompressed.cpp create mode 100644 src/ImageLoaderMachOCompressed.h delete mode 100644 src/ImageLoaderPE.cpp delete mode 100644 src/dyld64.exp create mode 100644 src/dyldLibSystemGlue.c create mode 100644 src/dyld_stub_binder.s delete mode 100644 src/strip.exp create mode 100755 unit-tests/bin/pass-iff-exit-zero.pl create mode 100644 unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile create mode 100644 unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c create mode 100644 unit-tests/test-cases/addend/Makefile create mode 100644 unit-tests/test-cases/addend/foo.c create mode 100644 unit-tests/test-cases/addend/main.c create mode 100644 unit-tests/test-cases/all_image_infos/Makefile create mode 100644 unit-tests/test-cases/all_image_infos/foo.c create mode 100644 unit-tests/test-cases/all_image_infos/main.c create mode 100644 unit-tests/test-cases/always-libSystem/Makefile create mode 100644 unit-tests/test-cases/always-libSystem/main.c create mode 100644 unit-tests/test-cases/bundle-memory-load-malloc/Makefile create mode 100644 unit-tests/test-cases/bundle-memory-load-malloc/bundle.c create mode 100644 unit-tests/test-cases/bundle-memory-load-malloc/main.c create mode 100644 unit-tests/test-cases/cxa_finalize/Makefile create mode 100644 unit-tests/test-cases/cxa_finalize/foo.cxx create mode 100644 unit-tests/test-cases/cxa_finalize/main.c create mode 100644 unit-tests/test-cases/dladdr-stripped/Makefile create mode 100644 unit-tests/test-cases/dladdr-stripped/main.c create mode 100644 unit-tests/test-cases/dlclose-unmap/Makefile create mode 100644 unit-tests/test-cases/dlclose-unmap/foo.c create mode 100644 unit-tests/test-cases/dlclose-unmap/main.c create mode 100644 unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c create mode 100644 unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile create mode 100644 unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c create mode 100644 unit-tests/test-cases/dlopen-error/Makefile create mode 100644 unit-tests/test-cases/dlopen-error/foo.c create mode 100644 unit-tests/test-cases/dlopen-error/main.c create mode 100644 unit-tests/test-cases/dlopen-executable/Makefile create mode 100644 unit-tests/test-cases/dlopen-executable/foo.c create mode 100644 unit-tests/test-cases/dlopen-executable/main.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx create mode 100644 unit-tests/test-cases/dlopen-leak-threaded/Makefile create mode 100644 unit-tests/test-cases/dlopen-leak-threaded/main.c create mode 100644 unit-tests/test-cases/dlopen-leak/Makefile create mode 100644 unit-tests/test-cases/dlopen-leak/bar.c create mode 100644 unit-tests/test-cases/dlopen-leak/foo.c create mode 100644 unit-tests/test-cases/dlopen-leak/main.c create mode 100644 unit-tests/test-cases/dlopen-notify-bind/Makefile create mode 100644 unit-tests/test-cases/dlopen-notify-bind/foo.c create mode 100644 unit-tests/test-cases/dlopen-notify-bind/main.c create mode 100644 unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile create mode 100644 unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c create mode 100644 unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c create mode 100644 unit-tests/test-cases/dlopen_preflight-leak/Makefile create mode 100644 unit-tests/test-cases/dlopen_preflight-leak/bar.c create mode 100644 unit-tests/test-cases/dlopen_preflight-leak/foo.c create mode 100644 unit-tests/test-cases/dlopen_preflight-leak/main.c create mode 100644 unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile create mode 100644 unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c create mode 100644 unit-tests/test-cases/dlopen_preflight-shared-cache/main.c create mode 100644 unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile rename unit-tests/test-cases/{text-relocs/foo.s => dlsym-RTLD_MAIN_ONLY/foo.c} (94%) create mode 100644 unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c create mode 100644 unit-tests/test-cases/dtrace-static-probes/Makefile create mode 100644 unit-tests/test-cases/dtrace-static-probes/foo.d create mode 100644 unit-tests/test-cases/dtrace-static-probes/main.c create mode 100644 unit-tests/test-cases/dynamic_cast-basic/Makefile create mode 100644 unit-tests/test-cases/dynamic_cast-basic/foo.cxx create mode 100644 unit-tests/test-cases/dynamic_cast-basic/foo.h create mode 100644 unit-tests/test-cases/dynamic_cast-basic/main.cxx create mode 100644 unit-tests/test-cases/dynamic_cast-basic/realmain.cxx create mode 100644 unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c create mode 100644 unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/image-slide/Makefile create mode 100644 unit-tests/test-cases/image-slide/foo.c create mode 100644 unit-tests/test-cases/image-slide/main.c create mode 100644 unit-tests/test-cases/image_path_containing_address/Makefile create mode 100644 unit-tests/test-cases/image_path_containing_address/foo.c create mode 100644 unit-tests/test-cases/image_path_containing_address/main.c create mode 100644 unit-tests/test-cases/init-libSystem-first/Makefile create mode 100644 unit-tests/test-cases/init-libSystem-first/foo.c create mode 100644 unit-tests/test-cases/init-libSystem-first/main.c create mode 100644 unit-tests/test-cases/insert-libraries-weak-symbols/Makefile create mode 100644 unit-tests/test-cases/insert-libraries-weak-symbols/insert.c create mode 100644 unit-tests/test-cases/insert-libraries-weak-symbols/main.c create mode 100644 unit-tests/test-cases/interpose-shared-cache/Makefile create mode 100644 unit-tests/test-cases/interpose-shared-cache/main.c create mode 100644 unit-tests/test-cases/interpose-shared-cache/mymalloc.c create mode 100644 unit-tests/test-cases/lazy-dylib-init-order/Makefile create mode 100644 unit-tests/test-cases/lazy-dylib-init-order/expected.out create mode 100644 unit-tests/test-cases/lazy-dylib-init-order/foo.c create mode 100644 unit-tests/test-cases/lazy-dylib-init-order/main.c create mode 100644 unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile create mode 100644 unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c create mode 100644 unit-tests/test-cases/lazy-dylib-missing-dylib/main.c create mode 100644 unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile create mode 100644 unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c create mode 100644 unit-tests/test-cases/lazy-dylib-missing-symbol/main.c create mode 100644 unit-tests/test-cases/non-lazy-slide/Makefile create mode 100644 unit-tests/test-cases/non-lazy-slide/bar.c create mode 100644 unit-tests/test-cases/non-lazy-slide/foo.c create mode 100644 unit-tests/test-cases/non-lazy-slide/main.c create mode 100644 unit-tests/test-cases/non-lazy-weak/Makefile create mode 100644 unit-tests/test-cases/non-lazy-weak/bar.c create mode 100644 unit-tests/test-cases/non-lazy-weak/foo.c create mode 100644 unit-tests/test-cases/non-lazy-weak/main.c create mode 100644 unit-tests/test-cases/operator-new-dylib/Makefile create mode 100644 unit-tests/test-cases/operator-new-dylib/foo.cxx create mode 100644 unit-tests/test-cases/operator-new-dylib/main.cxx create mode 100644 unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile create mode 100644 unit-tests/test-cases/pie-DYLD_NO_PIE/main.c create mode 100644 unit-tests/test-cases/pie-dylib/Makefile create mode 100644 unit-tests/test-cases/pie-dylib/foo.c create mode 100644 unit-tests/test-cases/pie-dylib/main.c create mode 100644 unit-tests/test-cases/pie-text-reloc/Makefile rename src/ImageLoaderPE.h => unit-tests/test-cases/pie-text-reloc/main.c (89%) create mode 100644 unit-tests/test-cases/restrict-environ/Makefile create mode 100644 unit-tests/test-cases/restrict-environ/main.c create mode 100644 unit-tests/test-cases/restrict-executable_path/Makefile create mode 100644 unit-tests/test-cases/restrict-executable_path/foo.c create mode 100644 unit-tests/test-cases/restrict-executable_path/main.c create mode 100644 unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile create mode 100644 unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c create mode 100644 unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c create mode 100644 unit-tests/test-cases/threaded-lazy-bind/Makefile create mode 100644 unit-tests/test-cases/threaded-lazy-bind/foo.c create mode 100644 unit-tests/test-cases/threaded-lazy-bind/gen.c create mode 100644 unit-tests/test-cases/threaded-lazy-bind/main.c create mode 100644 unit-tests/test-cases/trie-symbol-overrun/Makefile create mode 100644 unit-tests/test-cases/trie-symbol-overrun/foo.c create mode 100644 unit-tests/test-cases/trie-symbol-overrun/main.c create mode 100644 unit-tests/test-cases/weak-external-reloc-flat/Makefile create mode 100644 unit-tests/test-cases/weak-external-reloc-flat/bar.c create mode 100644 unit-tests/test-cases/weak-external-reloc-flat/baz.c create mode 100644 unit-tests/test-cases/weak-external-reloc-flat/foo.c create mode 100644 unit-tests/test-cases/weak-external-reloc-flat/main.c create mode 100644 unit-tests/test-cases/weak-external-reloc/Makefile create mode 100644 unit-tests/test-cases/weak-external-reloc/bar.c create mode 100644 unit-tests/test-cases/weak-external-reloc/baz.c create mode 100644 unit-tests/test-cases/weak-external-reloc/foo.c create mode 100644 unit-tests/test-cases/weak-external-reloc/main.c create mode 100644 unit-tests/test-cases/weak-external-reloc/realmain.c create mode 100644 unit-tests/test-cases/weak-in-dylib/Makefile create mode 100644 unit-tests/test-cases/weak-in-dylib/foo.c create mode 100644 unit-tests/test-cases/weak-in-dylib/main.c create mode 100644 unit-tests/test-cases/weak-lazy-slidable/Makefile create mode 100644 unit-tests/test-cases/weak-lazy-slidable/bar.c create mode 100644 unit-tests/test-cases/weak-lazy-slidable/bar3.c create mode 100644 unit-tests/test-cases/weak-lazy-slidable/foo.c create mode 100644 unit-tests/test-cases/weak-lazy-slidable/main.c create mode 100644 unit-tests/test-cases/weak-lazy-slidable/other.c create mode 100644 unit-tests/test-cases/weak-non-lazy/Makefile create mode 100644 unit-tests/test-cases/weak-non-lazy/bar.c create mode 100644 unit-tests/test-cases/weak-non-lazy/baz.c create mode 100644 unit-tests/test-cases/weak-non-lazy/foo.c create mode 100644 unit-tests/test-cases/weak-non-lazy/main.c create mode 100644 unit-tests/test-cases/weak-non-lazy/realmain.c create mode 100644 unit-tests/test-cases/weak_import/Makefile create mode 100644 unit-tests/test-cases/weak_import/foo.c create mode 100644 unit-tests/test-cases/weak_import/foo.h create mode 100644 unit-tests/test-cases/weak_import/main.c diff --git a/doc/man/man1/dyld.1 b/doc/man/man1/dyld.1 index 0ef371a..8570d9a 100644 --- a/doc/man/man1/dyld.1 +++ b/doc/man/man1/dyld.1 @@ -1,4 +1,4 @@ -.TH DYLD 1 "March 23, 2007" "Apple Inc." +.TH DYLD 1 "November 25, 2008" "Apple Inc." .SH NAME dyld \- the dynamic link editor .SH SYNOPSIS @@ -47,6 +47,12 @@ DYLD_PRINT_SEGMENTS DYLD_PRINT_STATISTICS .br DYLD_PRINT_DOFS +.br +DYLD_NO_PIE +.br +DYLD_SHARED_CACHE_DIR +.br +DYLD_SHARED_CACHE_DONT_VALIDATE .SH DESCRIPTION The dynamic linker uses the following environment variables. They affect any program that uses the dynamic linker. @@ -121,7 +127,7 @@ This is a colon separated list of directories. The dynamic linker will prepend this directory paths to every image access until a file is found. .TP .B DYLD_SHARED_REGION -This can be "use" (the default), "avoid", or "private". Settting it to +This can be "use" (the default), "avoid", or "private". Setting it to "avoid" tells dyld to not use the shared cache. All OS dylibs are loaded dynamically just like every other dylib. Setting it to "private" tells dyld to remove the shared region from the process address space and mmap() @@ -209,6 +215,60 @@ Causes dyld to print a line each time a symbolic name is bound. .TP .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. +.TP +.B DYLD_SHARED_CACHE_DIR +This is a directory containing dyld shared cache files. This variable can be used in +conjunction with DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE +to run a process with an alternate shared cache. +.TP +.B DYLD_SHARED_CACHE_DONT_VALIDATE +Causes dyld to not check that the inode and mod-time of files in the shared cache match +the requested dylib on disk. Thus a program can be made to run with the dylib in the +shared cache even though the real dylib has been updated on disk. +.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). +But there are times when a full path is not appropriate; for instance, may want your +binaries to be installable in anywhere on the disk. +To support that, there are three @xxx/ variables that can be used as a path prefix. At runtime dyld +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. +.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. +.TP +.B @rpath/ +Dyld maintains a current stack of paths called the run path list. When @rpath is encountered +it is substituted with each path in the run path list until a loadable dylib if found. +The run path stack is built from the LC_RPATH load commands in the depencency chain +that lead to the current dylib load. +You can add an LC_RPATH load command to an image with the -rpath option to ld(1). You can +even add a LC_RPATH load command path that starts with @loader_path/, and it will push a path +on the run path stack that relative to the image containing the LC_RPATH. +The use of @rpath is most useful when you have a complex directory structure of programs and +dylibs which can be installed anywhere, but keep their relative positions. This scenario +could be implemented using @loader_path, but every client of a dylib could need a different +load path because its relative position in the file system is different. The use of @rpath +introduces a level of indirection that simplies things. You pick a location in your directory +structure as an anchor point. Each dylib then gets an install path that starts with @rpath +and is the path to the dylib relative to the anchor point. Each main executable is linked +with -rpath @loader_path/zzz, where zzz is the path from the executable to the anchor point. +At runtime dyld sets it run path to be the anchor point, then each dylib is found relative +to the anchor point. .SH "SEE ALSO" libtool(1), ld(1), otool(1) diff --git a/doc/man/man1/update_dyld_shared_cache.1 b/doc/man/man1/update_dyld_shared_cache.1 index a370962..f5baa1b 100644 --- a/doc/man/man1/update_dyld_shared_cache.1 +++ b/doc/man/man1/update_dyld_shared_cache.1 @@ -1,4 +1,4 @@ -.Dd March 23, 2007 +.Dd Oct 10, 2008 .Dt update_dyld_shared_cache 1 .Os Darwin .Sh NAME @@ -7,20 +7,26 @@ .Sh SYNOPSIS .Nm .Op Fl root Ar directory +.Op Fl overlay Ar directory .Op Fl arch Ar arch .Op Fl force .Op Fl debug .Op Fl sort_by_name .Op Fl universal_boot +.Op Fl verify +.Op Fl dylib_list Ar file .Sh DESCRIPTION .Nm update_dyld_shared_cache -ensures that dyld's shared cache is up-to-date. Normally, this command -never needs to be manually run. Instead, it is automatically run by launchd -when dyld notices the shared cache is out of date. To prevent the cache -from being regeneated during an install or during development of OS dylibs, -dyld will not trigger a shared cache rebuild if the environment variable -DYLD_NO_FIX_PREBINDING is set, or if the main executable is a setuid binary, -or alternate dylibs are loaded via one of the DYLD_ environment variables. +ensures that dyld's shared cache is up-to-date. This tool is normally +only run by Apple's Installer and Software Update, as they are the only +official ways for OS dylibs to be updated. But if for some reason you +used another mechanism to alter an OS dylib, you should manually run +.Nm update_dyld_shared_cache . +.Pp +Note that the new cache does not take effect until the OS is rebooted. +.Pp +If a safe-boot is +done (booting with shift key held down) the cache is deleted. .Pp The dyld shared cache is mapped by dyld into a process at launch time. Later, when loading @@ -34,7 +40,7 @@ scans the directory /var/db/dyld/shared_region_roots for text files containing p mach-o executables. The full dependencies of all dylibs required by those executables is used to determine which libraries are commonly used and should be placed in the shared cache. If one of the text files contains a path to a dylib, that dylib and its -depenents will be forced into the cache. +dependents will be forced into the cache. .Pp .Nm update_dyld_shared_cache builds a separate cache file for each architecture. The cache files and a readable text @@ -46,8 +52,18 @@ The options are as follows: .Bl -tag .It Fl root Ar directory This option specifies the root of an OS installation for which dyld's -shared cache should be updated. This allosw you to update the -shared cache on a non-running version of the OS. The cache files +shared cache should be updated. This is used by the Installer to update the +dyld shared cache in a partition other than the one you into which you are currently +booted. The cache files are created in the var/db/dyld directory of the specified directory. +Note: if you are manually doing this, be sure to run the update_dyld_shared_cache tool +that is in the partition being updated. This assures the cache format created will +match that expected when booting off that partition. +.It Fl overlay Ar directory +This option specifies the root of a sparse directory tree. When building +the dyld shared cache, any corresponding mach-o files in the sparse directory +will override those in the boot partition. This is used by Software +Update to build a dyld shared cache for the update that is about to be +installed. The cache files are created in the var/db/dyld directory of the specified directory. .It Fl arch Ar arch By default @@ -64,12 +80,19 @@ This option prints out additional information about the work being done. .It Fl sort_by_name By default .Nm update_dyld_shared_cache -assignes a random start address to each mach-o image in the cache. -This option causes the start addresses to be choosen in path order, thus subsequent runs will +assigns a random start address to each mach-o image in the cache. +This option causes the start addresses to be chosen in path order, thus subsequent runs will produce the same address layout which can help reproduce some bugs. .It Fl universal_boot This option can only be used running on an machine with an Intel processor. It builds caches -that can be used when booting on an Intel or a PowerPC based machine. +that can be used when booting on both 32-bit and 64-bit machines. +.It Fl dylib_list Ar file +Instead of scanning /var/db/dyld/shared_region_roots/, this option provides a file that contains +a list of the dylibs to use when building the shared cache file. +.It Fl verify +Will regenerate a shared cache in-memory that matches the randomization of the existing shared +cache file. Then instead of writing the cache file, it compares the in-memory cache file to +the on disk version and reports any differences. .El .Sh FILES .Tp diff --git a/doc/man/man1/update_prebinding.1 b/doc/man/man1/update_prebinding.1 deleted file mode 100644 index a441bed..0000000 --- a/doc/man/man1/update_prebinding.1 +++ /dev/null @@ -1 +0,0 @@ -.so man1/update_dyld_shared_cache.1 diff --git a/doc/man/man3/dlopen.3 b/doc/man/man3/dlopen.3 index 8f88c47..60c2527 100644 --- a/doc/man/man3/dlopen.3 +++ b/doc/man/man3/dlopen.3 @@ -1,4 +1,4 @@ -.Dd Nov 6, 2006 +.Dd Aug 28, 2008 .Os .Dt DLOPEN 3 .Sh NAME @@ -143,10 +143,9 @@ contains a slash (i.e. a full path or a partial path) searches the following the following until it finds a compatible Mach-O file: $DYLD_LIBRARY_PATH (with leaf name from .Fa path -), current working directory (for partial paths), $DYLD_FALLBACK_LIBRARY_PATH -(with leaf name from -.Fa path -). +), then the supplied +.Fa path +(using current working directory for partial paths). .Pp Note: There are no configuration files to control dlopen searching. .Pp diff --git a/doc/man/man3/dlsym.3 b/doc/man/man3/dlsym.3 index 0adbe9a..2cd3f44 100644 --- a/doc/man/man3/dlsym.3 +++ b/doc/man/man3/dlsym.3 @@ -1,4 +1,4 @@ -.Dd Jan 16, 2008 +.Dd August 28, 2008 .Dt DLSYM 3 .Sh NAME .Nm dlsym @@ -39,10 +39,18 @@ If is called with the special .Fa handle .Dv RTLD_NEXT , -then the search for the symbol is limited to the images which were loaded -by the image issuing the call to -.Fn dlsym . -In other words, search the dylib symbols that the calling image linked against when it was built. +then dyld searches for the symbol in the dylibs the calling image +linked against when built. It is usually used when +you intentionally have multiply defined symbol across images +and want to find the "next" definition. It searches other images +for the definition that the caller would be using if it did not +have a definition. The exact search algorithm depends on whether +the caller's image was linked -flat_namespace or -twolevel_namespace. +For flat linked images, the search starts in the load ordered list +of all images, in the image right after the caller's image. +For two-level images, the search simulates how the static linker +would have searched for the symbol when linking the caller's +image. .Pp If .Fn dlsym @@ -53,6 +61,15 @@ then the search for the symbol starts with the image that called .Fn dlsym . If it is not found, the search continues as if RTLD_NEXT was used. .Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_MAIN_ONLY , +then it only searches for +.Fa symbol +in the main executable. +.Pp .Sh RETURN VALUES The .Fn dlsym @@ -62,9 +79,13 @@ condition which may be queried with .Fn dlerror . .Pp .Sh NOTES -Unlike other dyld API's, the symbol name passed to +The symbol name passed to .Fn dlsym -must NOT be prepended with an underscore. +is the name used in C source code. For example to find the address +of function foo(), you would pass "foo" as the symbol name. This +is unlike the older dyld APIs which required a leading underscore. +If you looking up a C++ symbol, you need to use the mangled C++ symbol +name. .Sh SEE ALSO .Xr dlopen 3 .Xr dlsym 3 diff --git a/doc/man/man3/dyld.3 b/doc/man/man3/dyld.3 index 9013b81..285a70c 100644 --- a/doc/man/man3/dyld.3 +++ b/doc/man/man3/dyld.3 @@ -1,4 +1,4 @@ -.Dd August 16, 2006 +.Dd February 12, 2009 .Dt dyld 3 .Sh NAME .Nm _dyld_image_count, @@ -64,7 +64,7 @@ may be adding or removing images during the iteration. .Fn _dyld_get_image_header returns a pointer to the mach header of the image indexed by image_index. If .Fa image_index -isout of range, NULL is returned. +is out of range, NULL is returned. .Pp .Fn _dyld_get_image_vmaddr_slide returns the virtural memory address slide amount of the image indexed by diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index efbfc38..75cd83f 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 42; + objectVersion = 46; objects = { /* Begin PBXAggregateTarget section */ @@ -23,31 +23,31 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - EF79A010070D293E00F78484 /* dyld.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; }; - EF79A011070D295200F78484 /* dladdr.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; }; - EF79A012070D295200F78484 /* dlclose.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; }; - EF79A013070D295200F78484 /* dlerror.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; }; - EF79A014070D295200F78484 /* dlopen.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; }; - EF79A015070D295200F78484 /* dlsym.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; }; - EF79A016070D295200F78484 /* dyld.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; }; + 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 */; }; F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; - F918691608B16D3500E0F9DB /* dyld-interposing.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; }; - F919ECB1090455AB002331E3 /* dyld-update-prebinding.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F919ECAB09045590002331E3 /* dyld-update-prebinding.h */; }; - F93075C30BA1FE4D004BCA09 /* dyld_shared_cache_server.c in Sources */ = {isa = PBXBuildFile; fileRef = F93075C10BA1FE4D004BCA09 /* dyld_shared_cache_server.c */; }; - F932C2520BC32ABB0018B20D /* com.apple.dyld.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = F93075D30BA204FF004BCA09 /* com.apple.dyld.plist */; }; + 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 */; }; - F939F21B078F1A2C00AC144F /* dyld_debug.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F939F219078F1A2100AC144F /* dyld_debug.h */; }; - F93AA9A30630AE1E00301D9F /* dyld_gdb.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; }; - F93AA9A40630AE1E00301D9F /* dyld_priv.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; }; - F93AA9A50630AE1E00301D9F /* dyld.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; }; - F93C8C2A0B84EBFC00615A00 /* update_prebinding.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F93C8C290B84EBFC00615A00 /* update_prebinding.1 */; }; - F9574CB306C95C1B00142BFA /* dlfcn.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; }; - F98D274D0AA79D7400416316 /* dyld_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; }; - F9AB705B0BA73A11002F6068 /* dyld_shared_cache_user.c in Sources */ = {isa = PBXBuildFile; fileRef = F9AB70590BA73A11002F6068 /* dyld_shared_cache_user.c */; settings = {COMPILER_FLAGS = "-fvisibility=hidden"; }; }; + 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 */; }; + F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */; }; F9AC7E940B7BB67700FEB38B /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = F9AC7E930B7BB67700FEB38B /* version.c */; }; - F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; - F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; + F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; }; + 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 */; }; @@ -60,12 +60,13 @@ F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; }; F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; }; F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; }; + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ F921D3160703769A000D1056 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.4_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.cpp; isEditable = 1; outputFiles = ( @@ -73,7 +74,7 @@ }; F921D317070376A6000D1056 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.4_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -81,7 +82,7 @@ }; F921D318070376B0000D1056 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.4_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.asm; isEditable = 1; outputFiles = ( @@ -89,7 +90,7 @@ }; F921D31E070376F1000D1056 /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.4_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.cpp; isEditable = 1; outputFiles = ( @@ -97,7 +98,7 @@ }; F9574C4906C94DA700142BFA /* PBXBuildRule */ = { isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc.4_0; + compilerSpec = com.apple.compilers.gcc; fileType = sourcecode.c; isEditable = 1; outputFiles = ( @@ -113,12 +114,12 @@ remoteGlobalIDString = F93937310A94FAF700070A07; remoteInfo = update_dyld_shared_cache; }; - F93937390A94FB6E00070A07 /* PBXContainerItemProxy */ = { + F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; proxyType = 1; - remoteGlobalIDString = F93937310A94FAF700070A07; - remoteInfo = update_dyld_shared_cache; + remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; + remoteInfo = dsc; }; F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -137,91 +138,107 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - F932C2560BC32AC30018B20D /* CopyFiles */ = { + F90CF2950E71D1FB000BF0F1 /* usr|local|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /System/Library/LaunchDaemons; + dstPath = /usr/local/include; dstSubfolderSpec = 0; files = ( - F932C2520BC32ABB0018B20D /* com.apple.dyld.plist in CopyFiles */, ); + name = "usr|local|include"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9B30630AE8200301D9F /* CopyFiles */ = { + F93AA9B30630AE8200301D9F /* usr|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = "/usr/include/mach-o"; dstSubfolderSpec = 0; files = ( - F939F21B078F1A2C00AC144F /* dyld_debug.h in CopyFiles */, - F93AA9A50630AE1E00301D9F /* dyld.h in CopyFiles */, - F98D274D0AA79D7400416316 /* dyld_images.h in CopyFiles */, + F93AA9A50630AE1E00301D9F /* dyld.h in usr|include|mach-o */, + F98D274D0AA79D7400416316 /* dyld_images.h in usr|include|mach-o */, ); + name = "usr|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9B60630AEB100301D9F /* CopyFiles */ = { + F93AA9B60630AEB100301D9F /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = "/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( - F918691608B16D3500E0F9DB /* dyld-interposing.h in CopyFiles */, - F919ECB1090455AB002331E3 /* dyld-update-prebinding.h in CopyFiles */, - F93AA9A30630AE1E00301D9F /* dyld_gdb.h in CopyFiles */, - F93AA9A40630AE1E00301D9F /* dyld_priv.h in CopyFiles */, + 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 */, ); + name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9C20630AF0700301D9F /* CopyFiles */ = { + F93AA9C20630AF0700301D9F /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - EF79A010070D293E00F78484 /* dyld.1 in CopyFiles */, + EF79A010070D293E00F78484 /* dyld.1 in usr|share|man|man1 */, ); + name = "usr|share|man|man1"; runOnlyForDeploymentPostprocessing = 1; }; - F93AA9C60630AF1F00301D9F /* CopyFiles */ = { + F93AA9C60630AF1F00301D9F /* usr|share|man|man3 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man3; dstSubfolderSpec = 0; files = ( - EF79A011070D295200F78484 /* dladdr.3 in CopyFiles */, - EF79A012070D295200F78484 /* dlclose.3 in CopyFiles */, - EF79A013070D295200F78484 /* dlerror.3 in CopyFiles */, - EF79A014070D295200F78484 /* dlopen.3 in CopyFiles */, - EF79A015070D295200F78484 /* dlsym.3 in CopyFiles */, - EF79A016070D295200F78484 /* dyld.3 in CopyFiles */, - F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in CopyFiles */, - ); + 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 */, + ); + name = "usr|share|man|man3"; runOnlyForDeploymentPostprocessing = 1; }; - F9574CB206C95C0D00142BFA /* CopyFiles */ = { + F9574CB206C95C0D00142BFA /* usr|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/include; dstSubfolderSpec = 0; files = ( - F9574CB306C95C1B00142BFA /* dlfcn.h in CopyFiles */, + F9574CB306C95C1B00142BFA /* dlfcn.h in usr|include */, + ); + name = "usr|include"; + runOnlyForDeploymentPostprocessing = 1; + }; + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "/usr/local/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, ); + name = "usr|local|include|mach-o"; runOnlyForDeploymentPostprocessing = 1; }; - F9D238DD0A9E2FEE002B55C7 /* CopyFiles */ = { + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1; dstSubfolderSpec = 0; files = ( - F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in CopyFiles */, - F93C8C2A0B84EBFC00615A00 /* update_prebinding.1 in CopyFiles */, + F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, ); + name = "usr|share|man|man1"; runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCLegacyAbstraction.hpp; sourceTree = ""; }; + 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCModernAbstraction.hpp; sourceTree = ""; }; EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; }; EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; }; @@ -232,12 +249,6 @@ F906E2230639E96400B13DB2 /* dyld_debug.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyld_debug.c; path = src/dyld_debug.c; sourceTree = ""; }; F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; - F918691708B16D5900E0F9DB /* dyld64.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld64.exp; path = src/dyld64.exp; sourceTree = ""; }; - F919ECAB09045590002331E3 /* dyld-update-prebinding.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-update-prebinding.h"; path = "include/mach-o/dyld-update-prebinding.h"; sourceTree = ""; }; - F93075C10BA1FE4D004BCA09 /* dyld_shared_cache_server.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyld_shared_cache_server.c; path = generated/dyld_shared_cache_server.c; sourceTree = BUILT_PRODUCTS_DIR; }; - F93075C20BA1FE4D004BCA09 /* dyld_shared_cache_server.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_shared_cache_server.h; path = generated/dyld_shared_cache_server.h; sourceTree = BUILT_PRODUCTS_DIR; }; - F93075D30BA204FF004BCA09 /* com.apple.dyld.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = com.apple.dyld.plist; sourceTree = ""; }; - F93075D40BA204FF004BCA09 /* dyld_shared_cache.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = dyld_shared_cache.defs; sourceTree = ""; }; F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; }; F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = ""; }; F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = ""; }; @@ -247,15 +258,17 @@ F93937440A94FC4700070A07 /* MachOLayout.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOLayout.hpp; sourceTree = ""; }; F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = update_dyld_shared_cache.cpp; sourceTree = ""; }; F939F219078F1A2100AC144F /* dyld_debug.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_debug.h; path = "include/mach-o/dyld_debug.h"; sourceTree = ""; }; - F93C8C290B84EBFC00615A00 /* update_prebinding.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_prebinding.1; sourceTree = ""; }; - F973667C0BD004F200E5E1B4 /* ImageLoaderPE.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderPE.cpp; path = src/ImageLoaderPE.cpp; sourceTree = ""; }; - F973667D0BD004F200E5E1B4 /* ImageLoaderPE.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderPE.h; path = src/ImageLoaderPE.h; sourceTree = ""; }; + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = ""; }; + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = ""; }; + 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 = ""; }; 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 = ""; }; F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; - F9AB70590BA73A11002F6068 /* dyld_shared_cache_user.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyld_shared_cache_user.c; path = generated/dyld_shared_cache_user.c; sourceTree = BUILT_PRODUCTS_DIR; }; - F9AB705A0BA73A11002F6068 /* dyld_shared_cache_user.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_shared_cache_user.h; path = generated/dyld_shared_cache_user.h; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; 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; }; @@ -282,6 +295,9 @@ F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = "include/mach-o/dyld_gdb.h"; sourceTree = SOURCE_ROOT; }; F9ED4CE90630A80600DF4E74 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_priv.h; path = "include/mach-o/dyld_priv.h"; sourceTree = SOURCE_ROOT; }; F9ED4CEA0630A80600DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = "include/mach-o/dyld.h"; sourceTree = SOURCE_ROOT; }; + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_iterator.cpp; sourceTree = ""; }; + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -292,6 +308,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -309,7 +332,6 @@ isa = PBXGroup; children = ( EF799FE9070D27BB00F78484 /* dyld.1 */, - F93C8C290B84EBFC00615A00 /* update_prebinding.1 */, F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */, ); name = man1; @@ -342,11 +364,12 @@ F93937440A94FC4700070A07 /* MachOLayout.hpp */, F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */, F98935B90A9A412B00FB6228 /* MachOBinder.hpp */, + F95C95160E994796007B7CB8 /* MachOTrie.hpp */, + 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */, + 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */, F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */, - F93075C10BA1FE4D004BCA09 /* dyld_shared_cache_server.c */, - F93075C20BA1FE4D004BCA09 /* dyld_shared_cache_server.h */, - F93075D30BA204FF004BCA09 /* com.apple.dyld.plist */, - F93075D40BA204FF004BCA09 /* dyld_shared_cache.defs */, + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, ); path = "launch-cache"; sourceTree = ""; @@ -368,6 +391,7 @@ F9ED4C980630A76000DF4E74 /* dyld */, F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */, F93937320A94FAF700070A07 /* update_dyld_shared_cache */, + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */, ); name = Products; sourceTree = ""; @@ -387,20 +411,21 @@ F9ED4CCD0630A7F100DF4E74 /* dyldLock.h */, F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */, F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */, + F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */, F9ED4CD00630A7F100DF4E74 /* glue.c */, F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */, F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */, F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */, F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */, - F973667C0BD004F200E5E1B4 /* ImageLoaderPE.cpp */, - F973667D0BD004F200E5E1B4 /* ImageLoaderPE.h */, + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */, + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */, + F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */, + F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */, F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */, F9B01E3D0739ABDE00CF981B /* dyld.exp */, - F918691708B16D5900E0F9DB /* dyld64.exp */, F9AC7E930B7BB67700FEB38B /* version.c */, - F9AB70590BA73A11002F6068 /* dyld_shared_cache_user.c */, - F9AB705A0BA73A11002F6068 /* dyld_shared_cache_user.h */, F918691408B16D2500E0F9DB /* dyld-interposing.h */, + F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */, F906E2230639E96400B13DB2 /* dyld_debug.c */, ); name = src; @@ -412,7 +437,6 @@ F98D274C0AA79D7400416316 /* dyld_images.h */, F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */, F9ED4CE90630A80600DF4E74 /* dyld_priv.h */, - F919ECAB09045590002331E3 /* dyld-update-prebinding.h */, F939F219078F1A2100AC144F /* dyld_debug.h */, F9ED4CEA0630A80600DF4E74 /* dyld.h */, F99EE6AE06B48D4200BF1992 /* dlfcn.h */, @@ -435,16 +459,14 @@ isa = PBXNativeTarget; buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */; buildPhases = ( - F93075AF0BA1FC60004BCA09 /* ShellScript */, F939372F0A94FAF700070A07 /* Sources */, F93937300A94FAF700070A07 /* Frameworks */, - F9D238DD0A9E2FEE002B55C7 /* CopyFiles */, - F932C2560BC32AC30018B20D /* CopyFiles */, - F93C8C250B84EB2300615A00 /* ShellScript */, + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, ); buildRules = ( ); dependencies = ( + F98C78DA0F7C017F006257D2 /* PBXTargetDependency */, ); name = update_dyld_shared_cache; productName = update_dyld_shared_cache; @@ -463,7 +485,6 @@ F921D3160703769A000D1056 /* PBXBuildRule */, ); dependencies = ( - F939373A0A94FB6E00070A07 /* PBXTargetDependency */, ); name = dyld; productName = dyld; @@ -474,15 +495,15 @@ isa = PBXNativeTarget; buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */; buildPhases = ( - F9AC7E7E0B7BB3D300FEB38B /* ShellScript */, - F9AB70540BA73985002F6068 /* ShellScript */, + F9AC7E7E0B7BB3D300FEB38B /* create version.c */, F9ED4C9C0630A76B00DF4E74 /* Sources */, - F93AA9B30630AE8200301D9F /* CopyFiles */, - F9574CB206C95C0D00142BFA /* CopyFiles */, - F93AA9B60630AEB100301D9F /* CopyFiles */, - F93AA9C20630AF0700301D9F /* CopyFiles */, - F93AA9C60630AF1F00301D9F /* CopyFiles */, - F918692408B16F6900E0F9DB /* ShellScript */, + 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 */, @@ -495,6 +516,23 @@ productReference = F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */; productType = "com.apple.product-type.library.static"; }; + F9F2A5580F7AEE9800B7C9EB /* dsc */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */; + buildPhases = ( + F9F2A5560F7AEE9800B7C9EB /* Sources */, + F9F2A5570F7AEE9800B7C9EB /* Frameworks */, + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dsc; + productName = dsc; + productReference = F9F2A5590F7AEE9800B7C9EB /* libdsc.a */; + productType = "com.apple.product-type.library.static"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -507,24 +545,25 @@ productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; projectDirPath = ""; projectRoot = ""; - shouldCheckCompatibility = 1; targets = ( F9ED4C920630A73900DF4E74 /* all */, F9ED4C970630A76000DF4E74 /* dyld */, F9ED4C9E0630A76B00DF4E74 /* libdyld */, F93937310A94FAF700070A07 /* update_dyld_shared_cache */, + F9F2A5580F7AEE9800B7C9EB /* dsc */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - F918692408B16F6900E0F9DB /* ShellScript */ = { + F918692408B16F6900E0F9DB /* install symlinks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( ); + name = "install symlinks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; @@ -532,61 +571,14 @@ shellScript = "cd ${DSTROOT}/usr/local/lib/system\nln -sf libdyldapis.a libdyldapis_profile.a\nln -sf libdyldapis.a libdyldapis_debug.a\n"; showEnvVarsInLog = 0; }; - F93075AF0BA1FC60004BCA09 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/launch-cache/dyld_shared_cache.defs", - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/generated/dyld_shared_cache_server.h", - "$(BUILT_PRODUCTS_DIR)/generated/dyld_shared_cache_server.c", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "mkdir -p ${BUILT_PRODUCTS_DIR}/generated\nmig -server ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_server.c -sheader ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_server.h -user /dev/null -header /dev/null ${SRCROOT}/launch-cache/dyld_shared_cache.defs\necho \"mig -server ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_server.c -sheader ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_server.h -user /dev/null -header /dev/null ${SRCROOT}/launch-cache/dyld_shared_cache.defs\""; - showEnvVarsInLog = 0; - }; - F93C8C250B84EB2300615A00 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "mkdir -p ${DSTROOT}/usr/bin\ncd ${DSTROOT}/usr/bin\nln -sf update_dyld_shared_cache update_prebinding\n\n\n"; - showEnvVarsInLog = 0; - }; - F9AB70540BA73985002F6068 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/launch-cache/dyld_shared_cache.defs", - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/generated/dyld_shared_cache_user.c", - "$(BUILT_PRODUCTS_DIR)/generated/dyld_shared_cache_user.h", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "mkdir -p ${BUILT_PRODUCTS_DIR}/generated\nmig -user ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_user.c -header ${BUILT_PRODUCTS_DIR}/generated/dyld_shared_cache_user.h -server /dev/null ${SRCROOT}/launch-cache/dyld_shared_cache.defs\n"; - showEnvVarsInLog = 0; - }; - F9AC7E7E0B7BB3D300FEB38B /* ShellScript */ = { + F9AC7E7E0B7BB3D300FEB38B /* create version.c */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "create version.c"; outputPaths = ( "$(BUILT_PRODUCTS_DIR)/version.c", ); @@ -603,7 +595,6 @@ buildActionMask = 2147483647; files = ( F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */, - F93075C30BA1FE4D004BCA09 /* dyld_shared_cache_server.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -622,6 +613,9 @@ 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 */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -632,8 +626,17 @@ 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 */, - F9AB705B0BA73A11002F6068 /* dyld_shared_cache_user.c in Sources */, + F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9F2A5560F7AEE9800B7C9EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -645,10 +648,10 @@ target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */; targetProxy = F93937370A94FB6A00070A07 /* PBXContainerItemProxy */; }; - F939373A0A94FB6E00070A07 /* PBXTargetDependency */ = { + F98C78DA0F7C017F006257D2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */; - targetProxy = F93937390A94FB6E00070A07 /* PBXContainerItemProxy */; + target = F9F2A5580F7AEE9800B7C9EB /* dsc */; + targetProxy = F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */; }; F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = { isa = PBXTargetDependency; @@ -668,8 +671,8 @@ buildSettings = { COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_DYNAMIC_NO_PIC = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_THREADSAFE_STATICS = NO; @@ -689,17 +692,18 @@ F93937360A94FB2900070A07 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; + COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_OPTIMIZATION_LEVEL = 3; GCC_THREADSAFE_STATICS = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = /usr/bin; - MACOSX_DEPLOYMENT_TARGET = 10.5; PREBINDING = NO; PRODUCT_NAME = update_dyld_shared_cache; - VALID_ARCHS = "ppc i386"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + VALID_ARCHS = "x86_64 i386"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -708,20 +712,32 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - BASE_ADDRESS_i386 = 0x8f000000; - BASE_ADDRESS_ppc = 0x8f000000; - BASE_ADDRESS_ppc64 = 0x7fff5fc00000; + ARCHS = ( + ppc, + i386, + x86_64, + ); + BASE_ADDRESS_armv4t = 0x2fe00000; + BASE_ADDRESS_armv5 = 0x2fe00000; + BASE_ADDRESS_armv6 = 0x2fe00000; + BASE_ADDRESS_armv7 = 0x2fe00000; + BASE_ADDRESS_i386 = 0x8fe00000; + BASE_ADDRESS_ppc = 0x8fe00000; BASE_ADDRESS_x86_64 = 0x7fff5fc00000; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; - EXPORTED_SYMBOLS_FILE = "$(EXPORTED_SYMBOLS_FILE_$(CURRENT_ARCH))"; - EXPORTED_SYMBOLS_FILE_i386 = "$(SRCROOT)/src/dyld.exp"; - EXPORTED_SYMBOLS_FILE_ppc = "$(SRCROOT)/src/dyld.exp"; - EXPORTED_SYMBOLS_FILE_ppc64 = "$(SRCROOT)/src/dyld64.exp"; - EXPORTED_SYMBOLS_FILE_x86_64 = "$(SRCROOT)/src/dyld64.exp"; + 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; - GCC_MODEL_TUNING = G4; + GCC_ENABLE_BUILTIN_FUNCTIONS = NO; GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; @@ -733,27 +749,28 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; - MACOSX_DEPLOYMENT_TARGET = 10.5; + 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))", "-nostdlib", /usr/local/lib/system/libc.a, - "-lgcc_eh", + /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 = ""; - PER_ARCH_CFLAGS_ppc64 = "-msoft-float"; PREBINDING = NO; PRODUCT_NAME = dyld; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = ( "-Wmost", @@ -765,21 +782,28 @@ F9D8C7E0087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + BASE_ADDRESS_armv4t = 0x2fe00000; + BASE_ADDRESS_armv5 = 0x2fe00000; + BASE_ADDRESS_armv6 = 0x2fe00000; + BASE_ADDRESS_armv7 = 0x2fe00000; BASE_ADDRESS_i386 = 0x8fe00000; BASE_ADDRESS_ppc = 0x8fe00000; - BASE_ADDRESS_ppc64 = 0x7fff5fc00000; BASE_ADDRESS_x86_64 = 0x7fff5fc00000; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; - EXPORTED_SYMBOLS_FILE = "$(EXPORTED_SYMBOLS_FILE_$(CURRENT_ARCH))"; - EXPORTED_SYMBOLS_FILE_i386 = "$(SRCROOT)/src/dyld.exp"; - EXPORTED_SYMBOLS_FILE_ppc = "$(SRCROOT)/src/dyld.exp"; - EXPORTED_SYMBOLS_FILE_ppc64 = "$(SRCROOT)/src/dyld64.exp"; - EXPORTED_SYMBOLS_FILE_x86_64 = "$(SRCROOT)/src/dyld64.exp"; + 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; GCC_ENABLE_CPP_RTTI = NO; - GCC_MODEL_TUNING = G4; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = "DYLD_VERSION=$(RC_ProjectSourceVersion)"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; @@ -788,28 +812,28 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; - MACOSX_DEPLOYMENT_TARGET = 10.5; + 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))", "-nostdlib", /usr/local/lib/system/libc.a, - "-lgcc_eh", + /usr/local/lib/libCoreSymbolicationSharedWithDyld.a, + "$(EXCEPTION_LIB_$(CURRENT_ARCH))", "-lgcc", "-Wl,-e,__dyld_start", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", - "-Wl,-non_global_symbols_strip_list,$(SRCROOT)/src/strip.exp", ); PER_ARCH_CFLAGS_ppc = ""; - PER_ARCH_CFLAGS_ppc64 = "-msoft-float"; PREBINDING = NO; PRODUCT_NAME = dyld; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = ( "-Wmost", @@ -826,11 +850,11 @@ 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; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -846,11 +870,11 @@ GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = ./include; INSTALL_PATH = /usr/local/lib/system; LIBRARY_STYLE = STATIC; PRODUCT_NAME = dyldapis; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -885,6 +909,48 @@ }; name = Release; }; + F9F2A55A0F7AEE9900B7C9EB /* 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 = dsc; + }; + name = Debug; + }; + 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"; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = NO; + 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_SIGN_COMPARE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/local/lib; + PREBINDING = NO; + PRODUCT_NAME = dsc; + ZERO_LINK = NO; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -933,6 +999,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F2A55A0F7AEE9900B7C9EB /* Debug */, + F9F2A55B0F7AEE9900B7C9EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = F9ED4C8B0630A72300DF4E74 /* Project object */; diff --git a/include/dlfcn.h b/include/dlfcn.h index 2e502c5..7e4ae35 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -75,9 +75,10 @@ extern bool dlopen_preflight(const char* __path) AVAILABLE_MAC_OS_X_VERSION_10_5 /* * Special handle arguments for dlsym(). */ -#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ +#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ #define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ -#define RTLD_SELF ((void *) -3) /* Search this and subsequent objects (Mac OS X 10.5 and later) */ +#define RTLD_SELF ((void *) -3) /* Search this and subsequent objects (Mac OS X 10.5 and later) */ +#define RTLD_MAIN_ONLY ((void *) -5) /* Search main executable only (Mac OS X 10.5 and later) */ #endif /* not POSIX */ #ifdef __cplusplus diff --git a/include/mach-o/dyld-interposing.h b/include/mach-o/dyld-interposing.h index 8584eb9..1621a52 100644 --- a/include/mach-o/dyld-interposing.h +++ b/include/mach-o/dyld-interposing.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,8 +40,8 @@ * DYLD_INTERPOSE(my_open, open) */ -#define DYLD_INTERPOSE(_replacment,_replacee) \ - __attribute__((used)) static struct{ const void* replacment; const void* replacee; } _interpose_##_replacee \ - __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacment, (const void*)(unsigned long)&_replacee }; +#define DYLD_INTERPOSE(_replacement,_replacee) \ + __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \ + __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee }; #endif diff --git a/include/mach-o/dyld-update-prebinding.h b/include/mach-o/dyld-update-prebinding.h deleted file mode 100644 index 9e09413..0000000 --- a/include/mach-o/dyld-update-prebinding.h +++ /dev/null @@ -1,19 +0,0 @@ - -#ifndef _DYLD_UPDATE_PREBINDING_H_ -#define _DYLD_UPDATE_PREBINDING_H_ - -#define UPDATE_PREBINDING_DRY_RUN 0x00000001 -#define UPDATE_PREBINDING_PROGRESS 0x00000002 -#define UPDATE_PREBINDING_DEBUG 0x00000004 -#define UPDATE_PREBINDING_FORCE 0x00000008 -#define UPDATE_PREBINDING_XBUILD_LOG 0x00000010 -#define UPDATE_PREBINDING_SORT 0x00000020 - - - -typedef void (*update_prebinding_ptr)(int pathCount, const char* paths[], uint32_t flags) __attribute__((noreturn)); - - - - -#endif diff --git a/include/mach-o/dyld.h b/include/mach-o/dyld.h index 10b7099..0f9828d 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,7 +29,7 @@ #include #include -#include +#include #if __cplusplus extern "C" { @@ -44,10 +44,10 @@ extern "C" { * will return the mach_header and name of an image, given an address in * the image. dladdr() is thread safe. */ -extern uint32_t _dyld_image_count(void) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; -extern const struct mach_header* _dyld_get_image_header(uint32_t image_index) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; -extern intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; -extern const char* _dyld_get_image_name(uint32_t image_index) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; +extern uint32_t _dyld_image_count(void) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const struct mach_header* _dyld_get_image_header(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const char* _dyld_get_image_name(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); /* @@ -58,8 +58,8 @@ extern const char* _dyld_get_image_name(uint32_t image_index) * _dyld_register_func_for_remove_image() is called after any terminators in an image are run * and before the image is un-memory-mapped. */ -extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; -extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; +extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); /* @@ -67,7 +67,7 @@ extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_ * specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if no such library is loaded. */ -extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; +extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); /* @@ -76,7 +76,7 @@ extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) AVA * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if the main executable did not link * against the specified library. */ -extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; +extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); /* @@ -88,7 +88,7 @@ extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) AVA * That is the path may be a symbolic link and not the real file. With deep directories the total bufsize * needed could be more than MAXPATHLEN. */ -extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; +extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) __OSX_AVAILABLE_STARTING(__MAC_10_2, __IPHONE_2_0); @@ -96,8 +96,8 @@ extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) AVA * _dyld_moninit() and _dyld_func_lookup() are private interface between * dyld and libSystem. */ -extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; -extern int _dyld_func_lookup(const char* dyld_func_name, void **address) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER; +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); @@ -142,23 +142,23 @@ typedef enum { typedef struct __NSObjectFileImage* NSObjectFileImage; /* NSObjectFileImage can only be used with MH_BUNDLE files */ -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; - -extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); typedef struct __NSModule* NSModule; -extern const char* NSNameOfModule(NSModule m) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern const char* NSLibraryNameForModule(NSModule m) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern const char* NSNameOfModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern const char* NSLibraryNameForModule(NSModule m) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); -extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); #define NSLINKMODULE_OPTION_NONE 0x0 #define NSLINKMODULE_OPTION_BINDNOW 0x1 #define NSLINKMODULE_OPTION_PRIVATE 0x2 @@ -166,27 +166,27 @@ extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* modu #define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8 #define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10 -extern bool NSUnLinkModule(NSModule module, uint32_t options) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern bool NSUnLinkModule(NSModule module, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); #define NSUNLINKMODULE_OPTION_NONE 0x0 #define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 #define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2 /* symbol API */ typedef struct __NSSymbol* NSSymbol; -extern bool NSIsSymbolNameDefined(const char* symbolName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern bool NSIsSymbolNameDefined(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 #define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 #define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 -extern const char* NSNameOfSymbol(NSSymbol symbol) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern void * NSAddressOfSymbol(NSSymbol symbol) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern NSModule NSModuleForSymbol(NSSymbol symbol) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern const char* NSNameOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void * NSAddressOfSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern NSModule NSModuleForSymbol(NSSymbol symbol) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); /* error handling API */ typedef enum { @@ -214,7 +214,7 @@ typedef enum { NSOtherErrorInvalidArgs } NSOtherErrorNumbers; -extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); typedef struct { void (*undefined)(const char* symbolName); @@ -223,28 +223,28 @@ typedef struct { const char* fileName, const char* errorString); } NSLinkEditErrorHandlers; -extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); -extern bool NSAddLibrary(const char* pathName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern bool NSAddLibraryWithSearching(const char* pathName) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern bool NSAddLibrary(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern bool NSAddLibraryWithSearching(const char* pathName) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); #define NSADDIMAGE_OPTION_NONE 0x0 #define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 #define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 #define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 #define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 -extern bool _dyld_present(void) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool _dyld_launched_prebound(void) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool _dyld_all_twolevel_modules_prebound(void) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern void _dyld_bind_objc_module(const void* objc_module) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool _dyld_bind_fully_image_containing_address(const void* address) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern bool _dyld_image_containing_address(const void* address) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; -extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; -extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) AVAILABLE_MAC_OS_X_VERSION_10_1_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; - -extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_5; +extern bool _dyld_present(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_launched_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_all_twolevel_modules_prebound(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_bind_objc_module(const void* objc_module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_bind_fully_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern bool _dyld_image_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_4,__IPHONE_NA,__IPHONE_NA); +extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); + +extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_3,__MAC_10_5,__IPHONE_NA,__IPHONE_NA); #if __cplusplus diff --git a/include/mach-o/dyld_images.h b/include/mach-o/dyld_images.h index 1f7b96b..8423f2f 100644 --- a/include/mach-o/dyld_images.h +++ b/include/mach-o/dyld_images.h @@ -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,6 +23,9 @@ #ifndef _DYLD_IMAGES_ #define _DYLD_IMAGES_ +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -43,6 +46,15 @@ extern "C" { * The notification is called after infoArray is updated. This means that if gdb attaches to a process * and infoArray is NULL, gdb can set a break point on notification and let the proccess continue to * run until the break point. Then gdb can inspect the full infoArray. + * + * The dyldVersion field always points to a C string that contains the dyld version. For instance, + * in dyld-127.3, dyldVersion would contain a pointer to "127.3". + * + * The errorMessage and terminationFlags fields are normally zero. If dyld terminates a process + * (for instance because a required dylib or symbol is missing), then the errorMessage field will + * 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. */ enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 }; @@ -58,15 +70,36 @@ struct dyld_image_info { typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]); struct dyld_all_image_infos { - uint32_t version; /* == 1 in Mac OS X 10.4 */ + uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */ uint32_t infoArrayCount; const struct dyld_image_info* infoArray; dyld_image_notifier notification; bool processDetachedFromSharedRegion; + /* 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 */ + void* jitInfo; + /* the following fields are only in version 5 (Mac OS X 10.6) 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 */ + void* coreSymbolicationShmPage; + /* the following field is only in version 7 (Mac OS X 10.6) and later */ + uintptr_t systemOrderFlag; }; 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 + * for dyld and read the 32-bit unsigned int at that location. Adding that value to dyld's + * mach_header address gets you the address of dyld_all_image_infos in dyld. + */ +#define DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET 0x1010 + /* diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 8d327f4..07ef161 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-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2008 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,26 +33,6 @@ extern "C" { #endif /* __cplusplus */ -/* - * Given an imageOffset into an ObjectFileImage, returns - * the segment/section name and offset into that section of - * that imageOffset. Returns FALSE if the imageOffset is not - * in any section. You can used the resulting sectionOffset to - * index into the data returned by NSGetSectionDataInObjectFileImage. - * - * First appeared in Mac OS X 10.3 - * - * SPI: currently only used by ZeroLink to detect +load methods - */ -bool -NSFindSectionAndOffsetInObjectFileImage( - NSObjectFileImage objectFileImage, - unsigned long imageOffset, - const char** segmentName, /* can be NULL */ - const char** sectionName, /* can be NULL */ - unsigned long* sectionOffset); /* can be NULL */ - - // // Possible state changes for which you can register to be notified // @@ -64,7 +44,7 @@ enum dyld_image_states dyld_image_state_bound = 40, dyld_image_state_dependents_initialized = 45, // Only single notification for this dyld_image_state_initialized = 50, - dyld_image_state_terminated = 60 // FIX ME - only called if image has termination routine + dyld_image_state_terminated = 60 // Only single notification for this }; // @@ -89,15 +69,51 @@ dyld_register_image_state_change_handler(enum dyld_image_states state, bool batc // +// get slide for a given loaded mach_header +// Mac OS X 10.6 and later // +extern intptr_t _dyld_get_image_slide(const struct mach_header* mh); + + // -extern void -_dyld_library_locator(const char* (*handler)(const char*)); +// get pointer to this process's dyld_all_image_infos +// Exists in Mac OS X 10.4 and later through _dyld_func_lookup() +// Exists in Mac OS X 10.6 and later through libSystem.dylib +// +const struct dyld_all_image_infos* _dyld_get_all_image_infos(); + + + +struct dyld_unwind_sections +{ + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; +}; + + +// +// Returns true iff some loaded mach-o image contains "addr". +// info->mh mach header of image containing addr +// info->dwarf_section pointer to start of __TEXT/__eh_frame section +// info->dwarf_section_length length of __TEXT/__eh_frame section +// info->compact_unwind_section pointer to start of __TEXT/__unwind_info section +// info->compact_unwind_section_length length of __TEXT/__unwind_info section +// +// Exists in Mac OS X 10.6 and later +extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info); + + +// +// This is an optimized form of dladdr() that only returns the dli_fname field. +// +// Exists in Mac OS X 10.6 and later +extern const char* dyld_image_path_containing_address(const void* addr); -extern void* dlord(void* handle, uint32_t ordinal); /* Mac OS X 10.5 and later */ -#define RTLD_MAIN_ONLY ((void *) -5) /* Search main executable only (Mac OS X 10.5 and later) */ diff --git a/launch-cache/Architectures.hpp b/launch-cache/Architectures.hpp index e735f9e..05d6430 100644 --- a/launch-cache/Architectures.hpp +++ b/launch-cache/Architectures.hpp @@ -35,41 +35,24 @@ struct ppc { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; -}; - -struct ppc64 -{ - typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff32, kPointerDiff64, - kBranch24, kBranch24WeakImport, kBranch14, - kPICBaseLow16, kPICBaseLow14, kPICBaseHigh16, - kAbsLow16, kAbsLow14, kAbsHigh16, kAbsHigh16AddLow }; }; struct x86 { typedef Pointer32 P; - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, - kPCRel32, kPCRel32WeakImport, kAbsolute32 }; }; struct x86_64 { typedef Pointer64 P; - - enum ReferenceKinds { kNoFixUp, kFollowOn, kPointer, kPointerWeakImport, kPointerDiff, kPointerDiff32, - kPCRel32, kPCRel32_1, kPCRel32_2, kPCRel32_4, - kBranchPCRel32, kBranchPCRel32WeakImport, - kPCRel32GOTLoad, kPCRel32GOTLoadWeakImport, - kPCRel32GOT, kPCRel32GOTWeakImport }; }; +struct arm +{ + typedef Pointer32 P; + +}; diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp index 1f7a629..8d75311 100644 --- a/launch-cache/FileAbstraction.hpp +++ b/launch-cache/FileAbstraction.hpp @@ -63,6 +63,9 @@ public: static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } @@ -93,6 +96,9 @@ public: static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); } static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); } + static int32_t get32(const int32_t& from) INLINE { return OSReadLittleInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); } + static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); } static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); } diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp index 6bde8cd..5b99dbf 100644 --- a/launch-cache/MachOBinder.hpp +++ b/launch-cache/MachOBinder.hpp @@ -45,6 +45,7 @@ #include "Architectures.hpp" #include "MachOLayout.hpp" #include "MachORebaser.hpp" +#include "MachOTrie.hpp" @@ -71,14 +72,18 @@ private: typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; struct BinderAndReExportFlag { Binder* binder; bool reExport; }; - typedef __gnu_cxx::hash_map*, __gnu_cxx::hash, CStringEquals> NameToSymbolMap; + typedef __gnu_cxx::hash_map, CStringEquals> NameToAddrMap; void doBindExternalRelocations(); void doBindIndirectSymbols(); void doSetUpDyldSection(); void doSetPreboundUndefines(); + void doBindDyldInfo(); + void doBindDyldLazyInfo(); + void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, + int libraryOrdinal, int64_t addend, const char* symbolName); pint_t resolveUndefined(const macho_nlist

* undefinedSymbol); - const macho_nlist

* findExportedSymbol(const char* name); + bool findExportedSymbolAddress(const char* name, pint_t* result); void bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value); const char* parentUmbrella(); @@ -86,7 +91,7 @@ private: static uint8_t pointerRelocType(); std::vector fDependentDylibs; - NameToSymbolMap fHashTable; + NameToAddrMap fHashTable; uint64_t fDyldBaseAddress; const macho_nlist

* fSymbolTable; const char* fStrings; @@ -94,6 +99,7 @@ private: const macho_segment_command

* fFristWritableSegment; const macho_dylib_command

* fDylibID; const macho_dylib_command

* fParentUmbrella; + const macho_dyld_info_command

* fDyldInfo; bool fOriginallyPrebound; }; @@ -102,7 +108,7 @@ template Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress) : Rebaser(layout), fDyldBaseAddress(dyldBaseAddress), fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), - fFristWritableSegment(NULL), fDylibID(NULL), + fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL), fParentUmbrella(NULL) { fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0); @@ -136,9 +142,16 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress case LC_SUB_FRAMEWORK: fParentUmbrella = (macho_dylib_command

*)cmd; break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

*)cmd; + break; + case LC_RPATH: + throwf("LC_RPATH not supported in dylibs in dyld shared cache"); + break; default: if ( cmd->cmd() & LC_REQ_DYLD ) - throwf("unknown required load command %d", cmd->cmd()); + throwf("unknown required load command 0x%08X", cmd->cmd()); } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -147,38 +160,53 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress if ( fSymbolTable == NULL ) throw "no LC_SYMTAB"; // build hash table - if ( fDynamicInfo->tocoff() == 0 ) { - const macho_nlist

* start = &fSymbolTable[fDynamicInfo->iextdefsym()]; - const macho_nlist

* end = &start[fDynamicInfo->nextdefsym()]; - 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; +// 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* 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; + //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); } } else { - int32_t count = fDynamicInfo->ntoc(); - fHashTable.resize(count); // set initial bucket count - const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)&this->fLinkEditBase[fDynamicInfo->tocoff()]; - for (int32_t i = 0; i < count; ++i) { - 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; + if ( fDynamicInfo->tocoff() == 0 ) { + const macho_nlist

* start = &fSymbolTable[fDynamicInfo->iextdefsym()]; + const macho_nlist

* end = &start[fDynamicInfo->nextdefsym()]; + 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(); + //fprintf(stderr, " 0x%08llX %s\n", sym->n_value(), name); + } + } + else { + int32_t count = fDynamicInfo->ntoc(); + fHashTable.resize(count); // set initial bucket count + const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)&this->fLinkEditBase[fDynamicInfo->tocoff()]; + for (int32_t i = 0; i < count; ++i) { + 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(); + //fprintf(stderr, "- 0x%08llX %s\n", sym->n_value(), name); + } } } - } template <> uint8_t Binder::pointerRelocSize() { return 2; } -template <> uint8_t Binder::pointerRelocSize() { return 3; } template <> uint8_t Binder::pointerRelocSize() { return 2; } template <> uint8_t Binder::pointerRelocSize() { return 3; } +template <> uint8_t Binder::pointerRelocSize() { return 2; } template <> uint8_t Binder::pointerRelocType() { return GENERIC_RELOC_VANILLA; } -template <> uint8_t Binder::pointerRelocType() { return GENERIC_RELOC_VANILLA; } template <> uint8_t Binder::pointerRelocType() { return GENERIC_RELOC_VANILLA; } template <> uint8_t Binder::pointerRelocType() { return X86_64_RELOC_UNSIGNED; } +template <> uint8_t Binder::pointerRelocType() { return ARM_RELOC_VANILLA; } template @@ -312,9 +340,16 @@ template void Binder::bind() { this->doSetUpDyldSection(); - this->doBindExternalRelocations(); - this->doBindIndirectSymbols(); - this->doSetPreboundUndefines(); + if ( fDyldInfo != NULL ) { + this->doBindDyldInfo(); + this->doBindDyldLazyInfo(); + // weak bind info is processed at launch time + } + else { + this->doBindExternalRelocations(); + this->doBindIndirectSymbols(); + this->doSetPreboundUndefines(); + } } @@ -346,6 +381,213 @@ void Binder::doSetUpDyldSection() } } +template +void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, int64_t addend, const char* symbolName) +{ + //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(); + if ( segmentIndex > segments.size() ) + throw "bad segment index in rebase info"; + + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) + throw "flat_namespace linkage not allowed in dyld shared cache"; + + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) + throw "linkage to main executable not allowed in dyld shared cache"; + + if ( libraryOrdinal < 0 ) + throw "bad mach-o binary, special library ordinal not allowd in dyld shared cache"; + + if ( (unsigned)libraryOrdinal > fDependentDylibs.size() ) + throw "bad mach-o binary, library ordinal too big"; + + Binder* binder; + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) + binder = this; + 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()); + + // do actual update + const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex]; + uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segmentOffset; + pint_t* mappedAddrP = (pint_t*)mappedAddr; + uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; + int32_t svalue32new; + switch ( type ) { + case BIND_TYPE_POINTER: + P::setP(*mappedAddrP, targetSymbolAddress + addend); + break; + + case BIND_TYPE_TEXT_ABSOLUTE32: + E::set32(*mappedAddr32, targetSymbolAddress + addend); + break; + + case BIND_TYPE_TEXT_PCREL32: + svalue32new = seg.address() + segmentOffset + 4 - (targetSymbolAddress + addend); + E::set32(*mappedAddr32, svalue32new); + break; + + default: + throw "bad bind type"; + } +} + + + +template +void Binder::doBindDyldLazyInfo() +{ + const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()]; + const uint8_t* end = &p[fDyldInfo->lazy_bind_size()]; + + uint8_t type = BIND_TYPE_POINTER; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // this opcode marks the end of each lazy pointer binding + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_SET_TYPE_IMM: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + throwf("bad lazy bind opcode %d", *p); + } + } + + +} + +template +void Binder::doBindDyldInfo() +{ + const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()]; + const uint8_t* end = &p[fDyldInfo->bind_size()]; + + uint8_t type = 0; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + int64_t addend = 0; + uint32_t count; + uint32_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segmentOffset += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + segmentOffset += sizeof(pint_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName); + 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); + 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); + segmentOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad bind opcode %d", *p); + } + } + + + +} + template void Binder::doSetPreboundUndefines() @@ -376,9 +618,9 @@ void Binder::doSetPreboundUndefines() macho_nlist

* const lastUndefine = &symbolTable[dysymtab->iundefsym()+dysymtab->nundefsym()]; for (macho_nlist

* entry = &symbolTable[dysymtab->iundefsym()]; entry < lastUndefine; ++entry) { if ( entry->n_type() & N_EXT ) { - pint_t pbaddr = this->resolveUndefined(entry); //fprintf(stderr, "doSetPreboundUndefines: r_sym=%s, pbaddr=0x%08X, in %s\n", // &fStrings[entry->n_strx()], pbaddr, this->getDylibID()); + pint_t pbaddr = this->resolveUndefined(entry); entry->set_n_value(pbaddr); } } @@ -469,6 +711,7 @@ void Binder::doBindIndirectSymbols() 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; + //fprintf(stderr, "doBindIndirectSymbols() %s\n", this->fLayout.getFilePath()); for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd() == macho_segment_command

::CMD ) { const macho_segment_command

* seg = (macho_segment_command

*)cmd; @@ -501,6 +744,7 @@ void Binder::doBindIndirectSymbols() break; default: const macho_nlist

* undefinedSymbol = &fSymbolTable[symbolIndex]; + //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]); pint_t symbolAddr = this->resolveUndefined(undefinedSymbol); switch ( sectionType ) { case S_NON_LAZY_SYMBOL_POINTERS: @@ -557,31 +801,32 @@ typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* undefine throw "two-level ordinal out of range"; binder = fDependentDylibs[ordinal-1].binder; } - const macho_nlist

* sym = binder->findExportedSymbol(symbolName); - if ( sym == NULL ) - throwf("could not resolve %s from %s", symbolName, this->getDylibID()); - return sym->n_value(); + pint_t addr; + if ( ! binder->findExportedSymbolAddress(symbolName, &addr) ) + throwf("could not resolve %s expected in %s", symbolName, binder->getDylibID()); + return addr; } } template -const macho_nlist* Binder::findExportedSymbol(const char* name) +bool Binder::findExportedSymbolAddress(const char* name, pint_t* result) { - //fprintf(stderr, "findExportedSymbol(%s) in %s\n", name, this->getDylibID()); - const macho_nlist

* sym = NULL; - typename NameToSymbolMap::iterator pos = fHashTable.find(name); - if ( pos != fHashTable.end() ) - return pos->second; + 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()); + return true; + } // search re-exports for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { if ( it->reExport ) { - sym = it->binder->findExportedSymbol(name); - if ( sym != NULL ) - return sym; + if ( it->binder->findExportedSymbolAddress(name, result) ) + return true; } } - return NULL; + //fprintf(stderr, "findExportedSymbolAddress(%s) => not found in %s\n", name, this->getDylibID()); + return false; } diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp index dadec7b..cba7f8a 100644 --- a/launch-cache/MachOFileAbstraction.hpp +++ b/launch-cache/MachOFileAbstraction.hpp @@ -43,59 +43,30 @@ struct uuid_command { #define S_16BYTE_LITERALS 0xE #endif + #include "FileAbstraction.hpp" #include "Architectures.hpp" +// utility to pair together a cpu-type and cpu-sub-type +struct ArchPair +{ + uint32_t arch; + uint32_t subtype; + + ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} + + bool operator<(const ArchPair& other) const { + if ( this->arch != other.arch ) + return (this->arch < other.arch); + return (this->subtype < other.subtype); + } +}; // // This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness // - - -// -// mach-o file header -// -template struct macho_header_content {}; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; -template <> struct macho_header_content > { mach_header fields; }; -template <> struct macho_header_content > { mach_header_64 fields; }; - -template -class macho_header { -public: - uint32_t magic() const INLINE { return E::get32(header.fields.magic); } - void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } - - uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } - void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } - - uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } - void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } - - uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } - void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } - - uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } - void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } - - uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } - void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } - - uint32_t flags() const INLINE { return E::get32(header.fields.flags); } - void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } - - uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } - void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } - - typedef typename P::E E; -private: - macho_header_content

header; -}; - - // // mach-o load command // @@ -729,10 +700,174 @@ private: }; +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + const macho_segment_command

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

* const cmds = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* segcmd = + (macho_segment_command

*)cmd; + if (0 == strncmp(segname, segcmd->segname(), 16)) { + return segcmd; + } + } + cmd = (const 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); + if (!segcmd) return NULL; + + const macho_section

* sectcmd = (macho_section

*)(segcmd+1); + const 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; + } + } + + return NULL; + } + + typedef typename P::E E; +private: + macho_header_content

header; +}; +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + +#ifndef NO_ULEB +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +static int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +#endif + + #endif // __MACH_O_FILE_ABSTRACTION__ diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index d5baede..945227b 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -69,13 +69,14 @@ public: { public: Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, - uint32_t prot, const char* segName) : fAddress(addr), fSize(vmsize), - fFileOffset(offset), fFileSize(file_size), fPermissions(prot), + uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), + fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot), + fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) { - strlcpy(fName, segName, 16); + strlcpy(fOrigName, segName, 16); } - uint64_t address() const { return fAddress; } + uint64_t address() const { return fOrigAddress; } uint64_t size() const { return fSize; } uint64_t fileOffset() const { return fFileOffset; } uint64_t fileSize() const { return fFileSize; } @@ -83,7 +84,7 @@ public: bool readable() const { return fPermissions & VM_PROT_READ; } bool writable() const { return fPermissions & VM_PROT_WRITE; } bool executable() const { return fPermissions & VM_PROT_EXECUTE; } - const char* name() const { return fName; } + const char* name() const { return fOrigName; } uint64_t newAddress() const { return fNewAddress; } void* mappedAddress() const { return fMappedAddress; } void setNewAddress(uint64_t addr) { fNewAddress = addr; } @@ -91,16 +92,21 @@ public: void setSize(uint64_t new_size) { fSize = new_size; } void setFileOffset(uint64_t new_off) { fFileOffset = new_off; } void setFileSize(uint64_t new_size) { fFileSize = new_size; } - void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; } + void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; } + void reset() { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; } private: - uint64_t fAddress; - uint64_t fSize; - uint64_t fFileOffset; - uint64_t fFileSize; - uint64_t fNewAddress; - void* fMappedAddress; - uint32_t fPermissions; - char fName[16]; + uint64_t fOrigAddress; + uint64_t fOrigSize; + uint64_t fOrigFileOffset; + uint64_t fOrigFileSize; + uint32_t fOrigPermissions; + char fOrigName[16]; + uint64_t fSize; + uint64_t fFileOffset; + uint64_t fFileSize; + uint32_t fPermissions; + uint64_t fNewAddress; + void* fMappedAddress; }; struct Library @@ -108,10 +114,11 @@ public: const char* name; uint32_t currentVersion; uint32_t compatibilityVersion; + bool weakImport; }; - virtual cpu_type_t getArchitecture() const = 0; + virtual ArchPair getArchPair() const = 0; virtual const char* getFilePath() const = 0; virtual uint64_t getOffsetInUniversalFile() const = 0; virtual uint32_t getFileType() const = 0; @@ -119,6 +126,8 @@ public: virtual Library getID() const = 0; virtual bool isSplitSeg() const = 0; virtual bool hasSplitSegInfo() const = 0; + virtual bool isRootOwned() const = 0; + virtual bool inSharableLocation() const = 0; virtual uint32_t getNameFileOffset() const = 0; virtual time_t getLastModTime() const = 0; virtual ino_t getInode() const = 0; @@ -142,10 +151,11 @@ template class MachOLayout : public MachOLayoutAbstraction { public: - MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime); + MachOLayout(const void* machHeader, uint64_t offset, const char* path, + ino_t inode, time_t modTime, uid_t uid); virtual ~MachOLayout() {} - virtual cpu_type_t getArchitecture() const; + virtual ArchPair getArchPair() const { return fArchPair; } virtual const char* getFilePath() const { return fPath; } virtual uint64_t getOffsetInUniversalFile() const { return fOffset; } virtual uint32_t getFileType() const { return fFileType; } @@ -153,6 +163,8 @@ public: virtual Library getID() const { return fDylibID; } virtual bool isSplitSeg() const; virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; } + virtual bool isRootOwned() const { return fRootOwned; } + virtual bool inSharableLocation() const { return fShareableLocation; } virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } virtual time_t getLastModTime() const { return fMTime; } virtual ino_t getInode() const { return fInode; } @@ -167,15 +179,18 @@ public: virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; } virtual uint64_t getWritableVMSize() const { return fVMWritablSize; } virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } - + private: typedef typename A::P P; typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; + static cpu_type_t arch(); + const char* fPath; uint64_t fOffset; uint32_t fFileType; + ArchPair fArchPair; uint32_t fFlags; std::vector fSegments; std::vector fLibraries; @@ -192,6 +207,8 @@ private: uint64_t fVMWritablSize; uint64_t fVMReadOnlySize; bool fHasSplitSegInfo; + bool fRootOwned; + bool fShareableLocation; }; @@ -199,12 +216,12 @@ private: class UniversalMachOLayout { public: - UniversalMachOLayout(const char* path, const std::set* onlyArchs=NULL); + UniversalMachOLayout(const char* path, const std::set* onlyArchs=NULL); ~UniversalMachOLayout() {} - static const UniversalMachOLayout* find(const char* path, const std::set* onlyArchs=NULL); - const MachOLayoutAbstraction* getArch(cpu_type_t) const; - const std::vector& getArchs() const { return fLayouts; } + static const UniversalMachOLayout& find(const char* path, const std::set* onlyArchs=NULL); + const MachOLayoutAbstraction* getSlice(ArchPair ap) const; + const std::vector& allLayouts() const { return fLayouts; } private: struct CStringEquals { @@ -212,6 +229,10 @@ 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 PathToNode fgLayoutCache; const char* fPath; std::vector fLayouts; @@ -220,23 +241,91 @@ private: UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache; -const MachOLayoutAbstraction* UniversalMachOLayout::getArch(cpu_type_t arch) const + + +// 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) { - for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { - const MachOLayoutAbstraction* layout = *it; - if ( layout->getArchitecture() == arch ) - return layout; + 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 UniversalMachOLayout* UniversalMachOLayout::find(const char* path, const std::set* onlyArchs) + + +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; + } + } + } + throwf("no compatible slice found in %s", fPath); +} + + +const UniversalMachOLayout& UniversalMachOLayout::find(const char* path, const std::set* onlyArchs) { // look in cache PathToNode::iterator pos = fgLayoutCache.find(path); if ( pos != fgLayoutCache.end() ) - return pos->second; + return *pos->second; // create UniversalMachOLayout const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs); @@ -244,11 +333,83 @@ const UniversalMachOLayout* UniversalMachOLayout::find(const char* path, const s // add it to cache fgLayoutCache[result->fPath] = result; - return result; + 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) +{ + 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; + } + } + } + } + } + return false; } -UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set* onlyArchs) +UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set* onlyArchs) : fPath(strdup(path)) { // map in whole file @@ -271,54 +432,76 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setmagic == OSSwapBigToHostInt32(FAT_MAGIC) ) { // Fat header is always big-endian - const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); - for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { - uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset); - cpu_type_t curArch = OSSwapBigToHostInt32(archs[i].cputype); - if ( fileOffset > stat_buf.st_size ) - throwf("malformed universal file, slice for architecture 0x%08X is beyond end of file: %s", curArch, path); - try { - if ( (onlyArchs == NULL) || (onlyArchs->count(curArch) != 0) ) { - switch ( curArch ) { + const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header)); + const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_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) ) { + 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); + } + try { + switch ( OSSwapBigToHostInt32(slices[i].cputype) ) { case CPU_TYPE_POWERPC: - fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); - break; - case CPU_TYPE_POWERPC64: - fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; case CPU_TYPE_I386: - fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; case CPU_TYPE_X86_64: - fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + case CPU_TYPE_ARM: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; + case CPU_TYPE_POWERPC64: + // ignore ppc64 slices break; default: - throw "unknown file format"; + throw "unknown slice in fat file"; } } - } - catch (const char* msg) { - fprintf(stderr, "warning: %s for %s\n", msg, path); + catch (const char* msg) { + fprintf(stderr, "warning: %s for %s\n", msg, path); + } } } } else { try { if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { - if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_POWERPC) != 0) ) - fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime)); - } - else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { - if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_POWERPC64) != 0) ) - fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + if ( (onlyArchs == NULL) || compatibleSubtype(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_I386)) { - if ( (onlyArchs == NULL) || (onlyArchs->count(CPU_TYPE_I386) != 0) ) - fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + if ( (onlyArchs == NULL) || compatibleSubtype(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) || (onlyArchs->count(CPU_TYPE_X86_64) != 0) ) - fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + if ( (onlyArchs == NULL) || compatibleSubtype(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)) ) + 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)) { + // ignore ppc64 slices } else { throw "unknown file format"; @@ -336,18 +519,18 @@ 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) - : fPath(path), fOffset(offset), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false) +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) { fDylibID.name = NULL; fDylibID.currentVersion = 0; fDylibID.compatibilityVersion = 0; - + const macho_header

* mh = (const macho_header

*)machHeader; - if ( mh->cputype() != getArchitecture() ) - throw "wrong architecture"; + if ( mh->cputype() != arch() ) + throw "Layout object is wrong architecture"; switch ( mh->filetype() ) { case MH_DYLIB: case MH_BUNDLE: @@ -360,6 +543,8 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } fFlags = mh->flags(); fFileType = mh->filetype(); + fArchPair.arch = mh->cputype(); + fArchPair.subtype = mh->cpusubtype(); const macho_load_command

* const cmds = (macho_load_command

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

)); const uint32_t cmd_count = mh->ncmds(); @@ -373,6 +558,7 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fDylibID.currentVersion = dylib->current_version(); fDylibID.compatibilityVersion = dylib->compatibility_version(); fNameFileOffset = dylib->name() - (char*)machHeader; + fShareableLocation = ( (strncmp(fDylibID.name, "/usr/lib/", 9) == 0) || (strncmp(fDylibID.name, "/System/Library/", 16) == 0) ); } break; case LC_LOAD_DYLIB: @@ -384,6 +570,7 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* lib.name = strdup(dylib->name()); lib.currentVersion = dylib->current_version(); lib.compatibilityVersion = dylib->compatibility_version(); + lib.weakImport = ( cmd->cmd() == LC_LOAD_WEAK_DYLIB ); fLibraries.push_back(lib); } break; @@ -436,10 +623,11 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); } -template <> cpu_type_t MachOLayout::getArchitecture() const { return CPU_TYPE_POWERPC; } -template <> cpu_type_t MachOLayout::getArchitecture() const { return CPU_TYPE_POWERPC64; } -template <> cpu_type_t MachOLayout::getArchitecture() const { return CPU_TYPE_I386; } -template <> cpu_type_t MachOLayout::getArchitecture() const { return CPU_TYPE_X86_64; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_POWERPC; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_I386; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_X86_64; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM; } + template <> bool MachOLayout::isSplitSeg() const @@ -453,6 +641,12 @@ bool MachOLayout::isSplitSeg() const return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); } +template <> +bool MachOLayout::isSplitSeg() const +{ + return ( (this->getFlags() & MH_SPLIT_SEGS) != 0 ); +} + template bool MachOLayout::isSplitSeg() const { diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp index e3c187f..9dcfc6b 100644 --- a/launch-cache/MachORebaser.hpp +++ b/launch-cache/MachORebaser.hpp @@ -40,12 +40,14 @@ #include #include #include +#include #include #include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" #include "MachOLayout.hpp" +#include "MachOTrie.hpp" @@ -80,11 +82,14 @@ protected: pint_t getSlideForNewAddress(pint_t newAddress); private: - pint_t calculateRelocBase(); + void calculateRelocBase(); void adjustLoadCommands(); void adjustSymbolTable(); void adjustDATA(); void adjustCode(); + void applyRebaseInfo(); + void adjustExportInfo(); + void doRebase(int segIndex, uint64_t segOffset, uint8_t type); void adjustSegmentLoadCommand(macho_segment_command

* seg); pint_t getSlideForVMAddress(pint_t vmaddress); pint_t* mappedAddressForVMAddress(pint_t vmaddress); @@ -101,14 +106,19 @@ protected: const MachOLayoutAbstraction& fLayout; private: pint_t fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to + const macho_symtab_command

* fSymbolTable; + const macho_dysymtab_command

* fDynamicSymbolTable; + const macho_dyld_info_command

* fDyldInfo; bool fSplittingSegments; + bool fOrignalVMRelocBaseAddressValid; }; template Rebaser::Rebaser(const MachOLayoutAbstraction& layout) - : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), fSplittingSegments(false) + : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), + fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), fOrignalVMRelocBaseAddressValid(false) { fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); switch ( fHeader->filetype() ) { @@ -130,15 +140,35 @@ Rebaser::Rebaser(const MachOLayoutAbstraction& layout) if ( fLinkEditBase == NULL ) throw "no __LINKEDIT segment"; - fOrignalVMRelocBaseAddress = calculateRelocBase(); + // get symbol table info + 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) { + switch (cmd->cmd()) { + case LC_SYMTAB: + fSymbolTable = (macho_symtab_command

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

*)cmd; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + calculateRelocBase(); fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides(); } template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC; } -template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_POWERPC64; } template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } template bool Rebaser::unequalSlides() const @@ -178,7 +208,10 @@ template void Rebaser::rebase() { // update writable segments that have internal pointers - this->adjustDATA(); + if ( fDyldInfo != NULL ) + this->applyRebaseInfo(); + else + this->adjustDATA(); // if splitting segments, update code-to-data references this->adjustCode(); @@ -191,6 +224,10 @@ void Rebaser::rebase() // update symbol table this->adjustSymbolTable(); + + // update export info + if ( fDyldInfo != NULL ) + this->adjustExportInfo(); } template <> @@ -315,50 +352,103 @@ typename A::P::uint_t Rebaser::getSlideForNewAddress(pint_t newAddress) template typename A::P::uint_t* Rebaser::mappedAddressForRelocAddress(pint_t r_address) { - return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress); + if ( fOrignalVMRelocBaseAddressValid ) + return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress); + else + throw "can't apply relocation. Relocation base not known"; } template void Rebaser::adjustSymbolTable() { - const macho_dysymtab_command

* dysymtab = NULL; - macho_nlist

* symbolTable = NULL; - - // get symbol table info - 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) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - symbolTable = (macho_nlist

*)(&fLinkEditBase[symtab->symoff()]); - } - break; - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } + macho_nlist

* symbolTable = (macho_nlist

*)(&fLinkEditBase[fSymbolTable->symoff()]); // walk all exports and slide their n_value - macho_nlist

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

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

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

* entry = &symbolTable[fDynamicSymbolTable->iextdefsym()]; entry < lastExport; ++entry) { if ( (entry->n_type() & N_TYPE) == N_SECT ) entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); } - // walk all local symbols and slide their n_value (don't adjust and stabs) - macho_nlist

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

* entry = &symbolTable[dysymtab->ilocalsym()]; entry < lastLocal; ++entry) { + // walk all local symbols and slide their n_value (don't adjust any stabs) + macho_nlist

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

* entry = &symbolTable[fDynamicSymbolTable->ilocalsym()]; entry < lastLocal; ++entry) { if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) entry->set_n_value(entry->n_value() + this->getSlideForVMAddress(entry->n_value())); } } +template +void Rebaser::adjustExportInfo() +{ + // if no export info, nothing to adjust + if ( fDyldInfo->export_size() == 0 ) + return; + + // since export info addresses are offsets from mach_header, everything in __TEXT is fine + // only __DATA addresses need to be updated + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off()]; + const uint8_t* end = &start[fDyldInfo->export_size()]; + std::vector originalExports; + try { + parseTrie(start, end, originalExports); + } + catch (const char* msg) { + throwf("%s in %s", msg, fLayout.getFilePath()); + } + + std::vector newExports; + newExports.reserve(originalExports.size()); + pint_t baseAddress = this->getBaseAddress(); + pint_t baseAddressSlide = this->getSlideForVMAddress(baseAddress); + for (std::vector::iterator it=originalExports.begin(); it != originalExports.end(); ++it) { + // remove symbols used by the static linker only + if ( (strncmp(it->name, "$ld$", 4) == 0) + || (strncmp(it->name, ".objc_class_name",16) == 0) + || (strncmp(it->name, ".objc_category_name",19) == 0) ) { + //fprintf(stderr, "ignoring symbol %s\n", it->name); + continue; + } + // adjust symbols in slid segments + //uint32_t oldOffset = it->address; + it->address += (this->getSlideForVMAddress(it->address + baseAddress) - baseAddressSlide); + //fprintf(stderr, "orig=0x%08X, new=0x%08llX, sym=%s\n", oldOffset, it->address, it->name); + newExports.push_back(*it); + } + + // rebuild export trie + std::vector newExportTrieBytes; + newExportTrieBytes.reserve(fDyldInfo->export_size()); + mach_o::trie::makeTrie(newExports, newExportTrieBytes); + // align + while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 ) + newExportTrieBytes.push_back(0); + + // copy into place, zero pad + 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); + } +} @@ -383,7 +473,7 @@ void Rebaser::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToData value64 += codeToDataDelta; A::P::E::set64(*(uint64_t*)p, value64); break; - case 3: // used only for ppc/ppc64, an instruction that sets the hi16 of a register + case 3: // used only for ppc, an instruction that sets the hi16 of a register // adjust low 16 bits of instruction which contain hi16 of distance to something in DATA if ( (codeToDataDelta & 0xFFFF) != 0 ) throwf("codeToDataDelta=0x%0llX is not a multiple of 64K", codeToDataDelta); @@ -476,41 +566,130 @@ void Rebaser::adjustCode() } } - template -void Rebaser::adjustDATA() +void Rebaser::doRebase(int segIndex, uint64_t segOffset, uint8_t type) { - const macho_dysymtab_command

* dysymtab = NULL; + const std::vector& segments = fLayout.getSegments(); + if ( segIndex > segments.size() ) + throw "bad segment index in rebase info"; + const MachOLayoutAbstraction::Segment& seg = segments[segIndex]; + uint8_t* mappedAddr = (uint8_t*)seg.mappedAddress() + segOffset; + pint_t* mappedAddrP = (pint_t*)mappedAddr; + uint32_t* mappedAddr32 = (uint32_t*)mappedAddr; + pint_t valueP; + pint_t valuePnew; + uint32_t value32; + int32_t svalue32; + int32_t svalue32new; + switch ( type ) { + case REBASE_TYPE_POINTER: + valueP= P::getP(*mappedAddrP); + P::setP(*mappedAddrP, valueP + this->getSlideForVMAddress(valueP)); + break; + + case REBASE_TYPE_TEXT_ABSOLUTE32: + value32 = E::get32(*mappedAddr32); + E::set32(*mappedAddr32, value32 + this->getSlideForVMAddress(value32)); + break; + + case REBASE_TYPE_TEXT_PCREL32: + svalue32 = E::get32(*mappedAddr32); + valueP = seg.address() + segOffset + 4 + svalue32; + valuePnew = valueP + this->getSlideForVMAddress(valueP); + svalue32new = seg.address() + segOffset + 4 - valuePnew; + E::set32(*mappedAddr32, svalue32new); + break; + + default: + throw "bad rebase type"; + } +} - // get symbol table info - 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) { - switch (cmd->cmd()) { - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

*)cmd; + +template +void Rebaser::applyRebaseInfo() +{ + const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()]; + const uint8_t* end = &p[fDyldInfo->rebase_size()]; + + uint8_t type = 0; + int segIndex; + uint64_t segOffset = 0; + uint32_t count; + uint32_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + doRebase(segIndex, segOffset, type); + 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); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + doRebase(segIndex, segOffset, type); + 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); + segOffset += skip + sizeof(pint_t); + } + break; + default: + throwf("bad rebase opcode %d", *p); } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } +} - +template +void Rebaser::adjustDATA() +{ // walk all local relocations and slide every pointer - const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[dysymtab->locreloff()]); - const macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]); + const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()]; for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { this->doLocalRelocation(reloc); } // walk non-lazy-pointers and slide the ones that are LOCAL - cmd = cmds; + 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 ) { 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()]; - const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[dysymtab->indirectsymoff()]); + const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[fDynamicSymbolTable->indirectsymoff()]); for(const macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) { const uint32_t indirectTableOffset = sect->reserved1(); @@ -533,23 +712,8 @@ void Rebaser::adjustDATA() template void Rebaser::adjustRelocBaseAddresses() { - // split seg file alreday have reloc base of first writable segment - // only non-split-segs that are being split need this adjusted - if ( (fHeader->flags() & MH_SPLIT_SEGS) == 0 ) { - - // get symbol table to find relocation records - const macho_dysymtab_command

* dysymtab = NULL; - 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) { - switch (cmd->cmd()) { - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } + // split seg file need reloc base to be first writable segment + if ( fSplittingSegments && ((fHeader->flags() & MH_SPLIT_SEGS) == 0) ) { // get amount to adjust reloc address int32_t relocAddressAdjust = 0; @@ -563,15 +727,15 @@ void Rebaser::adjustRelocBaseAddresses() } // walk all local relocations and adjust every address - macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[dysymtab->locreloff()]); - macho_relocation_info

* const relocsEnd = &relocsStart[dysymtab->nlocrel()]; + macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]); + macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()]; for (macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { reloc->set_r_address(reloc->r_address()-relocAddressAdjust); } // walk all external relocations and adjust every address - macho_relocation_info

* const externRelocsStart = (macho_relocation_info

*)(&fLinkEditBase[dysymtab->extreloff()]); - macho_relocation_info

* const externRelocsEnd = &externRelocsStart[dysymtab->nextrel()]; + macho_relocation_info

* const externRelocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->extreloff()]); + macho_relocation_info

* const externRelocsEnd = &externRelocsStart[fDynamicSymbolTable->nextrel()]; for (macho_relocation_info

* reloc=externRelocsStart; reloc < externRelocsEnd; ++reloc) { reloc->set_r_address(reloc->r_address()-relocAddressAdjust); } @@ -657,7 +821,7 @@ void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) template -typename A::P::uint_t Rebaser::calculateRelocBase() +void Rebaser::calculateRelocBase() { const std::vector& segments = fLayout.getSegments(); if ( fHeader->flags() & MH_SPLIT_SEGS ) { @@ -666,38 +830,21 @@ typename A::P::uint_t Rebaser::calculateRelocBase() const MachOLayoutAbstraction::Segment& seg = *it; if ( seg.writable() ) { // found first writable segment - return seg.address(); + fOrignalVMRelocBaseAddress = seg.address(); + fOrignalVMRelocBaseAddressValid = true; } } - throw "no writable segment"; } else { // reloc addresses are from the start of the mapped file (base address) - return segments[0].address(); + fOrignalVMRelocBaseAddress = segments[0].address(); + fOrignalVMRelocBaseAddressValid = true; } } -template <> -ppc64::P::uint_t Rebaser::calculateRelocBase() -{ - // reloc addresses either: - // 1) from the first segment vmaddr if no writable segment is > 4GB from first segment vmaddr - // 2) from start of first writable segment - const std::vector& segments = fLayout.getSegments(); - uint64_t threshold = segments[0].address() + 0x100000000ULL; - for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { - const MachOLayoutAbstraction::Segment& seg = *it; - if ( seg.writable() && (seg.address()+seg.size()) > threshold ) { - // found writable segment with address > 4GB past base address - return seg.address(); - } - } - // just use base address - return segments[0].address(); -} template <> -x86_64::P::uint_t Rebaser::calculateRelocBase() +void Rebaser::calculateRelocBase() { // reloc addresses are always based from the start of the first writable segment const std::vector& segments = fLayout.getSegments(); @@ -705,10 +852,10 @@ x86_64::P::uint_t Rebaser::calculateRelocBase() const MachOLayoutAbstraction::Segment& seg = *it; if ( seg.writable() ) { // found first writable segment - return seg.address(); + fOrignalVMRelocBaseAddress = seg.address(); + fOrignalVMRelocBaseAddressValid = true; } } - throw "no writable segment"; } @@ -748,15 +895,15 @@ public: case CPU_TYPE_POWERPC: fRebasers.push_back(new Rebaser(&p[fileOffset])); break; - case CPU_TYPE_POWERPC64: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; case CPU_TYPE_I386: fRebasers.push_back(new Rebaser(&p[fileOffset])); break; case CPU_TYPE_X86_64: fRebasers.push_back(new Rebaser(&p[fileOffset])); break; + case CPU_TYPE_ARM: + fRebasers.push_back(new Rebaser(&p[fileOffset])); + break; default: throw "unknown file format"; } @@ -771,15 +918,15 @@ public: if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) { fRebasers.push_back(new Rebaser(mh)); } - else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) { - fRebasers.push_back(new Rebaser(mh)); - } else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { fRebasers.push_back(new Rebaser(mh)); } else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { fRebasers.push_back(new Rebaser(mh)); } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) { + fRebasers.push_back(new Rebaser(mh)); + } else { throw "unknown file format"; } diff --git a/launch-cache/MachOTrie.hpp b/launch-cache/MachOTrie.hpp new file mode 100644 index 0000000..7c5d4a7 --- /dev/null +++ b/launch-cache/MachOTrie.hpp @@ -0,0 +1,320 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_TRIE__ +#define __MACH_O_TRIE__ + + +#include "MachOFileAbstraction.hpp" + +namespace mach_o { +namespace trie { + +struct Edge +{ + Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } + ~Edge() { } + const char* fSubString; + struct Node* fChild; + +}; + +struct Node +{ + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~Node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint32_t fFlags; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) { + 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); + return; + } + else { + for (int i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + Node* bNode = new Node(bNodeCummStr); + Node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + Edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + Edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags); + 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->fHaveExportInfo = true; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + int subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr) + // 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); + + // add children + ++nodeSize; // byte for count of chidren + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fHaveExportInfo ) { + // nodes with export info: size, flags, address + out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress)); + append_uleb128(fFlags, out); + append_uleb128(fAddress, out); + } + else { + // no export info + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + append_string(e.fSubString, out); + append_uleb128(e.fChild->fTrieOffset, out); + } + } + +private: + static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + static void append_string(const char* str, std::vector& out) { + for (const char* s = str; *s != '\0'; ++s) + out.push_back(*s); + out.push_back('\0'); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + +}; + +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + + +struct Entry +{ + const char* name; + uint64_t address; + uint64_t flags; +}; + + +inline void makeTrie(const std::vector& input, 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); + } + + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(input.size()*2); + for (std::vector::const_iterator it = input.begin(); it != input.end(); ++it) { + start.addOrderedNodes(it->name, orderedNodes); + } + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + if ( (*it)->updateOffset(offset) ) + more = true; + } + } while ( more ); + + // create trie stream + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + (*it)->appendToStream(output); + } +} + +struct EntryWithOffset +{ + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } +}; + + + +static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, std::vector& output) +{ + if ( p >= end ) + throwf("malformed trie, node %p outside trie (%p -> %p)", p, start, end); + const uint8_t terminalSize = *p++; + 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); + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + } +} + + +inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) +{ + // empty trie has no entries + if ( start == end ) + return; + + char cummulativeString[4000]; + std::vector entries; + processExportNode(start, start, end, cummulativeString, 0, entries); + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) + output.push_back(it->entry); +} + + + + +}; // namespace trie +}; // namespace mach_o + + +#endif // __MACH_O_TRIE__ + + diff --git a/launch-cache/ObjCLegacyAbstraction.hpp b/launch-cache/ObjCLegacyAbstraction.hpp new file mode 100644 index 0000000..ea0329b --- /dev/null +++ b/launch-cache/ObjCLegacyAbstraction.hpp @@ -0,0 +1,234 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +template +struct objc_image_info { + uint32_t version; + uint32_t flags; + + void setFlag(uint32_t bits) INLINE { uint32_t old = A::P::E::get32(flags); A::P::E::set32(flags, old | bits); } + void setSelectorsPrebound() INLINE { setFlag(1<<3); } +}; + +template +struct objc_method { + uint32_t method_name; // SEL + uint32_t method_types; // char * + uint32_t method_imp; // IMP + + uint32_t getName() const INLINE { return A::P::E::get32(method_name); } + void setName(uint32_t newName) INLINE { A::P::E::set32(method_name, newName); } +}; + +template +struct objc_method_list { + enum { OBJC_FIXED_UP = 1771 }; + uint32_t obsolete; // struct objc_method_list * + uint32_t method_count; // int + struct objc_method method_list[0]; + + uint32_t getCount() const INLINE { return A::P::E::get32(method_count); } + void setFixedUp(bool fixed) INLINE { A::P::E::set32(obsolete, fixed ? OBJC_FIXED_UP : 0); } +}; + +template +struct objc_class { + uint32_t isa; // struct objc_class * + uint32_t super_class; // struct objc_class * + uint32_t name; // const char * + uint32_t version; // long + uint32_t info; // long + uint32_t instance_size; // long + uint32_t ivars; // struct objc_ivar_list * + uint32_t methodList; // struct objc_method_list * + uint32_t method_cache; // struct objc_cache * + uint32_t protocols; // objc_protocol_list * + uint32_t ivar_layout; // const char * + uint32_t ext; // struct objc_class_ext * + + struct objc_class *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)); } +}; + +template +struct objc_category { + uint32_t category_name; // char * + uint32_t class_name; // char * + uint32_t instance_methods; // struct objc_method_list * + uint32_t class_methods; // struct objc_method_list * + uint32_t protocols; // objc_protocol_list * + uint32_t size; // uint32_t + uint32_t instance_properties; // struct objc_property_list * + + struct objc_method_list *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)); } +}; + +template +struct objc_symtab { + uint32_t sel_ref_cnt; // unsigned long + uint32_t refs; // SEL * + uint16_t cls_def_cnt; // unsigned short + uint16_t cat_def_cnt; // unsigned short + uint32_t defs[0]; // void * + + uint16_t getClassCount(void) const INLINE { return A::P::E::get16(cls_def_cnt); } + uint16_t getCategoryCount(void) const INLINE { return A::P::E::get16(cat_def_cnt); } + struct objc_class *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])); } +}; + +template +struct objc_module { + uint32_t version; // unsigned long + uint32_t size; // unsigned long + 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)); } +}; + +template +struct objc_method_description { + uint32_t name; // SEL + uint32_t types; // char * + + uint32_t getName() const INLINE { return A::P::E::get32(name); } + void setName(uint32_t newName) INLINE { A::P::E::set32(name, newName); } +}; + +template +struct objc_method_description_list { + uint32_t count; // int + struct objc_method_description list[0]; + + uint32_t getCount() const INLINE { return A::P::E::get32(count); } +}; + +template +struct objc_protocol { + uint32_t isa; // danger! contains strange values! + uint32_t protocol_name; // const char * + uint32_t protocol_list; // struct objc_protocol_list + uint32_t instance_methods; // struct objc_method_description_list * + uint32_t class_methods; // struct objc_method_description_list * + + struct objc_method_description_list *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)); } +}; + + +template +class LegacySelectorUpdater { + + typedef typename A::P P; + + static void visitMethodList(objc_method_list *mlist, V& visitor) + { + for (uint32_t m = 0; m < mlist->getCount(); m++) { + uint64_t oldValue = mlist->method_list[m].getName(); + uint64_t newValue = visitor.visit(oldValue); + mlist->method_list[m].setName(newValue); + } + mlist->setFixedUp(true); + } + + static void visitMethodDescriptionList(objc_method_description_list *mlist, V& visitor) + { + for (uint32_t m = 0; m < mlist->getCount(); m++) { + uint64_t oldValue = mlist->list[m].getName(); + uint64_t newValue = visitor.visit(oldValue); + mlist->list[m].setName(newValue); + } + } + +public: + + static void update(SharedCache* cache, const macho_header

* header, + V& visitor) + { + ArraySection > + modules(cache, header, "__OBJC", "__module_info"); + for (uint64_t m = 0; m < modules.count(); m++) { + objc_symtab *symtab = modules.get(m).getSymtab(cache); + if (!symtab) continue; + + // Method lists in classes + for (uint64_t c = 0; c < symtab->getClassCount(); c++) { + objc_class *cls = symtab->getClass(cache, c); + objc_class *isa = cls->getIsa(cache); + objc_method_list *mlist; + if ((mlist = cls->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = isa->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + } + + // Method lists from categories + for (uint64_t c = 0; c < symtab->getCategoryCount(); c++) { + objc_category *cat = symtab->getCategory(cache, c); + objc_method_list *mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = cat->getClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + } + } + + // Method description lists from protocols + ArraySection > + protocols(cache, header, "__OBJC", "__protocol"); + for (uint64_t p = 0; p < protocols.count(); p++) { + objc_protocol& protocol = protocols.get(p); + objc_method_description_list *mlist; + if ((mlist = protocol.getInstanceMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + if ((mlist = protocol.getClassMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + } + + // Message refs + PointerSection selrefs(cache, header, "__OBJC", "__message_refs"); + for (uint64_t s = 0; s < selrefs.count(); s++) { + uint64_t oldValue = selrefs.getUnmapped(s); + uint64_t newValue = visitor.visit(oldValue); + selrefs.set(s, newValue); + } + + // Mark image_info + const macho_section

*imageInfoSection = + header->getSection("__OBJC", "__image_info"); + if (imageInfoSection) { + objc_image_info *info = (objc_image_info *) + cache->mappedCacheAddressForAddress(imageInfoSection->addr()); + info->setSelectorsPrebound(); + } + } +}; diff --git a/launch-cache/ObjCModernAbstraction.hpp b/launch-cache/ObjCModernAbstraction.hpp new file mode 100644 index 0000000..ef200a5 --- /dev/null +++ b/launch-cache/ObjCModernAbstraction.hpp @@ -0,0 +1,265 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +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 + +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); } +}; + +template +class objc_method_list_t { + uint32_t entsize; + uint32_t count; + objc_method_t first; + + uint32_t getEntsize() const { return A::P::E::get32(entsize) & ~(uint32_t)3; } + +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()); } + + void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); } +}; + +template +class objc_ivar_t { + typename A::P::uint_t offset; // A::P * + typename A::P::uint_t name; // const char * + typename A::P::uint_t type; // const char * + uint32_t alignment; + uint32_t size; +}; + +template +class objc_ivar_list_t { + uint32_t entsize; + uint32_t count; + objc_ivar_t first; + +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)); } +}; + +template +class objc_protocol_t { + typename A::P::uint_t isa; + typename A::P::uint_t name; + typename A::P::uint_t protocols; + typename A::P::uint_t instanceMethods; + typename A::P::uint_t classMethods; + typename A::P::uint_t optionalInstanceMethods; + typename A::P::uint_t optionalClassMethods; + 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)); } + + objc_method_list_t *getOptionalInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(optionalInstanceMethods)); } + + objc_method_list_t *getOptionalClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(optionalClassMethods)); } + +}; + +template +class objc_protocol_list_t { + typename A::P::uint_t count; + typename A::P::uint_t list[0]; +}; + +template < typename P, typename E > +struct pad { }; + +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 + + typename A::P::uint_t ivarLayout; + typename A::P::uint_t name; + typename A::P::uint_t baseMethods; + typename A::P::uint_t baseProtocols; + typename A::P::uint_t ivars; + typename A::P::uint_t weakIvarLayout; + typename A::P::uint_t baseProperties; + +public: + objc_method_list_t *getMethodList(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedCacheAddressForAddress(A::P::getP(baseMethods)); } +}; + +template +class objc_class_t { + typename A::P::uint_t isa; + typename A::P::uint_t superclass; + typename A::P::uint_t method_cache; + typename A::P::uint_t vtable; + typename A::P::uint_t data; + +public: + objc_class_t *getIsa(SharedCache *cache) const { return (objc_class_t *)cache->mappedCacheAddressForAddress(A::P::getP(isa)); } + + objc_class_data_t *getData(SharedCache* cache) const { return (objc_class_data_t *)cache->mappedCacheAddressForAddress(A::P::getP(data)); } + + objc_method_list_t *getMethodList(SharedCache* cache) const { return getData(cache)->getMethodList(cache); } +}; + + + +template +class objc_category_t { + typename A::P::uint_t name; + typename A::P::uint_t cls; + typename A::P::uint_t instanceMethods; + typename A::P::uint_t classMethods; + typename A::P::uint_t protocols; + typename A::P::uint_t instanceProperties; + +public: + 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)); } +}; + +template +class objc_message_ref_t { + typename A::P::uint_t imp; + typename A::P::uint_t sel; + +public: + typename A::P::uint_t getName() const { return A::P::getP(sel); } + + void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); } +}; + +template +class SelectorUpdater { + + 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(); + } + +public: + + static void update(SharedCache* cache, const macho_header

* header, + V& visitor) + { + // 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); + } + if ((mlist = cls->getIsa(cache)->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + } + + // Method lists from categories + PointerSection *> + cats(cache, header, "__DATA", "__objc_catlist"); + for (pint_t i = 0; i < cats.count(); i++) { + objc_category_t *cat = cats.get(i); + objc_method_list_t *mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = cat->getClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + } + + // Method description lists from protocols + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t *proto = protocols.get(i); + objc_method_list_t *mlist; + if ((mlist = proto->getInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = proto->getClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = proto->getOptionalInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = proto->getOptionalClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + } + + // @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); + selrefs.set(i, newValue); + } + + // message references + ArraySection > + msgrefs(cache, header, "__DATA", "__objc_msgrefs"); + 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); + msg.setName(newValue); + } + + // Mark image_info + const macho_section

*imageInfoSection = + header->getSection("__DATA", "__objc_imageinfo"); + if (imageInfoSection) { + objc_image_info *info = (objc_image_info *) + cache->mappedCacheAddressForAddress(imageInfoSection->addr()); + info->setSelectorsPrebound(); + } + } +}; diff --git a/launch-cache/com.apple.dyld.plist b/launch-cache/com.apple.dyld.plist deleted file mode 100644 index c059670..0000000 --- a/launch-cache/com.apple.dyld.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Label - com.apple.dyld - Program - /usr/bin/update_dyld_shared_cache - MachServices - - com.apple.dyld - - - Nice - 10 - LowPriorityIO - - EnvironmentVariables - - DYLD_NO_FIX_PREBINDING - 1 - - - diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp new file mode 100644 index 0000000..5110cbc --- /dev/null +++ b/launch-cache/dsc_iterator.cpp @@ -0,0 +1,107 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 "dsc_iterator.h" +#include "dyld_cache_format.h" +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#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) + { + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { + return &cache[mappings[i].file_offset() + addr - mappings[i].address()]; + } + } + return NULL; + } + + // 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) + { + typedef typename A::P P; + typedef typename A::P::E E; + const macho_header

* mh = (const macho_header

*)machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); + const uint32_t cmd_count = mh->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

* 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()); + } + 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) + { + typedef typename A::P::E E; + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + 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); + } + return 0; + } + +} + + +// Given a pointer to an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment +// in each dylib in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) +{ + const uint8_t* cache = (uint8_t*)shared_cache_file; + if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) + return dyld::walkImages(cache, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) + return dyld::walkImages(cache, callback); + else if ( strcmp((char*)cache, "dyld_v1 ppc") == 0 ) + return dyld::walkImages(cache, callback); + else + return -1; +} + diff --git a/launch-cache/dsc_iterator.h b/launch-cache/dsc_iterator.h new file mode 100644 index 0000000..418eece --- /dev/null +++ b/launch-cache/dsc_iterator.h @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 + +#ifdef __cplusplus +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); + + +#ifdef __cplusplus +} +#endif diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h index c13ff7f..748fff5 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2006-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,7 +26,7 @@ #include #include -#include +#include struct dyld_cache_header diff --git a/launch-cache/dyld_shared_cache.defs b/launch-cache/dyld_shared_cache.defs deleted file mode 100644 index 4914364..0000000 --- a/launch-cache/dyld_shared_cache.defs +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -type cpu_type_t = int; - -subsystem dyld_server 10000; -serverprefix do_; - -simpleroutine dyld_shared_cache_missing( - dyld_port : mach_port_t; - arch : cpu_type_t; - SendTime sendTimeout : natural_t); - - -simpleroutine dyld_shared_cache_out_of_date( - dyld_port : mach_port_t; - arch : cpu_type_t; - SendTime sendTimeout : natural_t); - diff --git a/launch-cache/update_dyld_shared_cache.cpp b/launch-cache/update_dyld_shared_cache.cpp index b0060d4..704aa81 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,8 @@ #include #include #include +#include +#include #include "dyld_cache_format.h" @@ -57,29 +60,51 @@ #include "MachOBinder.hpp" #include "CacheFileAbstraction.hpp" -extern "C" { - #include "dyld_shared_cache_server.h" -} +#define SELOPT_WRITE +#include static bool verbose = false; +static bool progress = false; static std::vector warnings; +static void warn(const char *arch, const char *format, ...) +{ + char *msg; + + va_list args; + va_start(args, format); + ::vasprintf(&msg, format, args); + va_end(args); + + warnings.push_back(msg); + + if ( verbose ) { + ::fprintf(::stderr, "update_dyld_shared_cache: warning: %s%s%s%s\n", + arch ? "for arch " : "", + arch ? arch : "", + arch ? ", " : "", + msg); + } +} + + static uint64_t pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } class ArchGraph { public: - static void addArch(cpu_type_t arch); - static void addRoot(const char* vpath, const std::set& archs); - static void findSharedDylibs(cpu_type_t arch); - static ArchGraph* getArch(cpu_type_t arch) { return fgPerArchGraph[arch]; } - static void setFileSystemRoot(const char* root) { fgFileSystemRoot = root; } - static const char* archName(cpu_type_t arch); + static void addArchPair(ArchPair ap); + static void addRoot(const char* vpath, const std::set& archs); + static void findSharedDylibs(ArchPair ap); + static ArchGraph* graphForArchPair(ArchPair ap) { return fgPerArchGraph[ap]; } + static void setFileSystemRoot(const char* root, bool usesOverlay) { fgFileSystemRoot = root; fgUsesOverlay = usesOverlay; } + static const char* archName(ArchPair ap); - cpu_type_t getArch() { return fArch; } + ArchPair getArchPair() { return fArchPair; } std::set& getSharedDylibs() { return fSharedDylibs; } + const char* archName() { return archName(fArchPair); } private: @@ -109,62 +134,74 @@ private: typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; - ArchGraph(cpu_type_t arch) : fArch(arch) {} - static void addRootForArch(const char* path, const MachOLayoutAbstraction*); + ArchGraph(ArchPair ap) : fArchPair(ap) {} void addRoot(const char* path, const MachOLayoutAbstraction*); DependencyNode* getNode(const char* path); DependencyNode* getNodeForVirtualPath(const char* vpath); - static bool canBeShared(const MachOLayoutAbstraction* layout, cpu_type_t arch, const std::set& possibleLibs, std::map& shareableMap); + static bool canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set& possibleLibs, std::map& shareableMap); - static std::map fgPerArchGraph; + static std::map fgPerArchGraph; static const char* fgFileSystemRoot; + static bool fgUsesOverlay; - cpu_type_t fArch; + ArchPair fArchPair; std::set fRoots; PathToNode fNodes; std::set fSharedDylibs; // use set to avoid duplicates when installname!=realpath }; -std::map ArchGraph::fgPerArchGraph; +std::map ArchGraph::fgPerArchGraph; const char* ArchGraph::fgFileSystemRoot = ""; +bool ArchGraph::fgUsesOverlay = false; -void ArchGraph::addArch(cpu_type_t arch) +void ArchGraph::addArchPair(ArchPair ap) { - //fprintf(stderr, "adding arch 0x%08X\n", arch); - fgPerArchGraph[arch] = new ArchGraph(arch); + //fprintf(stderr, "adding ArchPair 0x%08X,0x%08X\n", ap.arch, ap.subtype); + fgPerArchGraph[ap] = new ArchGraph(ap); } -void ArchGraph::addRoot(const char* vpath, const std::set& archs) +void ArchGraph::addRoot(const char* vpath, const std::set& onlyArchs) { char completePath[strlen(fgFileSystemRoot)+strlen(vpath)+2]; - const char* path; + const char* path = NULL; if ( strlen(fgFileSystemRoot) == 0 ) { path = vpath; } else { strcpy(completePath, fgFileSystemRoot); strcat(completePath, vpath); // assumes vpath starts with '/' - path = completePath; + if ( fgUsesOverlay ) { + // using -overlay means if /overlay/usr/lib exists use it, otherwise use original path + struct stat stat_buf; + if ( stat(completePath, &stat_buf) == 0 ) + path = completePath; + else + path = vpath; + } + else { + // using -root means alway redirect /usr/lib to /rootpath/usr/lib + path = completePath; + } } try { - const UniversalMachOLayout* uni = UniversalMachOLayout::find(path, &archs); - const std::vector& layouts = uni->getArchs(); - for(std::vector::const_iterator it = layouts.begin(); it != layouts.end(); ++it) { - const MachOLayoutAbstraction* layout = *it; - if ( archs.count(layout->getArchitecture()) > 0 ) - ArchGraph::addRootForArch(path, layout); + const UniversalMachOLayout& uni = UniversalMachOLayout::find(path, &onlyArchs); + for(std::set::iterator ait = onlyArchs.begin(); ait != onlyArchs.end(); ++ait) { + try { + const MachOLayoutAbstraction* layout = uni.getSlice(*ait); + 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); + } + } - // don't delete uni, it is owned by UniversalMachOLayout cache } catch (const char* msg) { fprintf(stderr, "update_dyld_shared_cache: warning can't use root %s: %s\n", path, msg); } } -void ArchGraph::addRootForArch(const char* path, const MachOLayoutAbstraction* layout) -{ - ArchGraph* graph = fgPerArchGraph[layout->getArchitecture()]; - graph->addRoot(path, layout); -} + void ArchGraph::addRoot(const char* path, const MachOLayoutAbstraction* layout) { @@ -191,7 +228,18 @@ ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath) char completePath[strlen(fgFileSystemRoot)+strlen(vpath)+2]; strcpy(completePath, fgFileSystemRoot); strcat(completePath, vpath); // assumes vpath starts with '/' - return this->getNode(completePath); + if ( fgUsesOverlay ) { + // using -overlay means if /overlay/usr/lib exists use it, otherwise use original path + struct stat stat_buf; + if ( stat(completePath, &stat_buf) == 0 ) + return this->getNode(completePath); + else + return this->getNode(vpath); + } + else { + // using -root means always use /rootpath/usr/lib + return this->getNode(completePath); + } } } @@ -213,10 +261,10 @@ ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) return pos->second; // still does not exist, so create a new node - const UniversalMachOLayout* uni = UniversalMachOLayout::find(realPath); - DependencyNode* node = new DependencyNode(this, realPath, uni->getArch(fArch)); + const UniversalMachOLayout& uni = UniversalMachOLayout::find(realPath); + DependencyNode* node = new DependencyNode(this, realPath, uni.getSlice(fArchPair)); if ( node->getLayout() == NULL ) { - throwf("%s is missing arch %s", realPath, archName(fArch)); + throwf("%s is missing arch %s", realPath, archName(fArchPair)); } // add realpath to node map fNodes[node->getPath()] = node; @@ -269,8 +317,14 @@ void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* m fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath)); } catch (const char* msg) { - fprintf(stderr, "warning, could not bind %s because %s\n", fPath, msg); - fDependentMissing = true; + if ( it->weakImport && ! fLayout->hasSplitSegInfo() ) { + // ok to ignore missing weak imported dylibs from things that are + // not going to be in the dyld shared cache + } + else { + fprintf(stderr, "warning, could not bind %s because %s\n", fPath, msg); + fDependentMissing = true; + } } } // recurse @@ -297,46 +351,61 @@ ArchGraph::DependencyNode::DependencyNode(ArchGraph* graph, const char* path, co //fprintf(stderr, "new DependencyNode(0x%08X, %s)\n", graph->fArch, path); } -void ArchGraph::findSharedDylibs(cpu_type_t arch) +void ArchGraph::findSharedDylibs(ArchPair ap) { - const PathToNode& nodes = fgPerArchGraph[arch]->fNodes; + const PathToNode& nodes = fgPerArchGraph[ap]->fNodes; std::set possibleLibs; //fprintf(stderr, "shared for arch 0x%08X\n", arch); for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { DependencyNode* node = it->second; - if ( node->allDependentsFound() && (node->useCount() > 1) ) { - if ( node->getLayout()->hasSplitSegInfo() ) - possibleLibs.insert(node->getLayout()); + // 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); } } // prune so that all shareable libs depend only on other shareable libs - std::set& sharedLibs = fgPerArchGraph[arch]->fSharedDylibs; + std::set& sharedLibs = fgPerArchGraph[ap]->fSharedDylibs; std::map shareableMap; for (std::set::iterator lit = possibleLibs.begin(); lit != possibleLibs.end(); ++lit) { - if ( canBeShared(*lit, arch, possibleLibs, shareableMap) ) + if ( canBeShared(*lit, ap, possibleLibs, shareableMap) ) sharedLibs.insert(*lit); } } -const char* ArchGraph::archName(cpu_type_t arch) +const char* ArchGraph::archName(ArchPair ap) { - switch ( arch ) { + switch ( ap.arch ) { case CPU_TYPE_POWERPC: return "ppc"; - case CPU_TYPE_POWERPC64: - return "ppc64"; case CPU_TYPE_I386: return "i386"; case CPU_TYPE_X86_64: return "x86_64"; + case CPU_TYPE_ARM: + switch ( ap.subtype ) { + case CPU_SUBTYPE_ARM_V4T: + return "armv4t"; + case CPU_SUBTYPE_ARM_V6: + return "armv6"; + case CPU_SUBTYPE_ARM_V5TEJ: + return "armv5"; + case CPU_SUBTYPE_ARM_XSCALE: + return "arm-xscale"; + case CPU_SUBTYPE_ARM_V7: + return "armv7"; + default: + return "arm"; + } default: return "unknown"; } } -bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, cpu_type_t arch, const std::set& possibleLibs, std::map& shareableMap) +bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set& possibleLibs, std::map& shareableMap) { // check map which is a cache of results std::map::iterator mapPos = shareableMap.find(layout); @@ -348,37 +417,47 @@ bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, cpu_type_t arc 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", layout->getID().name); + 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); warnings.push_back(msg); if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(arch), msg); + fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg); return false; } // look recursively shareableMap[layout] = true; // mark this shareable early in case of circular references - const PathToNode& nodes = fgPerArchGraph[arch]->fNodes; + const PathToNode& nodes = fgPerArchGraph[ap]->fNodes; const std::vector& dependents = layout->getLibraries(); for (std::vector::const_iterator dit = dependents.begin(); dit != dependents.end(); ++dit) { PathToNode::const_iterator pos = nodes.find(dit->name); if ( pos == nodes.end() ) { + // path from load command does not match any loaded dylibs, maybe there is a temp symlink + char realPath[MAXPATHLEN]; + if ( realpath(dit->name, realPath) != NULL ) { + if ( nodes.find(realPath) != nodes.end() ) + continue; + } shareableMap[layout] = false; char* msg; asprintf(&msg, "can't put %s in shared cache because it depends on %s which can't be found", layout->getID().name, dit->name); warnings.push_back(msg); if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(arch), msg); + fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg); return false; } else { - if ( ! canBeShared(pos->second->getLayout(), arch, possibleLibs, shareableMap) ) { + if ( ! canBeShared(pos->second->getLayout(), ap, possibleLibs, shareableMap) ) { shareableMap[layout] = false; char* msg; asprintf(&msg, "can't put %s in shared cache because it depends on %s which can't be in shared cache", layout->getID().name, dit->name); warnings.push_back(msg); if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(arch), msg); + fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg); return false; } } @@ -391,16 +470,22 @@ template class SharedCache { public: - SharedCache(ArchGraph* graph, bool alphaSort, uint64_t dyldBaseAddress); - bool update(const char* rootPath, const char* cacheDir, bool force, bool optimize, bool deleteExistingFirst, int archIndex, int archCount); - static const char* filename(bool optimized); + SharedCache(ArchGraph* graph, const char* rootPath, 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); private: - typedef typename A::P::E E; + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; - bool notUpToDate(const char* cachePath); + bool notUpToDate(const char* path); bool notUpToDate(const void* cache); - uint8_t* optimizeLINKEDIT(); + uint8_t* optimizeLINKEDIT(bool keepSignatures); + void optimizeObjC(); static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable); static cpu_type_t arch(); @@ -425,75 +510,180 @@ private: { return (strcmp(left.layout->getID().name, right.layout->getID().name) < 0); } }; - struct RandomSorter { - RandomSorter(const std::vector& infos) { - for(typename std::vector::const_iterator it = infos.begin(); it != infos.end(); ++it) - fMap[it->layout] = arc4random(); - } + struct ByCStringSectionSizeSorter { + bool operator()(const LayoutInfo& left, const LayoutInfo& right) { + const std::vector& segs_l = + left.layout->getSegments(); + const std::vector& segs_r = + right.layout->getSegments(); + if (segs_l.size() == 0 || segs_r.size() == 0) { + // one image has no segments + return segs_l.size() > segs_r.size(); + } + const macho_header

*mh_l = (const macho_header

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

*mh_r = (const 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) { + // one image has no cstrings + return cstring_l && !cstring_r; + } + + return cstring_l->size() > cstring_r->size(); + } + }; + + struct Sorter { + Sorter(std::map& map): fMap(map) {} bool operator()(const LayoutInfo& left, const LayoutInfo& right) { return (fMap[left.layout] < fMap[right.layout]); } private: - std::map fMap; + std::map& fMap; }; ArchGraph* fArchGraph; + const bool fVerify; + bool fExistingIsNotUpToDate; + const char* fCacheFilePath; + uint8_t* fExistingCacheForVerification; std::vector fDylibs; std::vector fMappings; uint32_t fHeaderSize; + uint8_t* fInMemoryCache; uint64_t fDyldBaseAddress; uint64_t fLinkEditsTotalUnoptimizedSize; uint64_t fLinkEditsStartAddress; MachOLayoutAbstraction::Segment* fFirstLinkEditSegment; + uint32_t fOffsetOfBindInfoInCombinedLinkedit; + uint32_t fOffsetOfWeakBindInfoInCombinedLinkedit; + uint32_t fOffsetOfLazyBindInfoInCombinedLinkedit; + uint32_t fOffsetOfExportInfoInCombinedLinkedit; + uint32_t fOffsetOfOldSymbolTableInfoInCombinedLinkedit; + uint32_t fLinkEditsTotalOptimizedSize; +}; + + +// Access a section containing a list of pointers +template +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; + +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) + , fCount(fSection ? fSection->size() / sizeof(pint_t) : 0) + { + } + + uint64_t count() const { return fCount; } + + uint64_t getUnmapped(uint64_t index) const { + if (index >= fCount) throwf("index out of range"); + return P::getP(fBase[index]); + } + + T get(uint64_t index) const { + return (T)fCache->mappedCacheAddressForAddress(getUnmapped(index)); + } + + void set(uint64_t index, uint64_t value) { + if (index >= fCount) throwf("index out of range"); + P::setP(fBase[index], value); + } +}; + +// Access a section containing an array of structures +template +class ArraySection +{ + typedef typename A::P P; + + SharedCache* const fCache; + const macho_section

* const fSection; + T * const fBase; + uint64_t const fCount; + +public: + ArraySection(SharedCache* cache, const macho_header

* header, + const char *segname, const char *sectname) + : fCache(cache) + , fSection(header->getSection(segname, sectname)) + , fBase(fSection ? (T *)cache->mappedCacheAddressForAddress(fSection->addr()) : 0) + , fCount(fSection ? fSection->size() / sizeof(T) : 0) + { + } + + uint64_t count() const { return fCount; } + + T& get(uint64_t index) const { + if (index >= fCount) throwf("index out of range"); + return fBase[index]; + } }; +// GrP fixme +#include "ObjCLegacyAbstraction.hpp" +#include "ObjCModernAbstraction.hpp" + template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_POWERPC; } -template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_POWERPC64; } template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_I386; } template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_X86_64; } +template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_ARM; } template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x90000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x7FFF80000000LL; } template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x90000000; } template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x7FFF80000000LL; } +template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x30000000; } template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xA0000000; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x7FFF70000000LL; } template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xA0000000; } template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x7FFF70000000LL; } +template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x38000000; } template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x7FE00000; } 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::sharedRegionWritableSize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x20000000; } template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x10000000; } template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x20000000; } +template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x08000000; } template <> const char* SharedCache::archName() { return "ppc"; } -template <> const char* SharedCache::archName() { return "ppc64"; } template <> const char* SharedCache::archName() { return "i386"; } template <> const char* SharedCache::archName() { return "x86_64"; } +template <> const char* SharedCache::archName() { return "arm"; } -template <> const char* SharedCache::filename(bool optimized) { return optimized ? "ppc" : "rosetta"; } -template <> const char* SharedCache::filename(bool) { return "ppc64"; } -template <> const char* SharedCache::filename(bool) { return "i386"; } -template <> const char* SharedCache::filename(bool) { return "x86_64"; } +template <> const char* SharedCache::cacheFileSuffix(bool optimized, const char*) { return optimized ? "ppc" : "rosetta"; } +template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } +template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName){ return archName; } +template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } template -SharedCache::SharedCache(ArchGraph* graph, bool alphaSort, uint64_t dyldBaseAddress) - : fArchGraph(graph), fDyldBaseAddress(dyldBaseAddress) +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) { - if ( fArchGraph->getArch() != arch() ) - throw "wrong architecture"; - + 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 std::set& dylibs = fArchGraph->getSharedDylibs(); for(std::set::iterator it = dylibs.begin(); it != dylibs.end(); ++it) { @@ -506,17 +696,59 @@ SharedCache::SharedCache(ArchGraph* graph, bool alphaSort, uint64_t dyldBaseA temp.info.pathFileOffset = lib->getNameFileOffset(); fDylibs.push_back(temp); } + + // examine the existing shared cache file + 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]; + if ( overlay ) { + strcpy(cachePathNonOverlay, DYLD_SHARED_CACHE_DIR); + strcat(cachePathNonOverlay, DYLD_SHARED_CACHE_BASE_NAME); + strcat(cachePathNonOverlay, cacheFileSuffix(optimize, fArchGraph->archName())); + pathToExistingCacheFile = cachePathNonOverlay; + } + fExistingIsNotUpToDate = this->notUpToDate(pathToExistingCacheFile); // sort shared dylibs - if ( alphaSort ) + if ( verify ) { + // already sorted by notUpToDate() + } + else if ( alphaSort ) { std::sort(fDylibs.begin(), fDylibs.end(), ByNameSorter()); - else - std::sort(fDylibs.begin(), fDylibs.end(), RandomSorter(fDylibs)); - + } + else { + // random sort for Address Space Randomization + std::map map; + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) + map[it->layout] = arc4random(); + std::sort(fDylibs.begin(), fDylibs.end(), Sorter(map)); + } // assign segments in each dylib a new address this->assignNewBaseAddresses(); + // check that cache we are about to create for verification purposes has same layout as existing cache + if ( verify ) { + // if no existing cache, say so + if ( fExistingCacheForVerification == NULL ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n", + getpid(), archName()); + } + const dyldCacheHeader* header = (dyldCacheHeader*)fExistingCacheForVerification; + const dyldCacheImageInfo* cacheEntry = (dyldCacheImageInfo*)(fExistingCacheForVerification + header->imagesOffset()); + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++cacheEntry) { + if ( cacheEntry->address() != it->layout->getSegments()[0].newAddress() ) { + throwf("update_dyld_shared_cache[%u] warning: for arch=%s, could not verify cache because start address of %s is 0x%llX in cache, but should be 0x%llX\n", + getpid(), archName(), it->layout->getID().name, cacheEntry->address(), it->layout->getSegments()[0].newAddress()); + } + } + } + // calculate cache file header size fHeaderSize = pageAlign(sizeof(dyld_cache_header) + fMappings.size()*sizeof(shared_file_mapping_np) @@ -537,14 +769,7 @@ uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddres template <> uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide) { - // for ppc64 writable segments can only move in increments of 64K (so only hi16 instruction needs to be modified) - return (((executableSlide & 0x000000000000F000ULL) - ((proposedNewAddress - originalAddress) & 0x000000000000F000ULL)) & 0x000000000000F000ULL) + proposedNewAddress; -} - -template <> -uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide) -{ - // for ppc64 writable segments can only move in increments of 64K (so only hi16 instruction needs to be modified) + // for ppc writable segments can only move in increments of 64K (so only hi16 instruction needs to be modified) return (((executableSlide & 0x000000000000F000ULL) - ((proposedNewAddress - originalAddress) & 0x000000000000F000ULL)) & 0x000000000000F000ULL) + proposedNewAddress; } @@ -560,6 +785,7 @@ void SharedCache::assignNewBaseAddresses() MachOLayoutAbstraction::Segment* executableSegment = NULL; for (int i=0; i < segs.size(); ++i) { MachOLayoutAbstraction::Segment& seg = segs[i]; + seg.reset(); if ( seg.writable() ) { if ( seg.executable() && it->layout->hasSplitSegInfo() ) { // skip __IMPORT segments in this pass @@ -588,11 +814,6 @@ void SharedCache::assignNewBaseAddresses() } else { // skip read-only segments in this pass - // any non-LINKEDIT read-only segments leave a hole so that all R/W segment slide together - if ( (strcmp(seg.name(), "__LINKEDIT") != 0) && (i < (segs.size()-2)) ) { - fprintf(stderr, "update_dyld_shared_cache: warning %s segment in %s leaves a hole\n", seg.name(), it->layout->getID().name); - currentWritableAddress = pageAlign(currentWritableAddress + seg.size()); - } } } } @@ -615,7 +836,6 @@ void SharedCache::assignNewBaseAddresses() else if ( seg.writable() && seg.executable() && it->layout->hasSplitSegInfo() ) { // allocate IMPORT segments to end of writable shared region seg.setNewAddress(currentWritableExecutableAddress); - seg.setWritable(false); // __IMPORT segments are not-writable in shared cache currentWritableExecutableAddress += pageAlign(seg.size()); } } @@ -667,8 +887,7 @@ void SharedCache::assignNewBaseAddresses() writableExecutableMapping.sfm_size = currentWritableExecutableAddress - startWritableExecutableAddress; writableExecutableMapping.sfm_file_offset= cacheFileOffset; writableExecutableMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - // __IMPORT segments in shared cache are not writable - writableExecutableMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_EXECUTE; + writableExecutableMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; fMappings.push_back(writableExecutableMapping); cacheFileOffset += writableExecutableMapping.sfm_size; } @@ -708,47 +927,107 @@ uint64_t SharedCache::cacheFileOffsetForAddress(uint64_t addr) } +template +uint64_t SharedCache::mappedCacheAddressForAddress(uint64_t addr) +{ + if (!addr) return 0; + else return (uint64_t)(fInMemoryCache + cacheFileOffsetForAddress(addr)); +} + + template bool SharedCache::notUpToDate(const void* cache) { dyldCacheHeader* header = (dyldCacheHeader*)cache; // not valid if header signature is wrong + const char* archPairName = fArchGraph->archName(); char temp[16]; strcpy(temp, "dyld_v1 "); - strcpy(&temp[15-strlen(archName())], archName()); - if ( strcmp(header->magic(), temp) != 0 ) - return true; + strcpy(&temp[15-strlen(archPairName)], archPairName); + if ( strcmp(header->magic(), temp) != 0 ) { + if ( fVerify ) { + fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file has invalid header\n", getpid(), archName()); + return false; + } + else { + fprintf(stderr, "update_dyld_shared_cache[%u] 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() ) - return true; + if ( header->imagesCount() != fDylibs.size() ) { + 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()); + return true; + } + } // 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()); const dyldCacheImageInfo* imagesEnd = &imagesStart[header->imagesCount()]; for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { bool found = false; //fprintf(stderr, "inode=0x%llX, mTime=0x%llX, path=%s\n", it->info.inode, it->info.modTime, it->layout->getID().name); for(const dyldCacheImageInfo* cacheEntry = imagesStart; cacheEntry < imagesEnd; ++cacheEntry) { - if ( (cacheEntry->inode() == it->info.inode) - && (cacheEntry->modTime() == it->info.modTime) - && (strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0) ) { + if ( fVerify ) { + // in -verify mode, just match by path and warn if file looks different + if ( strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0 ) { + found = true; + sortingMap[it->layout] = cacheEntry-imagesStart; + if ( (cacheEntry->inode() != it->info.inode) || (cacheEntry->modTime() != it->info.modTime) ) { + fprintf(stderr, "update_dyld_shared_cache[%u] warning: for arch=%s, %s has changed since cache was built\n", + getpid(), archName(), it->layout->getID().name); + } + break; + } + } + else { + // in normal update mode, everything has to match for cache to be up-to-date + if ( (cacheEntry->inode() == it->info.inode) + && (cacheEntry->modTime() == it->info.modTime) + && (strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0) ) { found = true; break; + } } } if ( !found ) { - fprintf(stderr, "update_dyld_shared_cache[%u] current cache invalid because %s has changed\n", getpid(), it->layout->getID().name); - return true; + if ( fVerify ) { + 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); + return true; + } } } - return false; + // all dylibs in existing cache file match those determined need to be in shared cache + if ( fVerify ) { + // sort fDylibs to match existing cache file so we can compare content + std::sort(fDylibs.begin(), fDylibs.end(), Sorter(sortingMap)); + //fprintf(stderr, "dylibs sorted like existing cache:\n"); + //for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + // fprintf(stderr," %s\n", it->layout->getID().name); + //} + // do regenerate a new cache so we can compare content with existing + return true; + } + else { + // existing cache file is up-to-date, don't need to regenerate + return false; + } } template -bool SharedCache::notUpToDate(const char* cachePath) +bool SharedCache::notUpToDate(const char* path) { // mmap existing cache file - int fd = ::open(cachePath, O_RDONLY); + int fd = ::open(path, O_RDONLY); if ( fd == -1 ) return true; struct stat stat_buf; @@ -760,11 +1039,16 @@ bool SharedCache::notUpToDate(const char* cachePath) // validate it bool result = this->notUpToDate(mappingAddr); - // unmap - ::munmap(mappingAddr, stat_buf.st_size); - if ( verbose && !result ) - fprintf(stderr, "update_dyld_shared_cache: %s is up-to-date\n", cachePath); - + if ( fVerify ) { + // don't unmap yet, leave so it can be verified later + fExistingCacheForVerification = mappingAddr; + } + else { + // unmap + ::munmap(mappingAddr, stat_buf.st_size); + if ( verbose && !result ) + fprintf(stderr, "update_dyld_shared_cache: %s is up-to-date\n", path); + } return result; } @@ -794,7 +1078,7 @@ private: StringPool::StringPool() - : fBufferUsed(0), fBufferAllocated(4*1024*1024) + : fBufferUsed(0), fBufferAllocated(32*1024*1024) { fBuffer = (char*)malloc(fBufferAllocated); } @@ -804,8 +1088,7 @@ uint32_t StringPool::add(const char* str) uint32_t len = strlen(str); if ( (fBufferUsed + len + 1) > fBufferAllocated ) { // grow buffer - fBufferAllocated = fBufferAllocated*2; - fBuffer = (char*)realloc(fBuffer, fBufferAllocated); + throw "string buffer exhausted"; } strcpy(&fBuffer[fBufferUsed], str); uint32_t result = fBufferUsed; @@ -848,13 +1131,17 @@ public: LinkEditOptimizer(const MachOLayoutAbstraction&, uint8_t*, StringPool&); virtual ~LinkEditOptimizer() {} - static void makeDummyLocalSymbol(uint32_t&, uint8_t*, StringPool&); - void copyLocalSymbols(); - void copyExportedSymbols(uint32_t&); - void copyImportedSymbols(uint32_t&); - void copyExternalRelocations(uint32_t&); - void copyIndirectSymbolTable(uint32_t&); - void updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset); + void copyBindInfo(uint32_t&); + void copyWeakBindInfo(uint32_t&); + void copyLazyBindInfo(uint32_t&); + void copyExportInfo(uint32_t&); + void copyLocalSymbols(uint32_t symbolTableOffset, uint32_t&); + void copyExportedSymbols(uint32_t symbolTableOffset, uint32_t&); + void copyImportedSymbols(uint32_t symbolTableOffset, uint32_t&); + void copyExternalRelocations(uint32_t& offset); + void copyIndirectSymbolTable(uint32_t& offset); + void updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset, + uint32_t linkEditsFileOffset, bool keepSignatures); protected: @@ -868,12 +1155,22 @@ private: uint8_t* fNewLinkEditStart; uint8_t* fLinkEditBase; const MachOLayoutAbstraction& fLayout; + macho_dyld_info_command

* fDyldInfo; macho_dysymtab_command

* fDynamicSymbolTable; macho_symtab_command

* fSymbolTableLoadCommand; const macho_nlist

* fSymbolTable; const char* fStrings; StringPool& fNewStringPool; std::map fOldToNewSymbolIndexes; + uint32_t fBindInfoOffsetIntoNewLinkEdit; + uint32_t fBindInfoSizeInNewLinkEdit; + uint32_t fWeakBindInfoOffsetIntoNewLinkEdit; + uint32_t fWeakBindInfoSizeInNewLinkEdit; + uint32_t fLazyBindInfoOffsetIntoNewLinkEdit; + uint32_t fLazyBindInfoSizeInNewLinkEdit; + uint32_t fExportInfoOffsetIntoNewLinkEdit; + uint32_t fExportInfoSizeInNewLinkEdit; + uint32_t fSymbolTableStartOffsetInNewLinkEdit; uint32_t fLocalSymbolsStartIndexInNewLinkEdit; uint32_t fLocalSymbolsCountInNewLinkEdit; uint32_t fExportedSymbolsStartIndexInNewLinkEdit; @@ -882,16 +1179,19 @@ private: uint32_t fImportedSymbolsCountInNewLinkEdit; uint32_t fExternalRelocationsOffsetIntoNewLinkEdit; uint32_t fIndirectSymbolTableOffsetInfoNewLinkEdit; - static int32_t fgLocalSymbolsStartIndexInNewLinkEdit; }; -template int32_t LinkEditOptimizer::fgLocalSymbolsStartIndexInNewLinkEdit = 0; template LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, uint8_t* newLinkEdit, StringPool& stringPool) - : fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), + : fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL), fDynamicSymbolTable(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool), + fBindInfoOffsetIntoNewLinkEdit(0), fBindInfoSizeInNewLinkEdit(0), + fWeakBindInfoOffsetIntoNewLinkEdit(0), fWeakBindInfoSizeInNewLinkEdit(0), + fLazyBindInfoOffsetIntoNewLinkEdit(0), fLazyBindInfoSizeInNewLinkEdit(0), + fExportInfoOffsetIntoNewLinkEdit(0), fExportInfoSizeInNewLinkEdit(0), + fSymbolTableStartOffsetInNewLinkEdit(0), fLocalSymbolsStartIndexInNewLinkEdit(0), fLocalSymbolsCountInNewLinkEdit(0), fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0), fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0), @@ -924,6 +1224,10 @@ LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, ui case LC_DYSYMTAB: fDynamicSymbolTable = (macho_dysymtab_command

*)cmd; break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + fDyldInfo = (macho_dyld_info_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -951,51 +1255,97 @@ private: template -void LinkEditOptimizer::makeDummyLocalSymbol(uint32_t& symbolIndex, uint8_t* storage, StringPool& pool) +void LinkEditOptimizer::copyBindInfo(uint32_t& offset) +{ + if ( (fDyldInfo != NULL) && (fDyldInfo->bind_off() != 0) ) { + fBindInfoOffsetIntoNewLinkEdit = offset; + fBindInfoSizeInNewLinkEdit = fDyldInfo->bind_size(); + memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->bind_off()], fDyldInfo->bind_size()); + offset += fDyldInfo->bind_size(); + } +} + +template +void LinkEditOptimizer::copyWeakBindInfo(uint32_t& offset) +{ + if ( (fDyldInfo != NULL) && (fDyldInfo->weak_bind_off() != 0) ) { + fWeakBindInfoOffsetIntoNewLinkEdit = offset; + fWeakBindInfoSizeInNewLinkEdit = fDyldInfo->weak_bind_size(); + memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->weak_bind_off()], fDyldInfo->weak_bind_size()); + offset += fDyldInfo->weak_bind_size(); + } +} + +template +void LinkEditOptimizer::copyLazyBindInfo(uint32_t& offset) +{ + if ( (fDyldInfo != NULL) && (fDyldInfo->lazy_bind_off() != 0) ) { + fLazyBindInfoOffsetIntoNewLinkEdit = offset; + fLazyBindInfoSizeInNewLinkEdit = fDyldInfo->lazy_bind_size(); + memcpy(fNewLinkEditStart+offset, &fLinkEditBase[fDyldInfo->lazy_bind_off()], fDyldInfo->lazy_bind_size()); + offset += fDyldInfo->lazy_bind_size(); + } +} + +template +void LinkEditOptimizer::copyExportInfo(uint32_t& offset) { - fgLocalSymbolsStartIndexInNewLinkEdit = symbolIndex; - macho_nlist

* newSymbolEntry = (macho_nlist

*)storage; - newSymbolEntry->set_n_strx(pool.add("__no_local_symbols_in_dyld_shared_cache")); - newSymbolEntry->set_n_type(N_SECT); - newSymbolEntry->set_n_sect(1); - newSymbolEntry->set_n_desc(0); - newSymbolEntry->set_n_value(0); - ++symbolIndex; + if ( (fDyldInfo != NULL) && (fDyldInfo->export_off() != 0) ) { + 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()); + offset += fDyldInfo->export_size(); + } } + + template -void LinkEditOptimizer::copyLocalSymbols() +void LinkEditOptimizer::copyLocalSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex) { - if ( fDynamicSymbolTable->nlocalsym() > 0 ) { - // if image has any local symbols, make cache look like it has one local symbol - // which is actually shared by all images - fLocalSymbolsCountInNewLinkEdit = 1; - fLocalSymbolsStartIndexInNewLinkEdit = fgLocalSymbolsStartIndexInNewLinkEdit; + fLocalSymbolsStartIndexInNewLinkEdit = symbolIndex; + fSymbolTableStartOffsetInNewLinkEdit = symbolTableOffset + symbolIndex*sizeof(macho_nlist

); + macho_nlist

* const newSymbolTableStart = (macho_nlist

*)(fNewLinkEditStart+symbolTableOffset); + const macho_nlist

* const firstLocal = &fSymbolTable[fDynamicSymbolTable->ilocalsym()]; + const macho_nlist

* const lastLocal = &fSymbolTable[fDynamicSymbolTable->ilocalsym()+fDynamicSymbolTable->nlocalsym()]; + uint32_t oldIndex = fDynamicSymbolTable->ilocalsym(); + for (const macho_nlist

* entry = firstLocal; entry < lastLocal; ++entry, ++oldIndex) { + 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()])); + ++symbolIndex; + } } + fLocalSymbolsCountInNewLinkEdit = symbolIndex - fLocalSymbolsStartIndexInNewLinkEdit; + //fprintf(stderr, "%u locals starting at %u for %s\n", fLocalSymbolsCountInNewLinkEdit, fLocalSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath()); } template -void LinkEditOptimizer::copyExportedSymbols(uint32_t& symbolIndex) +void LinkEditOptimizer::copyExportedSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex) { fExportedSymbolsStartIndexInNewLinkEdit = symbolIndex; + macho_nlist

* const newSymbolTableStart = (macho_nlist

*)(fNewLinkEditStart+symbolTableOffset); const macho_nlist

* const firstExport = &fSymbolTable[fDynamicSymbolTable->iextdefsym()]; const macho_nlist

* const lastExport = &fSymbolTable[fDynamicSymbolTable->iextdefsym()+fDynamicSymbolTable->nextdefsym()]; uint32_t oldIndex = fDynamicSymbolTable->iextdefsym(); for (const macho_nlist

* entry = firstExport; entry < lastExport; ++entry, ++oldIndex) { - if ( ((entry->n_type() & N_TYPE) == N_SECT) && (strncmp(&fStrings[entry->n_strx()], ".objc_", 6) != 0) ) { - macho_nlist

* newSymbolEntry = &((macho_nlist

*)fNewLinkEditStart)[symbolIndex]; + if ( ((entry->n_type() & N_TYPE) == N_SECT) && (strncmp(&fStrings[entry->n_strx()], ".objc_", 6) != 0) + && (strncmp(&fStrings[entry->n_strx()], "$ld$", 4) != 0) ) { + macho_nlist

* newSymbolEntry = &newSymbolTableStart[symbolIndex]; *newSymbolEntry = *entry; newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()])); - fOldToNewSymbolIndexes[oldIndex] = symbolIndex; + fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit; ++symbolIndex; } } fExportedSymbolsCountInNewLinkEdit = symbolIndex - fExportedSymbolsStartIndexInNewLinkEdit; //fprintf(stderr, "%u exports starting at %u for %s\n", fExportedSymbolsCountInNewLinkEdit, fExportedSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath()); // sort by name, so that dyld does not need a toc - macho_nlist

* newSymbolsStart = &((macho_nlist

*)fNewLinkEditStart)[fExportedSymbolsStartIndexInNewLinkEdit]; - macho_nlist

* newSymbolsEnd = &((macho_nlist

*)fNewLinkEditStart)[fExportedSymbolsStartIndexInNewLinkEdit+fExportedSymbolsCountInNewLinkEdit]; + macho_nlist

* newSymbolsStart = &newSymbolTableStart[fExportedSymbolsStartIndexInNewLinkEdit]; + macho_nlist

* newSymbolsEnd = &newSymbolTableStart[fExportedSymbolsStartIndexInNewLinkEdit+fExportedSymbolsCountInNewLinkEdit]; std::sort(newSymbolsStart, newSymbolsEnd, SymbolSorter(fNewStringPool)); //for (macho_nlist

* entry = newSymbolsStart; entry < newSymbolsEnd; ++entry) // fprintf(stderr, "\t%u\t %s\n", (entry-newSymbolsStart)+fExportedSymbolsStartIndexInNewLinkEdit, fNewStringPool.stringAtIndex(entry->n_strx())); @@ -1003,18 +1353,19 @@ void LinkEditOptimizer::copyExportedSymbols(uint32_t& symbolIndex) template -void LinkEditOptimizer::copyImportedSymbols(uint32_t& symbolIndex) +void LinkEditOptimizer::copyImportedSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex) { fImportSymbolsStartIndexInNewLinkEdit = symbolIndex; + macho_nlist

* const newSymbolTableStart = (macho_nlist

*)(fNewLinkEditStart+symbolTableOffset); const macho_nlist

* const firstImport = &fSymbolTable[fDynamicSymbolTable->iundefsym()]; const macho_nlist

* const lastImport = &fSymbolTable[fDynamicSymbolTable->iundefsym()+fDynamicSymbolTable->nundefsym()]; uint32_t oldIndex = fDynamicSymbolTable->iundefsym(); for (const macho_nlist

* entry = firstImport; entry < lastImport; ++entry, ++oldIndex) { if ( ((entry->n_type() & N_TYPE) == N_UNDF) && (strncmp(&fStrings[entry->n_strx()], ".objc_", 6) != 0) ) { - macho_nlist

* newSymbolEntry = &((macho_nlist

*)fNewLinkEditStart)[symbolIndex]; + macho_nlist

* newSymbolEntry = &newSymbolTableStart[symbolIndex]; *newSymbolEntry = *entry; newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()])); - fOldToNewSymbolIndexes[oldIndex] = symbolIndex; + fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit; ++symbolIndex; } } @@ -1064,13 +1415,13 @@ void LinkEditOptimizer::copyIndirectSymbolTable(uint32_t& offset) } template -void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset) +void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset, + uint32_t linkEditsFileOffset, bool keepSignatures) { // set LINKEDIT segment commmand to new merged LINKEDIT 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; - uint32_t linkEditStartFileOffset = 0; for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd() == macho_segment_command

::CMD ) { macho_segment_command

* seg = (macho_segment_command

*)cmd; @@ -1078,37 +1429,94 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t si seg->set_vmaddr(newVMAddress); seg->set_vmsize(size); seg->set_filesize(size); - linkEditStartFileOffset = seg->fileoff(); + seg->set_fileoff(linkEditsFileOffset); } } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } + + // update dyld_info with new offsets + if ( fDyldInfo != NULL ) { + fDyldInfo->set_rebase_off(0); + fDyldInfo->set_rebase_size(0); + fDyldInfo->set_bind_off(linkEditsFileOffset+fBindInfoOffsetIntoNewLinkEdit); + fDyldInfo->set_bind_size(fBindInfoSizeInNewLinkEdit); + fDyldInfo->set_weak_bind_off(linkEditsFileOffset+fWeakBindInfoOffsetIntoNewLinkEdit); + fDyldInfo->set_weak_bind_size(fWeakBindInfoSizeInNewLinkEdit); + fDyldInfo->set_lazy_bind_off(linkEditsFileOffset+fLazyBindInfoOffsetIntoNewLinkEdit); + fDyldInfo->set_lazy_bind_size(fLazyBindInfoSizeInNewLinkEdit); + fDyldInfo->set_export_off(linkEditsFileOffset+fExportInfoOffsetIntoNewLinkEdit); + fDyldInfo->set_export_size(fExportInfoSizeInNewLinkEdit); +// fprintf(stderr, "dylib %s\n", fLayout.getFilePath()); +// fprintf(stderr, " bind_off=0x%08X\n", fDyldInfo->bind_off()); +// fprintf(stderr, " export_off=0x%08X\n", fDyldInfo->export_off()); +// fprintf(stderr, " export_size=%d\n", fDyldInfo->export_size()); + + } + // update symbol table and dynamic symbol table with new offsets - fSymbolTableLoadCommand->set_symoff(linkEditStartFileOffset); - fSymbolTableLoadCommand->set_nsyms(fExportedSymbolsCountInNewLinkEdit+fImportedSymbolsCountInNewLinkEdit); - fSymbolTableLoadCommand->set_stroff(linkEditStartFileOffset+stringPoolOffset); + fSymbolTableLoadCommand->set_symoff(linkEditsFileOffset+fSymbolTableStartOffsetInNewLinkEdit); + fSymbolTableLoadCommand->set_nsyms(fLocalSymbolsCountInNewLinkEdit+fExportedSymbolsCountInNewLinkEdit+fImportedSymbolsCountInNewLinkEdit); + fSymbolTableLoadCommand->set_stroff(linkEditsFileOffset+stringPoolOffset); fSymbolTableLoadCommand->set_strsize(fNewStringPool.size()); - fDynamicSymbolTable->set_ilocalsym(fLocalSymbolsStartIndexInNewLinkEdit); + fDynamicSymbolTable->set_ilocalsym(0); fDynamicSymbolTable->set_nlocalsym(fLocalSymbolsCountInNewLinkEdit); - fDynamicSymbolTable->set_iextdefsym(fExportedSymbolsStartIndexInNewLinkEdit); + fDynamicSymbolTable->set_iextdefsym(fExportedSymbolsStartIndexInNewLinkEdit-fLocalSymbolsStartIndexInNewLinkEdit); fDynamicSymbolTable->set_nextdefsym(fExportedSymbolsCountInNewLinkEdit); - fDynamicSymbolTable->set_iundefsym(fImportSymbolsStartIndexInNewLinkEdit); + fDynamicSymbolTable->set_iundefsym(fImportSymbolsStartIndexInNewLinkEdit-fLocalSymbolsStartIndexInNewLinkEdit); fDynamicSymbolTable->set_nundefsym(fImportedSymbolsCountInNewLinkEdit); fDynamicSymbolTable->set_tocoff(0); fDynamicSymbolTable->set_ntoc(0); fDynamicSymbolTable->set_modtaboff(0); fDynamicSymbolTable->set_nmodtab(0); - fDynamicSymbolTable->set_indirectsymoff(linkEditStartFileOffset+fIndirectSymbolTableOffsetInfoNewLinkEdit); - fDynamicSymbolTable->set_extreloff(linkEditStartFileOffset+fExternalRelocationsOffsetIntoNewLinkEdit); + fDynamicSymbolTable->set_indirectsymoff(linkEditsFileOffset+fIndirectSymbolTableOffsetInfoNewLinkEdit); + fDynamicSymbolTable->set_extreloff(linkEditsFileOffset+fExternalRelocationsOffsetIntoNewLinkEdit); fDynamicSymbolTable->set_locreloff(0); fDynamicSymbolTable->set_nlocrel(0); + + // now remove load commands no longer needed + const macho_load_command

* srcCmd = cmds; + macho_load_command

* dstCmd = (macho_load_command

*)cmds; + int32_t newCount = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdSize = srcCmd->cmdsize(); + switch ( srcCmd->cmd() ) { + case LC_SEGMENT_SPLIT_INFO: + // don't copy + break; + case LC_CODE_SIGNATURE: + if ( !keepSignatures ) + break; + // otherwise fall into copy case + default: + memmove(dstCmd, srcCmd, cmdSize); + dstCmd = (macho_load_command

*)(((uint8_t*)dstCmd)+cmdSize); + ++newCount; + break; + } + srcCmd = (const macho_load_command

*)(((uint8_t*)srcCmd)+cmdSize); + } + // zero out stuff removed + bzero(dstCmd, (uint8_t*)srcCmd - (uint8_t*)dstCmd); + + // update mach_header + macho_header

* writableHeader = (macho_header

*)fHeader; + writableHeader->set_ncmds(newCount); + writableHeader->set_sizeofcmds((uint8_t*)dstCmd - ((uint8_t*)fHeader + sizeof(macho_header

))); + + // this invalidates some ivars + fDynamicSymbolTable = NULL; + fSymbolTableLoadCommand = NULL; + fDyldInfo = NULL; + fSymbolTable = NULL; + fStrings = NULL; } template -uint8_t* SharedCache::optimizeLINKEDIT() +uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) { // allocate space for optimized LINKEDIT area uint8_t* newLinkEdit = new uint8_t[fLinkEditsTotalUnoptimizedSize]; @@ -1123,34 +1531,49 @@ uint8_t* SharedCache::optimizeLINKEDIT() optimizers.push_back(new LinkEditOptimizer(*it->layout, newLinkEdit, stringPool)); } - // copy local symbol table entries - uint32_t symbolTableIndex = 0; - LinkEditOptimizer::makeDummyLocalSymbol(symbolTableIndex, newLinkEdit, stringPool); + // rebase info is not copied because images in shared cache are never rebased + + // copy weak bind info + uint32_t offset = 0; + fOffsetOfWeakBindInfoInCombinedLinkedit = offset; + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyWeakBindInfo(offset); + } + + // copy export info + fOffsetOfExportInfoInCombinedLinkedit = offset; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyLocalSymbols(); + (*it)->copyExportInfo(offset); } - // copy exported symbol table entries + // copy bind info + fOffsetOfBindInfoInCombinedLinkedit = offset; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyExportedSymbols(symbolTableIndex); + (*it)->copyBindInfo(offset); } - //fprintf(stderr, "%u exported symbols, with %d bytes of strings\n", symbolTableIndex, stringPool.size()); - //uint32_t importStart = symbolTableIndex; - //uint32_t importPoolStart = stringPool.size(); - // copy imported symbol table entries + // copy lazy bind info + fOffsetOfLazyBindInfoInCombinedLinkedit = offset; + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyLazyBindInfo(offset); + } + + // copy symbol table entries + fOffsetOfOldSymbolTableInfoInCombinedLinkedit = offset; + uint32_t symbolTableOffset = offset; + uint32_t symbolTableIndex = 0; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyImportedSymbols(symbolTableIndex); + (*it)->copyLocalSymbols(symbolTableOffset, symbolTableIndex); + (*it)->copyExportedSymbols(symbolTableOffset, symbolTableIndex); + (*it)->copyImportedSymbols(symbolTableOffset, symbolTableIndex); } - //fprintf(stderr, "%u imported symbols, with %d bytes of strings\n", symbolTableIndex-importStart, stringPool.size()-importPoolStart); // copy external relocations, 8-byte aligned after end of symbol table - uint32_t externalRelocsOffset = (symbolTableIndex * sizeof(macho_nlist) + 7) & (-8); + uint32_t externalRelocsOffset = symbolTableOffset + (symbolTableIndex * sizeof(macho_nlist) + 7) & (-8); //uint32_t externalRelocsStartOffset = externalRelocsOffset; for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { (*it)->copyExternalRelocations(externalRelocsOffset); } - //fprintf(stderr, "%u bytes of external relocs\n", externalRelocsOffset-externalRelocsStartOffset); // copy indirect symbol tables uint32_t indirectSymbolTableOffset = externalRelocsOffset; @@ -1163,15 +1586,18 @@ uint8_t* SharedCache::optimizeLINKEDIT() memcpy(&newLinkEdit[stringPoolOffset], stringPool.getBuffer(), stringPool.size()); // find new size - uint32_t linkEditsTotalOptimizedSize = (stringPoolOffset + stringPool.size() + 4095) & (-4096); + fLinkEditsTotalOptimizedSize = (stringPoolOffset + stringPool.size() + 4095) & (-4096); + + // choose new linkedit file offset + 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); + (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, stringPoolOffset, linkEditsFileOffset, keepSignatures); } - //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, linkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, linkEditsTotalOptimizedSize); - //fprintf(stderr, "mega link edit mapped starting at: %p\n", fFirstLinkEditSegment->mappedAddress()); + //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, fLinkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, fLinkEditsTotalOptimizedSize); + //printf(stderr, "mega link edit mapped starting at: %p\n", fFirstLinkEditSegment->mappedAddress()); // overwrite mapped LINKEDIT area with new optimized LINKEDIT segment memcpy(fFirstLinkEditSegment->mappedAddress(), newLinkEdit, fLinkEditsTotalUnoptimizedSize); @@ -1184,15 +1610,131 @@ uint8_t* SharedCache::optimizeLINKEDIT() if ( !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") == 0) ) { seg.setNewAddress(fLinkEditsStartAddress); seg.setMappedAddress(fFirstLinkEditSegment->mappedAddress()); - seg.setSize(linkEditsTotalOptimizedSize); - seg.setFileSize(linkEditsTotalOptimizedSize); - //seg.setFileOffset(0); + seg.setSize(fLinkEditsTotalOptimizedSize); + seg.setFileSize(fLinkEditsTotalOptimizedSize); + seg.setFileOffset(linkEditsFileOffset); } } } // return new end of cache - return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + linkEditsTotalOptimizedSize; + return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + fLinkEditsTotalOptimizedSize; +} + + + +template +class ObjCSelectorUniquer +{ +private: + objc_selopt::string_map fSelectorStrings; + SharedCache *fCache; + size_t fCount; + +public: + + ObjCSelectorUniquer(SharedCache *newCache) + : fSelectorStrings() + , fCache(newCache) + , fCount(0) + { } + + typename A::P::uint_t visit(typename A::P::uint_t oldValue) + { + fCount++; + const char *s = (const char *) + fCache->mappedCacheAddressForAddress(oldValue); + objc_selopt::string_map::iterator element = + fSelectorStrings.insert(objc_selopt::string_map::value_type(s, oldValue)).first; + return (typename A::P::uint_t)element->second; + } + + objc_selopt::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() +{ + if ( verbose ) { + fprintf(stderr, "update_dyld_shared_cache: for %s, uniquing objc selectors\n", archName()); + } + + // Find libobjc's __TEXT,__objc_selopt section + const macho_section

*seloptSection = 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 (!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; + } + + + // Update selector references and build selector list + ObjCSelectorUniquer uniq(this); + + // Heuristic: choose selectors from libraries with more cstring data first. + // This tries to localize selector cstring memory. + std::vector sortedDylibs = fDylibs; + std::sort(sortedDylibs.begin(), sortedDylibs.end(), ByCStringSectionSizeSorter()); + + 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); + } + + 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); + if (err) { + warn(archName(), err); + return; + } + + if ( verbose ) { + 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)); + fprintf(stderr, "update_dyld_shared_cache: for %s, " + "updated %zu selector references\n", + archName(), uniq.count()); + } + + return; } @@ -1210,19 +1752,15 @@ static void cleanup(int sig) template -bool SharedCache::update(const char* rootPath, const char* cacheDir, bool force, bool optimize, bool deleteExistingFirst, int archIndex, int archCount) +bool SharedCache::update(bool usesOverlay, bool force, bool optimize, bool deleteExistingFirst, int archIndex, + int archCount, bool keepSignatures) { bool didUpdate = false; - char cachePath[1024]; - strcpy(cachePath, rootPath); - strcat(cachePath, cacheDir); - strcat(cachePath, DYLD_SHARED_CACHE_BASE_NAME); - strcat(cachePath, filename(optimize)); // already up to date? - if ( force || this->notUpToDate(cachePath) ) { + if ( force || fExistingIsNotUpToDate ) { if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: regenerating %s\n", cachePath); + fprintf(stderr, "update_dyld_shared_cache: regenerating %s\n", fCacheFilePath); if ( fDylibs.size() == 0 ) { fprintf(stderr, "update_dyld_shared_cache: warning, empty cache not generated for arch %s\n", archName()); return false; @@ -1230,11 +1768,11 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for // delete existing cache while building the new one // this is a flag to dyld to stop pinging update_dyld_shared_cache if ( deleteExistingFirst ) - ::unlink(cachePath); + ::unlink(fCacheFilePath); uint8_t* inMemoryCache = NULL; uint32_t allocatedCacheSize = 0; - char tempCachePath[strlen(cachePath)+16]; - sprintf(tempCachePath, "%s.tmp%u", cachePath, getpid()); + char tempCachePath[strlen(fCacheFilePath)+16]; + sprintf(tempCachePath, "%s.tmp%u", fCacheFilePath, getpid()); try { // allocate a memory block to hold cache uint32_t cacheFileSize = 0; @@ -1246,12 +1784,14 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for if ( vm_allocate(mach_task_self(), (vm_address_t*)(&inMemoryCache), cacheFileSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS ) throwf("can't vm_allocate cache of size %u", cacheFileSize); allocatedCacheSize = cacheFileSize; + fInMemoryCache = inMemoryCache; // fill in header dyldCacheHeader* header = (dyldCacheHeader*)inMemoryCache; + const char* archPairName = fArchGraph->archName(); char temp[16]; strcpy(temp, "dyld_v1 "); - strcpy(&temp[15-strlen(archName())], archName()); + strcpy(&temp[15-strlen(archPairName)], archPairName); header->set_magic(temp); //header->set_architecture(arch()); header->set_mappingOffset(sizeof(dyldCacheHeader)); @@ -1288,10 +1828,12 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for } // copy each segment to cache buffer + const int dylibCount = fDylibs.size(); int dylibIndex = 0; + int progressIndex = 0; for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) { const char* path = it->layout->getFilePath(); - int src = ::open(path, O_RDONLY, 0); + int src = ::open(path, O_RDONLY, 0); if ( src == -1 ) throwf("can't open file %s, errnor=%d", it->layout->getID().name, errno); // mark source as "don't cache" @@ -1301,7 +1843,7 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for if ( fstat(src, &stat_buf) == -1) throwf("can't stat open file %s, errno=%d", path, errno); if ( (it->layout->getInode() != stat_buf.st_ino) || (it->layout->getLastModTime() != stat_buf.st_mtime) ) - throwf("aborting because OS dylib modified during cache creation: %s", path); + throwf("file modified during cache creation: %s", path); if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: copying %s to cache\n", it->layout->getID().name); @@ -1316,31 +1858,12 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for const uint64_t segmentSize = seg.fileSize(); const uint64_t segmentDstStartOffset = cacheFileOffsetForAddress(seg.newAddress()); ssize_t readResult = ::pread(src, &inMemoryCache[segmentDstStartOffset], segmentSize, segmentSrcStartOffset); - if ( readResult != segmentSize ) + if ( readResult != segmentSize ) { if ( readResult == -1 ) throwf("read failure copying dylib errno=%d for %s", errno, it->layout->getID().name); else throwf("read failure copying dylib. Read of %lld bytes at file offset %lld returned %ld for %s", segmentSize, segmentSrcStartOffset, readResult, it->layout->getID().name); - // verify __TEXT segment has no zeroed out pages - if ( strcmp(seg.name(), "__TEXT") == 0 ) { - // only scan first 128KB. Some OS dylibs have zero filled TEXT pages later in __const... - int scanEnd = segmentSize; - if ( scanEnd > 0x20000 ) - scanEnd = 0x20000; - for (int pageOffset = 0; pageOffset < scanEnd; pageOffset += 4096) { - const uint32_t* page = (uint32_t*)(&inMemoryCache[segmentDstStartOffset+pageOffset]); - bool foundNonZero = false; - for(int p=0; p < 1024; ++p) { - if ( page[p] != 0 ) { - //fprintf(stderr, "found non-zero at pageOffset=0x%08X, p=0x%08X in memory=%p for %s\n", pageOffset, p, page, it->layout->getID().name); - foundNonZero = true; - break; - } - } - if ( !foundNonZero ) - throwf("suspected bad read. Found __TEXT segment page at offset 0x%08X that is all zeros for %s in %s", pageOffset, archName(), it->layout->getID().name); - } } } } @@ -1349,6 +1872,13 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for throwf("%s while copying %s to shared cache", msg, it->layout->getID().name); } ::close(src); + if ( progress ) { + // assuming read takes 40% of time + int nextProgressIndex = archIndex*100+(40*dylibIndex)/dylibCount; + if ( nextProgressIndex != progressIndex ) + fprintf(stdout, "%3u/%u\n", nextProgressIndex, archCount*100); + progressIndex = nextProgressIndex; + } } // set mapped address for each segment @@ -1378,7 +1908,7 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for // 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() - inMemoryCache); + 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; @@ -1402,6 +1932,7 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for if ( it->layout->getID().name != NULL ) map[it->layout->getID().name] = binder; } + // tell each Binder about the others for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { (*it)->setDependentBinders(map); @@ -1422,93 +1953,212 @@ bool SharedCache::update(const char* rootPath, const char* cacheDir, bool for delete *it; } - // install signal handlers to delete temp file if program is killed - sCleanupFile = tempCachePath; - ::signal(SIGINT, cleanup); - ::signal(SIGBUS, cleanup); - ::signal(SIGSEGV, cleanup); - - // create temp file for cache - int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644); - if ( fd == -1 ) - throwf("can't create temp file %s, errnor=%d", tempCachePath, errno); - - // try to allocate whole cache file contiguously - fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 }; - ::fcntl(fd, F_PREALLOCATE, &fcntlSpec); + // 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); + } - // write out cache file - if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: writing cache to disk\n"); - if ( ::pwrite(fd, inMemoryCache, cacheFileSize, 0) != cacheFileSize ) - throwf("write() failure creating cache file, errno=%d", errno); - - // flush to disk and close - int result = ::fcntl(fd, F_FULLFSYNC, NULL); - if ( result == -1 ) - fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath); - result = ::close(fd); - if ( result != 0 ) - fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath); - - // atomically swap in new cache file, do this after F_FULLFSYNC - result = ::rename(tempCachePath, cachePath); - if ( result != 0 ) - throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, cachePath, errno); + if ( fVerify ) { + // new cache is built, compare header entries + const dyldCacheHeader* newHeader = (dyldCacheHeader*)inMemoryCache; + const dyldCacheHeader* oldHeader = (dyldCacheHeader*)fExistingCacheForVerification; + if ( newHeader->mappingCount() != oldHeader->mappingCount() ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because caches have a different number of mappings\n", + getpid(), archName()); + } + const dyldCacheFileMapping* newMappings = (dyldCacheFileMapping*)&inMemoryCache[newHeader->mappingOffset()]; + const dyldCacheFileMapping* oldMappings = (dyldCacheFileMapping*)&fExistingCacheForVerification[oldHeader->mappingOffset()]; + for (int i=0; i < newHeader->mappingCount(); ++i) { + if ( newMappings[i].address() != oldMappings[i].address() ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because mapping %d starts at a different address 0x%0llX vs 0x%0llX\n", + getpid(), archName(), i, newMappings[i].address(), oldMappings[i].address() ); + } + if ( newMappings[i].size() != oldMappings[i].size() ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify cache because mapping %d has a different size\n", + getpid(), archName(), i); + } + } - // flush everything to disk to assure rename() gets recorded - ::sync(); - didUpdate = true; - - // restore default signal handlers - ::signal(SIGINT, SIG_DFL); - ::signal(SIGBUS, SIG_DFL); - ::signal(SIGSEGV, SIG_DFL); - - // generate human readable "map" file that shows the layout of the cache file - if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: writing .map file to disk\n"); - sprintf(tempCachePath, "%s.map", cachePath);// re-use path buffer - FILE* fmap = ::fopen(tempCachePath, "w"); - if ( fmap == NULL ) { - fprintf(stderr, "can't create map file %s, errnor=%d", tempCachePath, errno); + //fprintf(stderr, "%s existing cache = %p\n", archName(), fExistingCacheForVerification); + //fprintf(stderr, "%s new cache = %p\n", archName(), inMemoryCache); + // compare content to existing cache page by page + for (int offset=0; offset < cacheFileSize; offset += 4096) { + if ( memcmp(&inMemoryCache[offset], &fExistingCacheForVerification[offset], 4096) != 0 ) { + fprintf(stderr, "verifier found differences on page offset 0x%08X for %s:\n", offset, archName()); + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) { + const std::vector& segs = it->layout->getSegments(); + for(std::vector::const_iterator sit = segs.begin(); sit != segs.end(); ++sit) { + const MachOLayoutAbstraction::Segment& seg = *sit; + if ( (seg.mappedAddress() <= &inMemoryCache[offset]) && (&inMemoryCache[offset] < ((uint8_t*)seg.mappedAddress() + seg.fileSize())) ) { + // all LINKEDITs point to the same region, so just print one + if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) + fprintf(stderr, " in merged LINKEDIT segment\n"); + else + fprintf(stderr, " in segment %s of dylib %s\n", seg.name(), it->layout->getID().name); + break; + } + } + } + for (int po=0; po < 4096; po += 16) { + if ( memcmp(&inMemoryCache[offset+po], &fExistingCacheForVerification[offset+po], 16) != 0 ) { + fprintf(stderr, " existing: 0x%08X: ", offset+po); + for ( int j=0; j < 16; ++j) + fprintf(stderr, " 0x%02X", fExistingCacheForVerification[offset+po+j]); + fprintf(stderr, "\n"); + fprintf(stderr, " should be: 0x%08X: ", offset+po); + for ( int j=0; j < 16; ++j) + fprintf(stderr, " 0x%02X", inMemoryCache[offset+po+j]); + fprintf(stderr, "\n"); + } + } + } + } } else { - for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { - const char* prot = "RW"; - if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_READ) ) - prot = "EX"; - else if ( it->sfm_init_prot == VM_PROT_READ ) - prot = "RO"; - else if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_WRITE|VM_PROT_READ) ) - prot = "WX"; - if ( it->sfm_size > 1024*1024 ) - fprintf(fmap, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/(1024*1024), - it->sfm_address, it->sfm_address+it->sfm_size); - else - fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/1024, - it->sfm_address, it->sfm_address+it->sfm_size); - } - for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { - fprintf(fmap, "%s\n", it->layout->getID().name); - const std::vector& segs = it->layout->getSegments(); - for (int i=0; i < segs.size(); ++i) { - const MachOLayoutAbstraction::Segment& seg = segs[i]; - fprintf(fmap, "\t%16s 0x%0llX -> 0x%0llX\n", seg.name(), seg.newAddress(), seg.newAddress()+seg.size()); + // install signal handlers to delete temp file if program is killed + sCleanupFile = tempCachePath; + ::signal(SIGINT, cleanup); + ::signal(SIGBUS, cleanup); + ::signal(SIGSEGV, cleanup); + + // create var/db/dyld dirs if needed + char dyldDirs[1024]; + strcpy(dyldDirs, fCacheFilePath); + char* lastSlash = strrchr(dyldDirs, '/'); + if ( lastSlash != NULL ) + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(dyldDirs, &stat_buf) != 0 ) { + const char* afterSlash = &dyldDirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(dyldDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + *slash = '/'; + afterSlash = slash+1; } } - if ( warnings.size() > 0 ) { - fprintf(fmap, "# Warnings:\n"); - for (std::vector::iterator it=warnings.begin(); it != warnings.end(); ++it) { - fprintf(fmap, "# %s\n", *it); + + // create temp file for cache + int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644); + if ( fd == -1 ) + throwf("can't create temp file %s, errnor=%d", tempCachePath, errno); + + // try to allocate whole cache file contiguously + fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 }; + ::fcntl(fd, F_PREALLOCATE, &fcntlSpec); + + // write out cache file + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: writing cache to disk\n"); + if ( ::pwrite(fd, inMemoryCache, cacheFileSize, 0) != cacheFileSize ) + throwf("write() failure creating cache file, errno=%d", errno); + if ( progress ) { + // assuming write takes 35% of time + fprintf(stdout, "%3u/%u\n", (archIndex+1)*90, archCount*100); + } + + // flush to disk and close + int result = ::fcntl(fd, F_FULLFSYNC, NULL); + if ( result == -1 ) + fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath); + result = ::close(fd); + if ( result != 0 ) + fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath); + + // atomically swap in new cache file, do this after F_FULLFSYNC + 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; + + // restore default signal handlers + ::signal(SIGINT, SIG_DFL); + ::signal(SIGBUS, SIG_DFL); + ::signal(SIGSEGV, SIG_DFL); + + // generate human readable "map" file that shows the layout of the cache file + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: writing .map file to disk\n"); + char mapFilePath[strlen(fCacheFilePath)+16]; + sprintf(mapFilePath, "%s.map", fCacheFilePath); + char tempMapFilePath[strlen(fCacheFilePath)+32]; + sprintf(tempMapFilePath, "%s.map%u", fCacheFilePath, getpid()); + FILE* fmap = ::fopen(tempMapFilePath, "w"); + if ( fmap == NULL ) { + fprintf(stderr, "can't create map file %s, errnor=%d", tempCachePath, errno); + } + else { + for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + const char* prot = "RW"; + if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_READ) ) + prot = "EX"; + else if ( it->sfm_init_prot == VM_PROT_READ ) + prot = "RO"; + else if ( it->sfm_init_prot == (VM_PROT_EXECUTE|VM_PROT_WRITE|VM_PROT_READ) ) + prot = "WX"; + if ( it->sfm_size > 1024*1024 ) + fprintf(fmap, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/(1024*1024), + it->sfm_address, it->sfm_address+it->sfm_size); + else + fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/1024, + it->sfm_address, it->sfm_address+it->sfm_size); + } + + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX weak binding info\n", + (fOffsetOfExportInfoInCombinedLinkedit-fOffsetOfWeakBindInfoInCombinedLinkedit)/1024, + fLinkEditsStartAddress+fOffsetOfWeakBindInfoInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit); + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX export info\n", + (fOffsetOfBindInfoInCombinedLinkedit-fOffsetOfExportInfoInCombinedLinkedit)/1024, + fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit); + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX binding info\n", + (fOffsetOfLazyBindInfoInCombinedLinkedit-fOffsetOfBindInfoInCombinedLinkedit)/1024, + fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit); + fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX lazy binding info\n", + (fOffsetOfOldSymbolTableInfoInCombinedLinkedit-fOffsetOfLazyBindInfoInCombinedLinkedit)/1024, + fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit, + fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit); + fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table info\n", + (fLinkEditsTotalOptimizedSize-fOffsetOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024), + fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit, + fLinkEditsStartAddress+fLinkEditsTotalOptimizedSize); + + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + fprintf(fmap, "%s\n", it->layout->getID().name); + const std::vector& segs = it->layout->getSegments(); + for (int i=0; i < segs.size(); ++i) { + const MachOLayoutAbstraction::Segment& seg = segs[i]; + fprintf(fmap, "\t%16s 0x%0llX -> 0x%0llX\n", seg.name(), seg.newAddress(), seg.newAddress()+seg.size()); + } + } + if ( warnings.size() > 0 ) { + fprintf(fmap, "# Warnings:\n"); + for (std::vector::iterator it=warnings.begin(); it != warnings.end(); ++it) { + fprintf(fmap, "# %s\n", *it); + } } + fclose(fmap); + result = ::rename(tempMapFilePath, mapFilePath); } - fclose(fmap); } // free in memory cache vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize); inMemoryCache = NULL; + if ( progress ) { + // finished + fprintf(stdout, "%3u/%u\n", (archIndex+1)*100, archCount*100); + } } catch (...){ // remove temp cache file @@ -1592,10 +2242,32 @@ static void parsePathsFile(const char* filePath, std::vector& paths } -static void scanForSharedDylibs(const char* rootPath, const char* dirOfPathFiles, const std::set& onlyArchs) + +static void setSharedDylibs(const char* rootPath, bool usesOverlay, const std::set& onlyArchs, std::vector rootsPaths) +{ + // set file system root + ArchGraph::setFileSystemRoot(rootPath, usesOverlay); + + // initialize all architectures requested + for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a) + ArchGraph::addArchPair(*a); + + // add roots to graph + for(std::vector::const_iterator it = rootsPaths.begin(); it != rootsPaths.end(); ++it) + ArchGraph::addRoot(*it, onlyArchs); + + // determine shared dylibs + for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a) + ArchGraph::findSharedDylibs(*a); +} + + +static void scanForSharedDylibs(const char* rootPath, bool usesOverlay, const char* dirOfPathFiles, const std::set& onlyArchs) { char rootDirOfPathFiles[strlen(rootPath)+strlen(dirOfPathFiles)+2]; - if ( strlen(rootPath) != 0 ) { + // in -overlay mode, still look for roots in /var/db/dyld + // in -root mode, look for roots in /rootpath/var/db/dyld + if ( !usesOverlay && (strlen(rootPath) != 0) ) { strcpy(rootDirOfPathFiles, rootPath); strcat(rootDirOfPathFiles, dirOfPathFiles); dirOfPathFiles = rootDirOfPathFiles; @@ -1624,24 +2296,17 @@ static void scanForSharedDylibs(const char* rootPath, const char* dirOfPathFiles } } ::closedir(dir); - - // set file system root - ArchGraph::setFileSystemRoot(rootPath); - - // initialize all architectures requested - for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a) - ArchGraph::addArch(*a); - - // add roots to graph - for(std::vector::iterator it = rootsPaths.begin(); it != rootsPaths.end(); ++it) - ArchGraph::addRoot(*it, onlyArchs); - - // determine shared dylibs - for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a) - ArchGraph::findSharedDylibs(*a); if ( rootsPaths.size() == 0 ) fprintf(stderr, "update_dyld_shared_cache: warning, no entries found in shared_region_roots\n"); + setSharedDylibs(rootPath, usesOverlay, onlyArchs, rootsPaths); +} + +static void setSharedDylibs(const char* rootPath, bool usesOverlay, const char* pathsFile, const std::set& onlyArchs) +{ + std::vector rootsPaths; + parsePathsFile(pathsFile, rootsPaths); + setSharedDylibs(rootPath, usesOverlay, onlyArchs, rootsPaths); } @@ -1688,48 +2353,60 @@ static void deleteOrphanTempCacheFiles() -static bool updateSharedeCacheFile(const char* rootPath, const char* cacheDir, const std::set& onlyArchs, - bool force, bool alphaSort, bool optimize, bool deleteExistingFirst) +static bool updateSharedeCacheFile(const char* rootPath, bool usesOverlay, const char* cacheDir, const std::set& onlyArchs, + bool force, bool alphaSort, bool optimize, bool deleteExistingFirst, bool verify, bool keepSignatures) { bool didUpdate = false; // get dyld load address info - UniversalMachOLayout* dyldLayout = new UniversalMachOLayout("/usr/lib/dyld", &onlyArchs); - + UniversalMachOLayout* dyldLayout = NULL; + char dyldPath[1024]; + strlcpy(dyldPath, rootPath, 1024); + strlcat(dyldPath, "/usr/lib/dyld", 1024); + struct stat stat_buf; + if ( stat(dyldPath, &stat_buf) == 0 ) { + dyldLayout = new UniversalMachOLayout(dyldPath, &onlyArchs); + } + else { + dyldLayout = new UniversalMachOLayout("/usr/lib/dyld", &onlyArchs); + } const int archCount = onlyArchs.size(); int index = 0; - for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a, ++index) { - const MachOLayoutAbstraction* dyldLayoutForArch = dyldLayout->getArch(*a); - if ( dyldLayoutForArch == NULL ) - throw "dyld not avaiable for specified architecture"; - uint64_t dyldBaseAddress = dyldLayoutForArch->getBaseAddress(); - switch ( *a ) { + for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a, ++index) { + const MachOLayoutAbstraction* dyldLayoutForArch = dyldLayout->getSlice(*a); + uint64_t dyldBaseAddress = 0; + if ( dyldLayoutForArch != NULL ) + dyldBaseAddress = dyldLayoutForArch->getBaseAddress(); + else + fprintf(stderr, "update_dyld_shared_cache: warning, dyld not available for specified architectures\n"); + switch ( a->arch ) { case CPU_TYPE_POWERPC: { - SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); - #if __i386__ + #if __i386__ || __x86_64__ // Rosetta does not work with optimized dyld shared cache - didUpdate |= cache.update(rootPath, cacheDir, force, false, deleteExistingFirst, index, archCount); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, false, usesOverlay, dyldBaseAddress); + didUpdate |= cache.update(usesOverlay, force, false, deleteExistingFirst, index, archCount, keepSignatures); #else - didUpdate |= cache.update(rootPath, cacheDir, force, optimize, deleteExistingFirst, index, archCount); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); #endif } break; - case CPU_TYPE_POWERPC64: + case CPU_TYPE_I386: { - SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); - didUpdate |= cache.update(rootPath, cacheDir, force, optimize, deleteExistingFirst, index, archCount); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; - case CPU_TYPE_I386: + case CPU_TYPE_X86_64: { - SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); - didUpdate |= cache.update(rootPath, cacheDir, force, optimize, deleteExistingFirst, index, archCount); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; - case CPU_TYPE_X86_64: + case CPU_TYPE_ARM: { - SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); - didUpdate |= cache.update(rootPath, cacheDir, force, optimize, deleteExistingFirst, index, archCount); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress); + didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures); } break; } @@ -1743,192 +2420,163 @@ static bool updateSharedeCacheFile(const char* rootPath, const char* cacheDir, c static void usage() { - fprintf(stderr, "update_dyld_shared_cache [-force] [-root dir] [-arch arch] [-debug]\n"); -} - -// flag so that we only update cache once per invocation -static bool doNothingAndDrainQueue = false; - -static kern_return_t do_update_cache(cpu_type_t arch, bool deleteExistingCacheFileFirst) -{ - if ( !doNothingAndDrainQueue ) { - std::set onlyArchs; - onlyArchs.insert(arch); - try { - scanForSharedDylibs("", "/var/db/dyld/shared_region_roots/", onlyArchs); - if ( updateSharedeCacheFile("", DYLD_SHARED_CACHE_DIR, onlyArchs, false, false, true, deleteExistingCacheFileFirst) ) - fprintf(stderr, "update_dyld_shared_cache[%u] regenerated cache for arch=%s\n", getpid(), ArchGraph::archName(arch)); - } - catch (const char* msg) { - fprintf(stderr, "update_dyld_shared_cache[%u] for arch=%s failed: %s\n", getpid(), ArchGraph::archName(arch), msg); - return KERN_FAILURE; - } - // only build one cache file per life of process - doNothingAndDrainQueue = true; - } - return KERN_SUCCESS; -} - - - -kern_return_t do_dyld_shared_cache_missing(mach_port_t dyld_port, cpu_type_t arch) -{ - return do_update_cache(arch, false); -} - - -kern_return_t do_dyld_shared_cache_out_of_date(mach_port_t dyld_port, cpu_type_t arch) -{ - // If cache exists but is out of date, delete the file while building the new one. - // This will stop dyld from pinging update_dyld_share_cache while the cache is being built. - return do_update_cache(arch, true); + fprintf(stderr, "update_dyld_shared_cache [-force] [-root dir] [-overlay dir] [-arch arch] [-debug]\n"); } int main(int argc, const char* argv[]) { - mach_port_t mp; - if ( bootstrap_check_in(bootstrap_port, "com.apple.dyld", &mp) == KERN_SUCCESS ) { - // started by launchd - mach_msg_size_t mxmsgsz = sizeof(union __RequestUnion__do_dyld_server_subsystem) + MAX_TRAILER_SIZE; - doNothingAndDrainQueue = false; - while ( mach_msg_server(dyld_server_server, mxmsgsz, mp, MACH_RCV_TIMEOUT) == KERN_SUCCESS ) { - // keep processing messages - doNothingAndDrainQueue = true; - // but set flag so work is no longer done. - // This is because the rest of the tool leaks and processing more than once - // can hog system resources: 9A516 - Keep getting disk full errors - // We drain the queue of messages because there is usually are a couple of duplicate messages. - // It is ok to miss some messages. If the cache is out of date or missing, some new process - // will discover it and send another message. - } - return 0; - } - else { - // started as command line tool - std::set onlyArchs; - const char* rootPath = ""; - bool force = false; - bool alphaSort = false; - bool optimize = true; - bool makeSymLink = false; + std::set onlyArchs; + const char* rootPath = ""; + const char* dylibListFile = NULL; + bool force = false; + bool alphaSort = false; + bool optimize = true; + bool hasRoot = false; + bool hasOverlay = false; + bool verify = false; + bool keepSignatures = false; - try { - // parse command line options - for(int i=1; i < argc; ++i) { - const char* arg = argv[i]; - if ( arg[0] == '-' ) { - if ( strcmp(arg, "-debug") == 0 ) { - verbose = true; - } - else if ( strcmp(arg, "-force") == 0 ) { - force = true; - } - else if ( strcmp(arg, "-sort_by_name") == 0 ) { - alphaSort = true; - } - else if ( strcmp(arg, "-opt") == 0 ) { - optimize = true; - } - else if ( strcmp(arg, "-no_opt") == 0 ) { - optimize = false; - } - else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) { - rootPath = argv[++i]; - if ( rootPath == NULL ) - throw "-root missing path argument"; - // strip tailing slashes - int len = strlen(rootPath)-1; - if ( rootPath[len] == '/' ) { - char* newRootPath = strdup(rootPath); - while ( newRootPath[len] == '/' ) - newRootPath[len--] = '\0'; - rootPath = newRootPath; - } - } - else if ( strcmp(arg, "-arch") == 0 ) { - const char* arch = argv[++i]; - if ( strcmp(arch, "ppc") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC); - else if ( strcmp(arch, "ppc64") == 0 ) - onlyArchs.insert(CPU_TYPE_POWERPC64); - else if ( strcmp(arch, "i386") == 0 ) - onlyArchs.insert(CPU_TYPE_I386); - else if ( strcmp(arch, "x86_64") == 0 ) - onlyArchs.insert(CPU_TYPE_X86_64); - else - throwf("unknown architecture %s", arch); - } - else if ( strcmp(arg, "-universal_boot") == 0 ) { - #if __ppc__ - throwf("universal_boot option can only be used on Intel machines"); - #endif - onlyArchs.insert(CPU_TYPE_POWERPC); - onlyArchs.insert(CPU_TYPE_I386); - makeSymLink = true; - } - else { - usage(); - throwf("unknown option: %s\n", arg); - } + try { + // parse command line options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-debug") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-force") == 0 ) { + force = true; + } + else if ( strcmp(arg, "-verify") == 0 ) { + verify = true; + } + else if ( strcmp(arg, "-sort_by_name") == 0 ) { + alphaSort = true; + } + else if ( strcmp(arg, "-progress") == 0 ) { + progress = true; + } + else if ( strcmp(arg, "-opt") == 0 ) { + optimize = true; + } + else if ( strcmp(arg, "-no_opt") == 0 ) { + optimize = false; + } + else if ( strcmp(arg, "-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 ) + throw "cannot use both -root and -overlay"; + rootPath = argv[++i]; + if ( rootPath == NULL ) + throw "-root missing path argument"; + hasRoot = true; + } + else if ( strcmp(arg, "-overlay") == 0 ) { + if ( hasRoot ) + throw "cannot use both -root and -overlay"; + rootPath = argv[++i]; + if ( rootPath == NULL ) + throw "-root missing path argument"; + hasOverlay = true; + } + else if ( strcmp(arg, "-arch") == 0 ) { + const char* arch = argv[++i]; + if ( strcmp(arch, "ppc") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)); + else if ( strcmp(arch, "i386") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); + else if ( strcmp(arch, "x86_64") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); + else if ( strcmp(arch, "armv4t") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T)); + else if ( strcmp(arch, "armv5") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V5TEJ)); + else if ( strcmp(arch, "armv6") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6)); + else if ( strcmp(arch, "armv7") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7)); + else + throwf("unknown architecture %s", arch); + } + else if ( strcmp(arg, "-universal_boot") == 0 ) { + #if __ppc__ + throwf("universal_boot option can only be used on Intel machines"); + #endif + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); + onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); } else { usage(); throwf("unknown option: %s\n", arg); } } - - // if no restrictions specified, use architectures that work on this machine - if ( onlyArchs.size() == 0 ) { - int available; - size_t len = sizeof(int); - #if __ppc__ - onlyArchs.insert(CPU_TYPE_POWERPC); - if ( (sysctlbyname("hw.optional.64bitops", &available, &len, NULL, 0) == 0) && available ) - onlyArchs.insert(CPU_TYPE_POWERPC64); - #elif __i386__ - onlyArchs.insert(CPU_TYPE_I386); - onlyArchs.insert(CPU_TYPE_POWERPC); // assume rosetta always available - if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) - onlyArchs.insert(CPU_TYPE_X86_64); - #else - #error unknown architecture - #endif + else { + usage(); + throwf("unknown option: %s\n", arg); } - - if ( geteuid() != 0 ) - throw "you must be root to run this tool"; - - // build list of shared dylibs - scanForSharedDylibs(rootPath, "/var/db/dyld/shared_region_roots/", onlyArchs); - updateSharedeCacheFile(rootPath, DYLD_SHARED_CACHE_DIR, onlyArchs, force, alphaSort, optimize, false); - - // To make a universal bootable image with dyld caches, - // build the rosetta cache and symlink ppc to point to it. - // A rosetta cache is just an unoptimized ppc cache, so ppc machine can use it too. - // rdar://problem/5498469 - if ( makeSymLink ) { - char symLinkLocation[1024]; - strcpy(symLinkLocation, rootPath); - strcat(symLinkLocation, DYLD_SHARED_CACHE_DIR); - strcat(symLinkLocation, DYLD_SHARED_CACHE_BASE_NAME); - strcat(symLinkLocation, SharedCache::filename(true)); - char symLinkTarget[1024]; - strcpy(symLinkTarget, DYLD_SHARED_CACHE_BASE_NAME); - strcat(symLinkTarget, SharedCache::filename(false)); - if ( symlink(symLinkTarget, symLinkLocation) == -1 ) { - if ( errno != EEXIST ) - throwf("symlink() returned errno=%d", errno); - } + } + + // strip tailing slashes on -root or -overlay + if ( rootPath[0] != '\0' ) { + int len = strlen(rootPath)-1; + if ( rootPath[len] == '/' ) { + char* newRootPath = strdup(rootPath); + while ( newRootPath[len] == '/' ) + newRootPath[len--] = '\0'; + rootPath = newRootPath; } } - catch (const char* msg) { - fprintf(stderr, "update_dyld_shared_cache failed: %s\n", msg); - return 1; + + // 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)); + } + 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 unknown architecture + #endif } - return 0; + if ( !verify && (geteuid() != 0) ) + throw "you must be root to run this tool"; + + // build list of shared dylibs + if ( dylibListFile != NULL ) + 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, + false, verify, keepSignatures); } + catch (const char* msg) { + fprintf(stderr, "update_dyld_shared_cache failed: %s\n", msg); + return 1; + } + + return 0; } diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 1274a7f..23f89cb 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -37,13 +37,12 @@ #include "ImageLoader.h" -// in libc.a -extern "C" void _spin_lock(uint32_t*); -extern "C" void _spin_unlock(uint32_t*); 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; uint32_t ImageLoader::fgTotalBindFixups = 0; uint32_t ImageLoader::fgTotalBindSymbolsResolved = 0; @@ -56,61 +55,39 @@ uint64_t ImageLoader::fgTotalBytesPreFetched = 0; uint64_t ImageLoader::fgTotalLoadLibrariesTime; uint64_t ImageLoader::fgTotalRebaseTime; uint64_t ImageLoader::fgTotalBindTime; +uint64_t ImageLoader::fgTotalWeakBindTime; +uint64_t ImageLoader::fgTotalDOF; uint64_t ImageLoader::fgTotalInitTime; -uintptr_t ImageLoader::fgNextSplitSegAddress = 0x90000000; uint16_t ImageLoader::fgLoadOrdinal = 0; -uintptr_t Segment::fgNextPIEDylibAddress = 0; +uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; -void ImageLoader::init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate) + +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), + fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), + fHideSymbols(false), fMatchByInstallName(false), + fRegisteredDOF(false), fAllLazyPointersBound(false), fBeingRemoved(false), fAddFuncNotified(false), + fPathOwnedByImage(false), fWeakSymbolsBound(false) { - fPathHash = 0; - fPath = path; - fLogicalPath = NULL; - fDevice = device; - fInode = inode; - fLastModified = modDate; - fOffsetInFatFile = offsetInFat; - fLibraries = NULL; - fLibrariesCount = 0; - fDlopenReferenceCount = 0; - fStaticReferenceCount = 0; - fDynamicReferenceCount = 0; - fDynamicReferences = NULL; - fDepth = 0; - fLoadOrder = fgLoadOrdinal++; - fState = 0; - fAllLibraryChecksumsAndLoadAddressesMatch = false; - fLeaveMapped = false; - fNeverUnload = false; - fHideSymbols = false; - fMatchByInstallName = false; - fRegisteredDOF = false; -#if IMAGE_NOTIFY_SUPPORT - fAnnounced = false; -#endif - fAllLazyPointersBound = false; - fBeingRemoved = false; - fAddFuncNotified = false; - fPathOwnedByImage = false; -#if RECURSIVE_INITIALIZER_LOCK - fInitializerRecursiveLock = NULL; -#else - fInitializerLock = 0; -#endif if ( fPath != NULL ) fPathHash = hash(fPath); } -ImageLoader::ImageLoader(const char* path, uint64_t offsetInFat, const struct stat& info) +void ImageLoader::deleteImage(ImageLoader* image) { - init(path, offsetInFat, info.st_dev, info.st_ino, info.st_mtime); -} - -ImageLoader::ImageLoader(const char* moduleName) -{ - init(moduleName, 0, 0, 0, 0); + // this cannot be done in destructor because libImage() is implemented + // in a subclass + for(unsigned int i=0; i < image->libraryCount(); ++i) { + ImageLoader* lib = image->libImage(i); + if ( lib != NULL ) + lib->fStaticReferenceCount--; + } + delete image; } @@ -118,15 +95,6 @@ ImageLoader::~ImageLoader() { if ( fPathOwnedByImage && (fPath != NULL) ) delete [] fPath; - if ( fLogicalPath != NULL ) - delete [] fLogicalPath; - if ( fLibraries != NULL ) { - for (uint32_t i = 0; i < fLibrariesCount; ++i) { - if ( fLibraries[i].image != NULL ) - fLibraries[i].image->fStaticReferenceCount--; - } - delete [] fLibraries; - } if ( fDynamicReferences != NULL ) { for (std::set::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { const_cast(*it)->fDynamicReferenceCount--; @@ -135,10 +103,17 @@ ImageLoader::~ImageLoader() } } +void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) +{ + fDevice = device; + fInode = inode; + fLastModified = modDate; +} + void ImageLoader::setMapped(const LinkContext& context) { fState = dyld_image_state_mapped; - context.notifySingle(dyld_image_state_mapped, this->machHeader(), fPath, fLastModified); + context.notifySingle(dyld_image_state_mapped, this); // note: can throw exception } void ImageLoader::addDynamicReference(const ImageLoader* target) @@ -190,29 +165,6 @@ void ImageLoader::setPathUnowned(const char* path) fPathHash = hash(fPath); } -void ImageLoader::setLogicalPath(const char* path) -{ - if ( fPath == NULL ) { - // no physical path set yet, so use this path as physical - this->setPath(path); - } - else if ( strcmp(path, fPath) == 0 ) { - // do not set logical path because it is the same as the physical path - fLogicalPath = NULL; - } - else { - fLogicalPath = new char[strlen(path)+1]; - strcpy((char*)fLogicalPath, path); - } -} - -const char* ImageLoader::getLogicalPath() const -{ - if ( fLogicalPath != NULL ) - return fLogicalPath; - else - return fPath; -} uint32_t ImageLoader::hash(const char* path) { @@ -251,11 +203,6 @@ const char* ImageLoader::getShortName() const return fPath; } -uint64_t ImageLoader::getOffsetInFatFile() const -{ - return fOffsetInFatFile; -} - void ImageLoader::setLeaveMapped() { fLeaveMapped = true; @@ -285,11 +232,10 @@ bool ImageLoader::containsAddress(const void* addr) const { if ( ! this->isLinked() ) return false; - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - const uint8_t* start = (const uint8_t*)seg->getActualLoadAddress(this); - const uint8_t* end = start + seg->getSize(); - if ( (start <= addr) && (addr < end) && !seg->unaccessible() ) + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uint8_t* start = (const uint8_t*)segActualLoadAddress(i); + const uint8_t* end = (const uint8_t*)segActualEndAddress(i); + if ( (start <= addr) && (addr < end) && !segUnaccessible(i) ) return true; } return false; @@ -297,10 +243,9 @@ bool ImageLoader::containsAddress(const void* addr) const bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) const { - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - const uint8_t* segStart = (const uint8_t*)(seg->getActualLoadAddress(this)); - const uint8_t* segEnd = segStart + seg->getSize(); + 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 ( (start <= segStart) && (segStart < end) ) return true; if ( (start <= segEnd) && (segEnd < end) ) @@ -313,11 +258,10 @@ bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) c void ImageLoader::getMappedRegions(MappedRegion*& regions) const { - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { MappedRegion region; - region.address = seg->getActualLoadAddress(this); - region.size = seg->getSize(); + region.address = segActualLoadAddress(i); + region.size = segSize(i); *regions++ = region; } } @@ -340,25 +284,25 @@ const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcep // search self if ( notInImgageList(this, dsiStart, dsiCur) ) { - sym = this->findExportedSymbol(name, NULL, false, foundIn); + sym = this->findExportedSymbol(name, false, foundIn); if ( sym != NULL ) return sym; *dsiCur++ = this; } // search directly dependent libraries - for (uint32_t i=0; i < fLibrariesCount; ++i) { - ImageLoader* dependentImage = fLibraries[i].image; + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { - const ImageLoader::Symbol* sym = dependentImage->findExportedSymbol(name, NULL, false, foundIn); + const ImageLoader::Symbol* sym = dependentImage->findExportedSymbol(name, false, foundIn); if ( sym != NULL ) return sym; } } // search indirectly dependent libraries - for (uint32_t i=0; i < fLibrariesCount; ++i) { - ImageLoader* dependentImage = fLibraries[i].image; + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { *dsiCur++ = dependentImage; const ImageLoader::Symbol* sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn); @@ -394,7 +338,7 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fStaticReferenceCount, fNeverUnload); uint64_t t0 = mach_absolute_time(); - this->recursiveLoadLibraries(context,loaderRPaths); + this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths); context.notifyBatch(dyld_image_state_dependents_mapped); // we only do the loading step for preflights @@ -411,20 +355,26 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr uint64_t t3 = mach_absolute_time(); this->recursiveBind(context, forceLazysBound); + + uint64_t t4 = mach_absolute_time(); + this->weakBind(context); context.notifyBatch(dyld_image_state_bound); - uint64_t t4 = 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(); fgTotalLoadLibrariesTime += t1 - t0; fgTotalRebaseTime += t3 - t2; fgTotalBindTime += t4 - t3; + fgTotalWeakBindTime += t5 - t4; + fgTotalDOF += t6 - t5; // done with initial dylib loads - Segment::fgNextPIEDylibAddress = 0; + fgNextPIEDylibAddress = 0; } @@ -445,16 +395,11 @@ bool ImageLoader::decrementDlopenReferenceCount() void ImageLoader::runInitializers(const LinkContext& context) { -#if IMAGE_NOTIFY_SUPPORT - ImageLoader* newImages[context.imageCount()]; - ImageLoader** end = newImages; - this->recursiveImageAnnouncement(context, end); // build bottom up list images being added - context.notifyAdding(newImages, end-newImages); // tell anyone who cares about these -#endif - uint64_t t1 = mach_absolute_time(); - this->recursiveInitialization(context, mach_thread_self()); + mach_port_t this_thread = mach_thread_self(); + this->recursiveInitialization(context, this_thread); context.notifyBatch(dyld_image_state_initialized); + mach_port_deallocate(mach_task_self(), this_thread); uint64_t t2 = mach_absolute_time(); fgTotalInitTime += (t2 - t1); } @@ -467,10 +412,10 @@ void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive if ( recursive ) { // bind lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->bindAllLazyPointers(context, recursive); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->bindAllLazyPointers(context, recursive); } } // bind lazies in this image @@ -479,83 +424,6 @@ void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive } -intptr_t ImageLoader::assignSegmentAddresses(const LinkContext& context) -{ - // preflight and calculate slide if needed - intptr_t slide = 0; - if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { - bool needsToSlide = false; - uintptr_t lowAddr = UINTPTR_MAX; - uintptr_t highAddr = 0; - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - const uintptr_t segLow = seg->getPreferredLoadAddress(); - const uintptr_t segHigh = (segLow + seg->getSize() + 4095) & -4096; - if ( segLow < lowAddr ) - lowAddr = segLow; - if ( segHigh > highAddr ) - highAddr = segHigh; - - if ( !seg->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) ) - needsToSlide = true; - } - if ( needsToSlide ) { - // find a chunk of address space to hold all segments - uintptr_t addr = Segment::reserveAnAddressRange(highAddr-lowAddr, context); - slide = addr - lowAddr; - } - } - else if ( ! this->segmentsCanSlide() ) { - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( strcmp(seg->getName(), "__PAGEZERO") == 0 ) - continue; - if ( !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) ) - throw "can't map"; - } - } - else { - // mach-o does not support independently sliding segments - } - return slide; -} - - -void ImageLoader::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) -{ - if ( context.verboseMapping ) - dyld::log("dyld: Mapping %s\n", this->getPath()); - // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); - // map in all segments - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - seg->map(fd, offsetInFat, slide, this, context); - } - // update slide to reflect load location - this->setSlide(slide); -} - -void ImageLoader::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) -{ - if ( context.verboseMapping ) - dyld::log("dyld: Mapping memory %p\n", memoryImage); - // find address range for image - intptr_t slide = this->assignSegmentAddresses(context); - // map in all segments - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - seg->map(memoryImage, slide, this, context); - } - // update slide to reflect load location - this->setSlide(slide); - // set R/W permissions on all segments at slide location - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - seg->setPermissions(context, this); - } -} - bool ImageLoader::allDependentLibrariesAsWhenPreBound() const { return fAllLibraryChecksumsAndLoadAddressesMatch; @@ -573,10 +441,10 @@ unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) // get depth of dependents unsigned int minDependentDepth = maxDepth; - for(unsigned int i=0; i < fLibrariesCount; ++i) { - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) { - unsigned int d = libInfo.image->recursiveUpdateDepth(maxDepth); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth); if ( d < minDependentDepth ) minDependentDepth = d; } @@ -590,17 +458,15 @@ unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) } -void ImageLoader::recursiveLoadLibraries(const LinkContext& context, const RPathChain& loaderRPaths) +void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths) { if ( fState < dyld_image_state_dependents_mapped ) { // break cycles fState = dyld_image_state_dependents_mapped; // get list of libraries this image needs - fLibrariesCount = this->doGetDependentLibraryCount(); - fLibraries = new DependentLibrary[fLibrariesCount]; - bzero(fLibraries, sizeof(DependentLibrary)*fLibrariesCount); - DependentLibraryInfo libraryInfos[fLibrariesCount]; + //dyld::log("ImageLoader::recursiveLoadLibraries() %ld = %d*%ld\n", fLibrariesCount*sizeof(DependentLibrary), fLibrariesCount, sizeof(DependentLibrary)); + DependentLibraryInfo libraryInfos[fLibraryCount]; this->doGetDependentLibraries(libraryInfos); // get list of rpaths that this image adds @@ -610,74 +476,85 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, const RPath // try to load each bool canUsePrelinkingInfo = true; - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& requiredLib = fLibraries[i]; + for(unsigned int i=0; i < fLibraryCount; ++i){ + ImageLoader* dependentLib; + bool depLibReExported = false; + bool depLibReRequired = false; + bool depLibCheckSumsMatch = false; DependentLibraryInfo& requiredLibInfo = libraryInfos[i]; +#if DYLD_SHARED_CACHE_SUPPORT + 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); + continue; + } +#endif try { - bool depNamespace = false; - requiredLib.image = context.loadLibrary(requiredLibInfo.name, true, depNamespace, this->getPath(), &thisRPaths); - if ( requiredLib.image == this ) { + dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths); + if ( dependentLib == this ) { // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 - requiredLib.image = context.loadLibrary(requiredLibInfo.name, false, depNamespace, NULL, NULL); - if ( requiredLib.image != this ) + dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL); + if ( dependentLib != this ) dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath()); } if ( fNeverUnload ) - requiredLib.image->setNeverUnload(); - requiredLib.image->fStaticReferenceCount += 1; - LibraryInfo actualInfo = requiredLib.image->doGetLibraryInfo(); - requiredLib.required = requiredLibInfo.required; - requiredLib.checksumMatches = ( actualInfo.checksum == requiredLibInfo.info.checksum ); - requiredLib.isReExported = requiredLibInfo.reExported; - if ( ! requiredLib.isReExported ) { - requiredLib.isSubFramework = requiredLib.image->isSubframeworkOf(context, this); - requiredLib.isReExported = requiredLib.isSubFramework || this->hasSubLibrary(context, requiredLib.image); + dependentLib->setNeverUnload(); + dependentLib->fStaticReferenceCount += 1; + LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(); + depLibReRequired = requiredLibInfo.required; + depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum ); + depLibReExported = requiredLibInfo.reExported; + if ( ! depLibReExported ) { + // for pre-10.5 binaries that did not use LC_REEXPORT_DYLIB + depLibReExported = dependentLib->isSubframeworkOf(context, this) || this->hasSubLibrary(context, dependentLib); } // check found library version is compatible if ( actualInfo.minVersion < requiredLibInfo.info.minVersion ) { 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, - requiredLib.image->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.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 ( !requiredLib.checksumMatches || (requiredLib.image->getSlide() != 0) ) + if ( !depLibCheckSumsMatch || (dependentLib->getSlide() != 0) ) canUsePrelinkingInfo = false; //if ( context.verbosePrebinding ) { // if ( !requiredLib.checksumMatches ) // fprintf(stderr, "dyld: checksum mismatch, (%u v %u) for %s referencing %s\n", - // requiredLibInfo.info.checksum, actualInfo.checksum, this->getPath(), requiredLib.image->getPath()); - // if ( requiredLib.image->getSlide() != 0 ) - // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), requiredLib.image->getPath()); + // requiredLibInfo.info.checksum, actualInfo.checksum, this->getPath(), dependentLib->getPath()); + // if ( dependentLib->getSlide() != 0 ) + // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), dependentLib->getPath()); //} } catch (const char* msg) { //if ( context.verbosePrebinding ) - // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), requiredLib.image->getPath()); + // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath()); if ( requiredLibInfo.required ) { fState = dyld_image_state_mapped; dyld::throwf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getPath(), msg); } // ok if weak library not found - requiredLib.image = NULL; + 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); } fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo; // tell each to load its dependents - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& lib = fLibraries[i]; - if ( lib.image != NULL ) { - lib.image->recursiveLoadLibraries(context, thisRPaths); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths); } } // do deep prebind check if ( fAllLibraryChecksumsAndLoadAddressesMatch ) { - for(unsigned int i=0; i < fLibrariesCount; ++i){ - const DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) { - if ( !libInfo.image->allDependentLibrariesAsWhenPreBound() ) + for(unsigned int i=0; i < libraryCount(); ++i){ + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + if ( !dependentImage->allDependentLibrariesAsWhenPreBound() ) fAllLibraryChecksumsAndLoadAddressesMatch = false; } } @@ -700,17 +577,17 @@ void ImageLoader::recursiveRebase(const LinkContext& context) try { // rebase lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveRebase(context); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveRebase(context); } // rebase this image doRebase(context); // notify - context.notifySingle(dyld_image_state_rebased, this->machHeader(), fPath, fLastModified); + context.notifySingle(dyld_image_state_rebased, this); } catch (const char* msg) { // this image is not rebased @@ -735,19 +612,18 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound try { // bind lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveBind(context, forceLazysBound); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveBind(context, forceLazysBound); } // bind this image this->doBind(context, forceLazysBound); - this->doUpdateMappingPermissions(context); // mark if lazys are also bound if ( forceLazysBound || this->usablePrebinding(context) ) fAllLazyPointersBound = true; - context.notifySingle(dyld_image_state_bound, this->machHeader(), fPath, fLastModified); + context.notifySingle(dyld_image_state_bound, this); } catch (const char* msg) { // restore state @@ -757,31 +633,116 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound } } - -#if IMAGE_NOTIFY_SUPPORT -void ImageLoader::recursiveImageAnnouncement(const LinkContext& context, ImageLoader**& newImages) +void ImageLoader::weakBind(const LinkContext& context) { - if ( ! fAnnounced ) { - // break cycles - fAnnounced = true; - - // announce lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveImageAnnouncement(context, newImages); + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind start:\n"); + // get set of ImageLoaders that participate in coalecsing + ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; + int count = context.getCoalescedImages(imagesNeedingCoalescing); + + // count how many have not already had weakbinding done + int countNotYetWeakBound = 0; + int countOfImagesWithWeakDefinitions = 0; + for(int i=0; i < count; ++i) { + if ( ! imagesNeedingCoalescing[i]->fWeakSymbolsBound ) + ++countNotYetWeakBound; + if ( imagesNeedingCoalescing[i]->hasCoalescedExports() ) + ++countOfImagesWithWeakDefinitions; + } + + // don't need to do any coalescing if only one image has overrides, or all have already been done + if ( (countOfImagesWithWeakDefinitions > 1) && (countNotYetWeakBound > 0) ) { + // make symbol iterators for each + ImageLoader::CoalIterator iterators[count]; + ImageLoader::CoalIterator* sortedIts[count]; + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i); + sortedIts[i] = &iterators[i]; + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getPath()); } - // add to list of images to notify about - *newImages++ = this; - //dyld::log("next size = %d\n", newImages.size()); + // walk all symbols keeping iterators in sync by + // only ever incrementing the iterator with the lowest symbol + int doneCount = 0; + while ( doneCount != count ) { + //for(int i=0; i < count; ++i) + // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName); + //dyld::log("\n"); + // increment iterator with lowest symbol + if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) ) + ++doneCount; + // re-sort iterators + for(int i=1; i < count; ++i) { + int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName); + if ( result == 0 ) + sortedIts[i-1]->symbolMatches = true; + if ( result > 0 ) { + // new one is bigger then next, so swap + ImageLoader::CoalIterator* temp = sortedIts[i-1]; + sortedIts[i-1] = sortedIts[i]; + sortedIts[i] = temp; + } + if ( result < 0 ) + break; + } + // process all matching symbols just before incrementing the lowest one that matches + if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) { + const char* nameToCoalesce = sortedIts[0]->symbolName; + // pick first symbol in load order (and non-weak overrides weak) + uintptr_t targetAddr = 0; + ImageLoader* targetImage = NULL; + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getPath()); + if ( iterators[i].weakSymbol ) { + if ( targetAddr == 0 ) { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) + targetImage = iterators[i].image; + } + } + else { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) { + targetImage = iterators[i].image; + // strong implementation found, stop searching + break; + } + } + } + } + if ( context.verboseWeakBind ) + dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName()); + + // tell each to bind to this symbol (unless already bound) + if ( targetAddr != 0 ) { + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", nameToCoalesce, iterators[i].image->getShortName(), targetAddr, targetImage->getShortName()); + if ( ! iterators[i].image->fWeakSymbolsBound ) + iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, context); + iterators[i].symbolMatches = false; + } + } + } + + } + } - // remember that this image wants to be notified about other images - if ( this->hasImageNotification() ) - context.addImageNeedingNotification(this); + // mark all as having all weak symbols bound + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->fWeakSymbolsBound = true; + } } + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind end\n"); } -#endif + + void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) { @@ -790,10 +751,10 @@ void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vecto fRegisteredDOF = true; // gather lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveGetDOFSections(context, dofs); + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveGetDOFSections(context, dofs); } this->doGetDOFSections(context, dofs); } @@ -822,25 +783,21 @@ void ImageLoader::recursiveSpinUnLock() void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread) { -#if RECURSIVE_INITIALIZER_LOCK recursive_lock lock_info(this_thread); recursiveSpinLock(lock_info); -#else - _spin_lock(&fInitializerLock); -#endif if ( fState < dyld_image_state_dependents_initialized-1 ) { uint8_t oldState = fState; // break cycles fState = dyld_image_state_dependents_initialized-1; - try { // initialize lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) // don't try to initialize stuff "above" me - if ( (libInfo.image != NULL) && (libInfo.image->fDepth >= fDepth) ) - libInfo.image->recursiveInitialization(context, this_thread); + if ( (dependentImage != NULL) && (dependentImage->fDepth >= fDepth) ) + dependentImage->recursiveInitialization(context, this_thread); } // record termination order @@ -850,32 +807,25 @@ void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_ // let objc know we are about to initalize this image fState = dyld_image_state_dependents_initialized; oldState = fState; - context.notifySingle(dyld_image_state_dependents_initialized, this->machHeader(), fPath, fLastModified); + context.notifySingle(dyld_image_state_dependents_initialized, this); // initialize this image 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->machHeader(), fPath, fLastModified); + context.notifySingle(dyld_image_state_initialized, this); } catch (const char* msg) { // this image is not initialized fState = oldState; - #if RECURSIVE_INITIALIZER_LOCK recursiveSpinUnLock(); - #else - _spin_unlock(&fInitializerLock); - #endif throw; } } -#if RECURSIVE_INITIALIZER_LOCK recursiveSpinUnLock(); -#else - _spin_unlock(&fInitializerLock); -#endif } @@ -889,11 +839,11 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) } } if ( partTime < sUnitsPerSecond ) { - uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; - uint32_t milliSeconds = milliSecondsTimeTen/10; + uint32_t milliSecondsTimesHundred = (partTime*100000)/sUnitsPerSecond; + uint32_t milliSeconds = milliSecondsTimesHundred/100; uint32_t percentTimesTen = (partTime*1000)/totalTime; uint32_t percent = percentTimesTen/10; - dyld::log("%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); } else { uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; @@ -926,10 +876,9 @@ static char* commatize(uint64_t in, char* out) } - void ImageLoader::printStatistics(unsigned int imageCount) { - uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalInitTime; + uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; char commaNum1[40]; char commaNum2[40]; @@ -937,6 +886,7 @@ void ImageLoader::printStatistics(unsigned int imageCount) dyld::log("total images loaded: %d (%u from dyld shared cache, %u needed no fixups)\n", imageCount, fgImagesUsedFromSharedCache, fgImagesRequiringNoFixups); dyld::log("total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096); printTime("total images loading time", fgTotalLoadLibrariesTime, totalTime); + printTime("total dtrace DOF registration time", fgTotalDOF, totalTime); dyld::log("total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); printTime("total rebase fixups time", fgTotalRebaseTime, totalTime); dyld::log("total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); @@ -948,8 +898,9 @@ void ImageLoader::printStatistics(unsigned int imageCount) commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths); } printTime("total binding fixups time", fgTotalBindTime, totalTime); + printTime("total weak binding fixups time", fgTotalWeakBindTime, totalTime); dyld::log("total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); - printTime("total init time time", fgTotalInitTime, totalTime); + printTime("total initializer time", fgTotalInitTime, totalTime); } @@ -983,129 +934,5 @@ void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) } -void Segment::map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader* image, const ImageLoader::LinkContext& context) -{ - vm_offset_t fileOffset = this->getFileOffset() + offsetInFatWrapper; - vm_size_t size = this->getFileSize(); - void* requestedLoadAddress = (void*)(this->getPreferredLoadAddress() + slide); - int protection = 0; - if ( !this->unaccessible() ) { - if ( this->executable() ) - protection |= PROT_EXEC; - if ( this->readable() ) - protection |= PROT_READ; - if ( this->writeable() ) - protection |= PROT_WRITE; - } -#if __i386__ - // initially map __IMPORT segments R/W so dyld can update them - if ( this->readOnlyImportStubs() ) - protection |= PROT_WRITE; -#endif - // wholly zero-fill segments have nothing to mmap() in - if ( size > 0 ) { - void* loadAddress = mmap(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, this->getName(), image->getPath()); - } - // update stats - ++ImageLoader::fgTotalSegmentsMapped; - ImageLoader::fgTotalBytesMapped += size; - if ( context.verboseMapping ) - dyld::log("%18s at %p->%p with permissions %c%c%c\n", this->getName(), requestedLoadAddress, (char*)requestedLoadAddress+this->getFileSize()-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); -} - -void Segment::map(const void* memoryImage, intptr_t slide, const ImageLoader* image, const ImageLoader::LinkContext& context) -{ - vm_address_t loadAddress = this->getPreferredLoadAddress() + slide; - vm_address_t srcAddr = (uintptr_t)memoryImage + this->getFileOffset(); - vm_size_t size = this->getFileSize(); - kern_return_t r = vm_copy(mach_task_self(), srcAddr, size, loadAddress); - if ( r != KERN_SUCCESS ) - throw "can't map segment"; - - if ( context.verboseMapping ) - dyld::log("%18s at %p->%p\n", this->getName(), (char*)loadAddress, (char*)loadAddress+this->getFileSize()-1); -} - -void Segment::setPermissions(const ImageLoader::LinkContext& context, const ImageLoader* image) -{ - vm_prot_t protection = 0; - if ( !this->unaccessible() ) { - if ( this->executable() ) - protection |= VM_PROT_EXECUTE; - if ( this->readable() ) - protection |= VM_PROT_READ; - if ( this->writeable() ) - protection |= VM_PROT_WRITE; - } - vm_address_t addr = this->getActualLoadAddress(image); - vm_size_t size = this->getSize(); - 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 ( context.verboseMapping ) { - dyld::log("%18s at %p->%p altered permissions to %c%c%c\n", this->getName(), (char*)addr, (char*)addr+this->getFileSize()-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); - } -} - -void Segment::tempWritable(const ImageLoader::LinkContext& context, const ImageLoader* image) -{ - vm_address_t addr = this->getActualLoadAddress(image); - vm_size_t size = this->getSize(); - const bool setCurrentPermissions = false; - vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ; - if ( this->executable() ) - 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 ( context.verboseMapping ) { - dyld::log("%18s at %p->%p altered permissions to %c%c%c\n", this->getName(), (char*)addr, (char*)addr+this->getFileSize()-1, - (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); - } -} - - -bool Segment::hasTrailingZeroFill() -{ - return ( this->writeable() && (this->getSize() > this->getFileSize()) ); -} - - -uintptr_t Segment::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context) -{ - vm_address_t addr = 0; - 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 - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED); - if ( r == KERN_SUCCESS ) { - fgNextPIEDylibAddress = addr + size; - return addr; - } - fgNextPIEDylibAddress = 0; - } - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE); - if ( r != KERN_SUCCESS ) - throw "out of address space"; - - return addr; -} - -bool Segment::reserveAddressRange(uintptr_t start, size_t length) -{ - vm_address_t addr = start; - vm_size_t size = length; - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); - if ( r != KERN_SUCCESS ) - return false; - return true; -} - diff --git a/src/ImageLoader.h b/src/ImageLoader.h index e6c2480..587a339 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,24 +27,54 @@ #define __IMAGELOADER__ #include +#include #include // struct mach_timebase_info #include // struct mach_thread_self +#include +#include +#include #include #include #include +#include #include "mach-o/dyld_images.h" #include "mach-o/dyld_priv.h" +#if __i386__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_I386 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_I386 +#elif __x86_64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_X86_64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_X86_64 +#elif __ppc__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_PPC + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_PPC +#elif __ppc64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_PPC64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_PPC64 +#elif __arm__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM +#endif + -#define SPLIT_SEG_SHARED_REGION_SUPPORT 0 -#define SPLIT_SEG_DYLIB_SUPPORT (__ppc__ || __i386__) + +#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 IMAGE_NOTIFY_SUPPORT 0 -#define RECURSIVE_INITIALIZER_LOCK 1 #define SUPPORT_OLD_CRT_INITIALIZATION (__ppc__ || __i386__) +#define COMPRESSED_DYLD_INFO_SUPPORT (__i386__ || __x86_64__) +#define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__) +#if __arm__ + #define INITIAL_IMAGE_COUNT 100 +#else + #define INITIAL_IMAGE_COUNT 200 +#endif +#define CODESIGNING_SUPPORT __arm__ // utilities namespace dyld { @@ -55,6 +85,15 @@ namespace dyld { }; +#if __LP64__ + struct macho_header : public mach_header_64 {}; + struct macho_nlist : public nlist_64 {}; +#else + struct macho_header : public mach_header {}; + struct macho_nlist : public nlist {}; +#endif + + struct ProgramVars { const void* mh; @@ -112,28 +151,22 @@ public: }; struct LinkContext { - ImageLoader* (*loadLibrary)(const char* libraryName, bool search, bool findDLL, const char* origin, const RPathChain* rpaths); + ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths); void (*terminationRecorder)(ImageLoader* image); bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + unsigned int (*getCoalescedImages)(ImageLoader* images[]); void (*undefinedHandler)(const char* name); -#if IMAGE_NOTIFY_SUPPORT - void (*addImageNeedingNotification)(ImageLoader* image); - void (*notifyAdding)(const ImageLoader* const * images, unsigned int count); -#endif MappedRegion* (*getAllMappedRegions)(MappedRegion*); void * (*bindingHandler)(const char *, const char *, void *); - void (*notifySingle)(dyld_image_states, const struct mach_header*, const char* path, time_t modDate); + void (*notifySingle)(dyld_image_states, const ImageLoader* image); void (*notifyBatch)(dyld_image_states state); void (*removeImage)(ImageLoader* image); void (*registerDOFs)(const std::vector& dofs); void (*clearAllDepths)(); unsigned int (*imageCount)(); - void (*notifySharedCacheInvalid)(); -#if __i386__ - void (*makeSharedCacheImportSegmentsWritable)(bool writable); -#endif void (*setNewProgramVars)(const ProgramVars&); + bool (*inSharedCache)(const char* path); #if SUPPORT_OLD_CRT_INITIALIZATION void (*setRunInitialzersOldWay)(); #endif @@ -146,6 +179,7 @@ public: ProgramVars programVars; ImageLoader* mainExecutable; const char* imageSuffix; + const char** rootPaths; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; bool dyldLoadedAtSameAddressNeededBySharedCache; @@ -154,17 +188,43 @@ public: bool bindFlat; bool linkingMainExecutable; bool startedInitializingMainExecutable; + bool noPIE; + bool processIsRestricted; bool verboseOpts; bool verboseEnv; bool verboseMapping; bool verboseRebase; bool verboseBind; + bool verboseWeakBind; bool verboseInit; bool verboseDOF; bool verbosePrebinding; bool verboseWarnings; }; + struct CoalIterator + { + ImageLoader* image; + const char* symbolName; + unsigned int loadOrder; + bool weakSymbol; + bool symbolMatches; + bool done; + // the following are private to the ImageLoader subclass + uintptr_t curIndex; + uintptr_t endIndex; + uintptr_t address; + uintptr_t type; + uintptr_t addend; + }; + + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0; + + + // constructor is protected, but anyone can delete an image virtual ~ImageLoader(); @@ -190,9 +250,6 @@ public: uint32_t getPathHash() const { return fPathHash; } - // get path used to load this image represents (ZeroLink only) which image this .o is part of - const char* getLogicalPath() const; - // get path this image is intended to be placed on disk or NULL if no preferred install location virtual const char* getInstallPath() const = 0; @@ -200,9 +257,6 @@ public: bool matchInstallPath() const; void setMatchInstallPath(bool); - // if path was a fat file, offset of image loaded in that fat file - uint64_t getOffsetInFatFile() const; - // mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL) void setHideExports(bool hide = true); @@ -221,6 +275,9 @@ public: // checks if the specifed address is within one of this image's segments virtual bool containsAddress(const void* addr) const; + // checks if the specifed symbol is within this image's symbol table + virtual bool containsSymbol(const void* addr) const = 0; + // checks if the specifed address range overlaps any of this image's segments virtual bool overlapsWithAddressRange(const void* start, const void* end) const; @@ -246,7 +303,7 @@ public: virtual bool hasCoalescedExports() const = 0; // search symbol table of definitions in this image for requested name - virtual const Symbol* findExportedSymbol(const char* name, const void* hint, bool searchReExports, const ImageLoader** foundIn) const = 0; + 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; @@ -278,40 +335,47 @@ public: virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const = 0; // gets attributes of the specified imported symbol - virtual ReferenceFlags geImportedSymbolInfo(const Symbol* sym) const = 0; + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const = 0; // gets name of the specified imported symbol virtual const char* getImportedSymbolName(const Symbol* sym) const = 0; + // find the closest symbol before addr + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + // checks if this image is a bundle and can be loaded but not linked virtual bool isBundle() const = 0; // checks if this image is a dylib virtual bool isDylib() const = 0; + // checks if this image is a main executable + virtual bool isExecutable() const = 0; + + // checks if this image is a main executable + virtual bool isPositionIndependentExecutable() const = 0; + // only for main executable virtual bool forceFlat() const = 0; // called at runtime when a lazily bound function is first called virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + // called at runtime when a fast lazily bound function is first called + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0; + // calls termination routines (e.g. C++ static destructors for image) virtual void doTermination(const LinkContext& context) = 0; -#if IMAGE_NOTIFY_SUPPORT - // tell this image about other images - virtual void doNotification(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]) = 0; -#endif // return if this image has initialization routines virtual bool needsInitialization() = 0; -#if IMAGE_NOTIFY_SUPPORT - // return if this image has a routine to be called when any image is loaded or unloaded - virtual bool hasImageNotification() = 0; -#endif // return if this image has specified section and set start and length virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0; - + + // fills in info about __eh_frame and __unwind_info sections + virtual void getUnwindInfo(dyld_unwind_sections* info) = 0; + // given a pointer into an image, find which segment and section it is in virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0; @@ -324,6 +388,31 @@ public: // add all RPATH paths this image contains virtual void getRPaths(const LinkContext& context, std::vector&) const = 0; + // image has or uses weak definitions that need runtime coalescing + virtual bool participatesInCoalescing() const = 0; + + // if image has a UUID, copy into parameter and return true + virtual bool getUUID(uuid_t) const = 0; + + +// +// A segment is a chunk of an executable file that is mapped into memory. +// + virtual unsigned int segmentCount() const = 0; + virtual const char* segName(unsigned int) const = 0; + virtual uintptr_t segSize(unsigned int) const = 0; + virtual uintptr_t segFileSize(unsigned int) const = 0; + virtual bool segHasTrailingZeroFill(unsigned int) = 0; + virtual uintptr_t segFileOffset(unsigned int) const = 0; + virtual bool segReadable(unsigned int) const = 0; + virtual bool segWriteable(unsigned int) const = 0; + virtual bool segExecutable(unsigned int) const = 0; + virtual bool segUnaccessible(unsigned int) const = 0; + virtual bool segHasPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + dyld_image_states getState() { return (dyld_image_states)fState; } @@ -350,12 +439,13 @@ public: static void addSuffix(const char* path, const char* suffix, char* result); static uint32_t hash(const char*); + + // used instead of directly deleting image + static void deleteImage(ImageLoader*); void setPath(const char* path); // only called for images created from memory void setPathUnowned(const char* path); - - void setLogicalPath(const char* path); - + void clearDepth() { fDepth = 0; } void setBeingRemoved() { fBeingRemoved = true; } @@ -364,36 +454,12 @@ public: void setAddFuncNotified() { fAddFuncNotified = true; } bool addFuncNotified() const { return fAddFuncNotified; } -protected: - struct DependentLibrary; -public: - friend class iterator; - - class iterator - { - public: - iterator& operator++() { ++fLocation; return *this; } - bool operator!=(const iterator& it) const { return (it.fLocation != this->fLocation); } - ImageLoader* operator*() const { return fLocation->image; } - private: - friend class ImageLoader; - iterator(DependentLibrary* loc) : fLocation(loc) {} - DependentLibrary* fLocation; - }; - - iterator beginDependents() { return iterator(fLibraries); } - iterator endDependents() { return iterator(&fLibraries[fLibrariesCount]); } - - - - friend class Segment; - -protected: +protected: // abstract base class so all constructors protected - ImageLoader(const char* path, uint64_t offsetInFat, const struct stat& info); - ImageLoader(const char* moduleName); + ImageLoader(const char* path, unsigned int libCount); ImageLoader(const ImageLoader&); void operator=(const ImageLoader&); + void operator delete(void* image) throw() { free(image); } struct LibraryInfo { @@ -417,38 +483,31 @@ protected: bool reExported; }; - class SegmentIterator - { - public: - SegmentIterator& operator++(); // use inline later to work around circular reference - bool operator!=(const SegmentIterator& it) const { return (it.fLocation != this->fLocation); } - class Segment* operator*() const { return fLocation; } - SegmentIterator(class Segment* loc) : fLocation(loc) {} - private: - class Segment* fLocation; - }; typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars); typedef void (*Terminator)(void); + + + 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; + + // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. // These methods do the above, exactly once, and it the right order - void recursiveLoadLibraries(const LinkContext& context, const RPathChain& loaderRPaths); + void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths); void recursiveUnLoadMappedLibraries(const LinkContext& context); unsigned int recursiveUpdateDepth(unsigned int maxDepth); void recursiveValidate(const LinkContext& context); void recursiveRebase(const LinkContext& context); void recursiveBind(const LinkContext& context, bool forceLazysBound); + void weakBind(const LinkContext& context); void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); -#if IMAGE_NOTIFY_SUPPORT - void recursiveImageAnnouncement(const LinkContext& context, ImageLoader**& newImages); -#endif void recursiveInitialization(const LinkContext& context, mach_port_t this_thread); - // return how many libraries this image depends on - virtual uint32_t doGetDependentLibraryCount() = 0; - - // fill in information about dependent libraries (array length is doGetDependentLibraryCount()) + // fill in information about dependent libraries (array length is fLibraryCount) virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; // called on images that are libraries, returns info about itself @@ -463,9 +522,6 @@ protected: // called later via API to force all lazy pointer to be bound virtual void doBindJustLazies(const LinkContext& context) = 0; - // update any segment permissions - virtual void doUpdateMappingPermissions(const LinkContext& context) = 0; - // if image has any dtrace DOF sections, append them to list to be registered virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) = 0; @@ -484,12 +540,6 @@ protected: // set how much all segments slide virtual void setSlide(intptr_t slide) = 0; - // utility routine to map in all segements in fSegments from a file - virtual void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); - - // utility routine to map in all segements in fSegments from a memory image - virtual void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); - // returns if all dependent libraries checksum's were as expected and none slide bool allDependentLibrariesAsWhenPreBound() const; @@ -504,17 +554,15 @@ protected: // mark that target should not be unloaded unless this is also unloaded void addDynamicReference(const ImageLoader* target); - - // used to start iterating Segments - virtual SegmentIterator beginSegments() const = 0; - // used to end iterating Segments - virtual SegmentIterator endSegments() const = 0; + void setFileInfo(dev_t device, ino_t inode, time_t modDate); - + 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; static uint32_t fgTotalBindFixups; static uint32_t fgTotalBindSymbolsResolved; @@ -527,16 +575,13 @@ protected: static uint64_t fgTotalLoadLibrariesTime; static uint64_t fgTotalRebaseTime; static uint64_t fgTotalBindTime; + static uint64_t fgTotalWeakBindTime; + static uint64_t fgTotalDOF; static uint64_t fgTotalInitTime; - static uintptr_t fgNextSplitSegAddress; const char* fPath; - const char* fLogicalPath; // for ZeroLink - the image which this bundle is part of dev_t fDevice; ino_t fInode; time_t fLastModified; - uint64_t fOffsetInFatFile; - DependentLibrary* fLibraries; - uint32_t fLibrariesCount; uint32_t fPathHash; 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 @@ -544,7 +589,6 @@ protected: std::set* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup private: -#if RECURSIVE_INITIALIZER_LOCK struct recursive_lock { recursive_lock(mach_port_t t) : thread(t), count(0) {} mach_port_t thread; @@ -552,95 +596,33 @@ private: }; void recursiveSpinLock(recursive_lock&); void recursiveSpinUnLock(); -#endif - void init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate); - intptr_t assignSegmentAddresses(const LinkContext& context); const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const; + recursive_lock* fInitializerRecursiveLock; uint16_t fDepth; uint16_t fLoadOrder; uint32_t fState : 8, + fLibraryCount : 10, fAllLibraryChecksumsAndLoadAddressesMatch : 1, fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it 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 fRegisteredDOF : 1, -#if IMAGE_NOTIFY_SUPPORT - fAnnounced : 1, -#endif fAllLazyPointersBound : 1, fBeingRemoved : 1, fAddFuncNotified : 1, - fPathOwnedByImage : 1; + fPathOwnedByImage : 1, + fWeakSymbolsBound : 1; -#if RECURSIVE_INITIALIZER_LOCK - recursive_lock* fInitializerRecursiveLock; -#else - uint32_t fInitializerLock; -#endif static uint16_t fgLoadOrdinal; }; -// -// Segment is an abstract base class. A segment is a chunk of an executable -// file that is mapped into memory. Each subclass of ImageLoader typically -// implements its own concrete subclass of Segment. -// -// -class Segment { -public: - virtual ~Segment() {} - - virtual const char* getName() = 0; - virtual uintptr_t getSize() = 0; - virtual uintptr_t getFileSize() = 0; - virtual bool hasTrailingZeroFill(); - virtual uintptr_t getFileOffset() = 0; - virtual bool readable() = 0; - virtual bool writeable() = 0; - virtual bool executable() = 0; - virtual bool unaccessible() = 0; - virtual uintptr_t getActualLoadAddress(const ImageLoader*) = 0; - virtual uintptr_t getPreferredLoadAddress() = 0; - virtual void unmap(const ImageLoader*) = 0; - virtual Segment* next(Segment*) = 0; -#if __i386__ - virtual bool readOnlyImportStubs() = 0; -#endif - - -protected: - // abstract base class so all constructors protected - Segment() {} - Segment(const Segment&); - void operator=(const Segment&); - - virtual bool hasPreferredLoadAddress() = 0; - //virtual void setActualLoadAddress(uint64_t addr) = 0; - virtual void map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader* image, const ImageLoader::LinkContext& context); - - static bool reserveAddressRange(uintptr_t start, size_t length); - static uintptr_t reserveAnAddressRange(size_t length, const class ImageLoader::LinkContext& context); - static uintptr_t fgNextPIEDylibAddress; - -private: - void setPermissions(const ImageLoader::LinkContext& context, const ImageLoader* image); - void map(const void* memoryImage, intptr_t slide, const ImageLoader* image, const ImageLoader::LinkContext& context); - void tempWritable(const ImageLoader::LinkContext& context, const ImageLoader* image); - - friend class ImageLoader; - friend class ImageLoaderMachO; -}; - -inline ImageLoader::SegmentIterator& ImageLoader::SegmentIterator::operator++() { fLocation = fLocation->next(fLocation); return *this; } - - #endif diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 1a8bfdf..094d56d 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -30,278 +30,153 @@ #include #include +#include #include #include #include #include -#include #include #include #include -#include #include #include #include #include -#if __ppc__ || __ppc64__ - #include -#endif -#if __x86_64__ - #include -#endif - -#ifndef MH_PIE - #define MH_PIE 0x200000 -#endif - -#ifndef S_DTRACE_DOF - #define S_DTRACE_DOF 0xF -#endif - -#ifndef S_ATTR_SELF_MODIFYING_CODE - #define S_ATTR_SELF_MODIFYING_CODE 0x04000000 -#endif #include "ImageLoaderMachO.h" +#include "ImageLoaderMachOCompressed.h" +#include "ImageLoaderMachOClassic.h" #include "mach-o/dyld_images.h" -// optimize strcmp for ppc -#if __ppc__ - #include -#else - #define astrcmp(a,b) strcmp(a,b) -#endif - -// in libc.a -extern "C" void _spin_lock(uint32_t*); -extern "C" void _spin_unlock(uint32_t*); // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables #if __LP64__ - #define RELOC_SIZE 3 #define LC_SEGMENT_COMMAND LC_SEGMENT_64 #define LC_ROUTINES_COMMAND LC_ROUTINES_64 - struct macho_header : public mach_header_64 {}; struct macho_segment_command : public segment_command_64 {}; struct macho_section : public section_64 {}; - struct macho_nlist : public nlist_64 {}; struct macho_routines_command : public routines_command_64 {}; #else - #define RELOC_SIZE 2 #define LC_SEGMENT_COMMAND LC_SEGMENT #define LC_ROUTINES_COMMAND LC_ROUTINES - struct macho_header : public mach_header {}; struct macho_segment_command : public segment_command {}; struct macho_section : public section {}; - struct macho_nlist : public nlist {}; struct macho_routines_command : public routines_command {}; #endif -#if __x86_64__ - #define POINTER_RELOC X86_64_RELOC_UNSIGNED -#else - #define POINTER_RELOC GENERIC_RELOC_VANILLA -#endif - -uint32_t ImageLoaderMachO::fgHintedBinaryTreeSearchs = 0; -uint32_t ImageLoaderMachO::fgUnhintedBinaryTreeSearchs = 0; -uint32_t ImageLoaderMachO::fgCountOfImagesWithWeakExports = 0; - -#if __i386__ -uint32_t ImageLoaderMachO::fgReadOnlyImportSpinLock = 0; -#endif +uint32_t ImageLoaderMachO::fgSymbolTableBinarySearchs = 0; +uint32_t ImageLoaderMachO::fgSymbolTrieSearchs = 0; -//#define LINKEDIT_USAGE_DEBUG 1 -#if LINKEDIT_USAGE_DEBUG - #include - static std::set sLinkEditPageBuckets; - - namespace dyld { - extern ImageLoader* findImageContainingAddress(const void* addr); - }; - - static void noteAccessedLinkEditAddress(const void* addr) - { - uintptr_t page = ((uintptr_t)addr) & (-4096); - if ( sLinkEditPageBuckets.count(page) == 0 ) { - ImageLoader* inImage = dyld::findImageContainingAddress(addr); - dyld::log("dyld: accessing page 0x%016lX in __LINKEDIT of %s\n", page, inImage != NULL ? inImage->getPath() : "unknown" ); - } - sLinkEditPageBuckets.insert(page); - } -#endif - -// only way to share initialization in C++ -void ImageLoaderMachO::init() -{ - fMachOData = NULL; - fLinkEditBase = NULL; - fSymbolTable = NULL; - fStrings = NULL; - fDynamicInfo = NULL; - fSlide = 0; - fTwoLevelHints = NULL; - fDylibID = NULL; +ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoader(path, libCount), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), + fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), + fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), #if TEXT_RELOC_SUPPORT - fTextSegmentWithFixups = NULL; + fTextSegmentRebases(false), + fTextSegmentBinds(false), #endif #if __i386__ - fReadOnlyImportSegment = NULL; -#endif - fIsSplitSeg = false; - fInSharedCache = false; -#if __ppc64__ - f4GBWritable = false; -#endif - fHasSubLibraries= false; - fHasSubUmbrella = false; - fInUmbrella = false; - fHasDOFSections = false; - fHasDashInit = false; - fHasInitializers= false; - fHasTerminators = false; -#if IMAGE_NOTIFY_SUPPORT - fHasImageNotifySection = false; + fReadOnlyImportSegment(false), #endif -} - -// create image for main executable -ImageLoaderMachO::ImageLoaderMachO(const struct mach_header* mh, uintptr_t slide, const char* path, const LinkContext& context) - : ImageLoader(path) + fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), + fHasInitializers(false), fHasTerminators(false) { - // clean slate - this->init(); - - // temporary use this buffer until TEXT is mapped in - fMachOData = (const uint8_t*)mh; - - // create segments - this->instantiateSegments((const uint8_t*)mh); - - // set slide for PIE programs - this->setSlide(slide); - - // get pointers to interesting things - this->parseLoadCmds(); + fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); - // update segments to reference load commands in mapped in __TEXT segment - this->adjustSegments(); - -#if __i386__ - // kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding - if ( fReadOnlyImportSegment != NULL ) - fReadOnlyImportSegment->tempWritable(context, this); -#endif - - // for PIE record end of program, to know where to start loading dylibs - if ( mh->flags & MH_PIE ) - Segment::fgNextPIEDylibAddress = (uintptr_t)this->getEnd(); - - // notify state change - this->setMapped(context); - - if ( context.verboseMapping ) { - dyld::log("dyld: Main executable mapped %s\n", this->getPath()); - for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( (strcmp(seg->getName(), "__PAGEZERO") == 0) || (strcmp(seg->getName(), "__UNIXSTACK") == 0) ) - dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->getName(), seg->getPreferredLoadAddress(), seg->getPreferredLoadAddress()+seg->getSize()); - else - dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getSize()); + // construct SegmentMachO object for each LC_SEGMENT cmd using "placement new" to put + // each SegmentMachO object in array at end of ImageLoaderMachO object + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0, segIndex=0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + // ignore zero-sized segments + if ( segCmd->vmsize != 0 ) { + // record offset of load command + segOffsets[segIndex++] = (uint8_t*)segCmd - fMachOData; + } } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + } -// create image by copying an in-memory mach-o file -ImageLoaderMachO::ImageLoaderMachO(const char* moduleName, const struct mach_header* mh, uint64_t len, const LinkContext& context) - : ImageLoader(moduleName) +// 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) { - // clean slate - this->init(); - - // temporary use this buffer until TEXT is mapped in - fMachOData = (const uint8_t*)mh; - - // create segments - this->instantiateSegments((const uint8_t*)mh); - - // map segments - if ( mh->filetype == MH_EXECUTE ) { - throw "can't load another MH_EXECUTE"; - } - else { - ImageLoader::mapSegments((const void*)mh, len, context); + *compressed = false; + *segCount = 0; + *libCount = 0; + 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); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + *compressed = true; + break; + case LC_SEGMENT_COMMAND: + // ignore zero-sized segments + if ( ((struct macho_segment_command*)cmd)->vmsize != 0 ) + *segCount += 1; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + *libCount += 1; + break; + } + uint32_t cmdLength = cmd->cmdsize; + cmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( cmd > endCmds ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", + i, cmdLength, mh->sizeofcmds, path); + } } - - // for compatibility, never unload dylibs loaded from memory - this->setNeverUnload(); - - // get pointers to interesting things - this->parseLoadCmds(); - - // update segments to reference load commands in mapped in __TEXT segment - this->adjustSegments(); - - // bundle loads need path copied - if ( moduleName != NULL ) - this->setPath(moduleName); - - // notify state change - this->setMapped(context); + // fSegmentsArrayCount is only 8-bits + if ( *segCount > 255 ) + dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); -} - -// create image by using cached mach-o file -ImageLoaderMachO::ImageLoaderMachO(const struct mach_header* mh, const char* path, const struct stat& info, const LinkContext& context) - : ImageLoader(path, 0, info) -{ - // clean slate - this->init(); - - // already mapped to mh address - fMachOData = (const uint8_t*)mh; - - // usually a split seg - fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); - - // remember this is from shared cache and cannot be unloaded - fInSharedCache = true; - this->setNeverUnload(); + // fSegmentsArrayCount is only 8-bits + if ( *libCount > 4095 ) + dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); - // create segments - this->instantiateSegments((const uint8_t*)mh); - - // segments already mapped in cache - if ( context.verboseMapping ) { - dyld::log("dyld: Using shared cached for %s\n", path); - for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getSize()); - } - } + if ( needsAddedLibSystemDepency(*libCount, mh) ) + *libCount = 1; +} - // get pointers to interesting things - this->parseLoadCmds(); - // note: path is mapped into cache so no need for ImageLoader to make a copy - // notify state change - this->setMapped(context); +// create image for main executable +ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context) +{ + //dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n", + // sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed)); + bool compressed; + unsigned int segCount; + unsigned int libCount; + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); + else + return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); } // create image by mapping in a mach-o file -ImageLoaderMachO::ImageLoaderMachO(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat, +ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, const LinkContext& context) - : ImageLoader(path, offsetInFat, info) -{ - // clean slate - this->init(); - - // read load commands +{ + // get load commands const unsigned int dataSize = sizeof(macho_header) + ((macho_header*)firstPage)->sizeofcmds; uint8_t buffer[dataSize]; const uint8_t* fileData = firstPage; @@ -311,990 +186,490 @@ ImageLoaderMachO::ImageLoaderMachO(const char* path, int fd, const uint8_t first memcpy(buffer, firstPage, 4096); pread(fd, &buffer[4096], dataSize-4096, offsetInFat+4096); } - - // temporary use this buffer until TEXT is mapped in - fMachOData = fileData; - - // the meaning of many fields changes in split seg mach-o files - fIsSplitSeg = ((((macho_header*)fileData)->flags & MH_SPLIT_SEGS) != 0) && (((macho_header*)fileData)->filetype == MH_DYLIB); - - // create segments - this->instantiateSegments(fileData); - - // map segments, except for main executable which is already mapped in by kernel - if ( ((macho_header*)fileData)->filetype != MH_EXECUTE ) - this->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); - - // get pointers to interesting things - this->parseLoadCmds(); - - // update segments to reference load commands in mapped in __TEXT segment - this->adjustSegments(); - - // notify state change - this->setMapped(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 = getInstallPath(); - if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) - this->setPathUnowned(installName); - if ( path[0] != '/' ) { - // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them - char realPath[MAXPATHLEN]; - if ( realpath(path, realPath) != NULL ) - this->setPath(realPath); - else - this->setPath(path); - } - else - this->setPath(path); - - // tell kernel about pages we are going to need soon - if ( ! context.preFetchDisabled ) - this->preFetch(fd, offsetInFat, context); + bool compressed; + unsigned int segCount; + unsigned int libCount; + sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, context); + else + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, 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) +{ + // instantiate right concrete class + bool compressed; + unsigned int segCount; + unsigned int libCount; + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, info, segCount, libCount, context); + else + return ImageLoaderMachOClassic::instantiateFromCache(mh, path, info, segCount, libCount, context); +} - -ImageLoaderMachO::~ImageLoaderMachO() -{ - // keep count of images with weak exports - if ( this->hasCoalescedExports() ) - --fgCountOfImagesWithWeakExports; - - // keep count of images used in shared cache - if ( fInSharedCache ) - --fgImagesUsedFromSharedCache; - - // usually unmap image when done - if ( ! this->leaveMapped() && (this->getState() >= dyld_image_state_mapped) ) { - // first segment has load commands, so unmap last - Segment* firstSeg = *(this->beginSegments()); - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( seg != firstSeg ) - seg->unmap(this); - } - firstSeg->unmap(this); - } - // free segment objects - free(fSegmentsArray); +// create image by copying an in-memory mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context) +{ + bool compressed; + unsigned int segCount; + unsigned int libCount; + sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); + else + return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); } -void ImageLoaderMachO::instantiateSegments(const uint8_t* fileData) +void ImageLoaderMachO::parseLoadCmds() { - const uint32_t cmd_count = ((macho_header*)fileData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fileData[sizeof(macho_header)]; - - // count LC_SEGMENT cmd and reserve that many segment slots - uint32_t segCount = 0; - const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - // ignore zero-sized segments - if ( ((struct macho_segment_command*)cmd)->vmsize != 0 ) - ++segCount; + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + for(unsigned int i=0; i < fSegmentsCount; ++i) { + // set up pointer to __LINKEDIT segment + if ( strcmp(segName(i),"__LINKEDIT") == 0 ) + fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); +#if TEXT_RELOC_SUPPORT + // __TEXT segment always starts at beginning of file and contains mach_header and load commands + if ( strcmp(segName(i),"__TEXT") == 0 ) { + if ( segHasRebaseFixUps(i) ) + fTextSegmentRebases = true; + if ( segHasBindFixUps(i) ) + fTextSegmentBinds = true; + } +#endif +#if __i386__ + if ( segIsReadOnlyImport(i) ) + fReadOnlyImportSegment = true; +#endif + // some segment always starts at beginning of file and contains mach_header and load commands + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + fMachOData = (uint8_t*)(segActualLoadAddress(i)); } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - // fSegmentsArrayCount is only 8-bits - if ( segCount > 255 ) - dyld::throwf("more than 255 segments in %s", this->getPath()); - - // allocate array of segment objects in one call to malloc() - //fSegmentsArray = static_cast(operator new[](segCount*sizeof(SegmentMachO))); - fSegmentsArray = static_cast(malloc(segCount*sizeof(SegmentMachO))); - fSegmentsArrayCount = segCount; - // construct Segment object for each LC_SEGMENT cmd using "placment new" - uint32_t segIndex = 0; - cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; - // ignore zero-sized segments - if ( segCmd->vmsize != 0 ) - new (&fSegmentsArray[segIndex++]) SegmentMachO(segCmd); - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + // keep count of prebound images with weak exports + if ( this->participatesInCoalescing() ) { + ++fgImagesRequiringCoalescing; + if ( this->hasCoalescedExports() ) + ++fgImagesHasWeakDefinitions; } -} + // keep count of images used in shared cache + if ( fInSharedCache ) + ++fgImagesUsedFromSharedCache; -void ImageLoaderMachO::adjustSegments() -{ - // tell each segment where is load command is finally mapped + // walk load commands (mapped in at start of __TEXT segment) + const dyld_info_command* dyldInfo = NULL; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const dysymtab_command* dynSymbolTable = NULL; const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - uint32_t segIndex = 0; const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; - // ignore zero-sized segments - if ( segCmd->vmsize != 0 ) { - fSegmentsArray[segIndex].adjust(segCmd); - ++segIndex; - } + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + case LC_SUB_UMBRELLA: + fHasSubUmbrella = true; + break; + case LC_SUB_FRAMEWORK: + fInUmbrella = true; + break; + case LC_SUB_LIBRARY: + fHasSubLibraries = true; + break; + case LC_ROUTINES_COMMAND: + fHasDashInit = true; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (struct dyld_info_command*)cmd; + break; + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) + fHasInitializers = true; + else if ( type == S_MOD_TERM_FUNC_POINTERS ) + fHasTerminators = true; + else if ( type == S_DTRACE_DOF ) + fHasDOFSections = true; + else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) + fEHFrameSectionOffset = (uint8_t*)sect - fMachOData; + else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) + fUnwindInfoSectionOffset = (uint8_t*)sect - fMachOData;; + } + } + break; + case LC_TWOLEVEL_HINTS: + // no longer supported + break; + case LC_ID_DYLIB: + { + fDylibIDOffset = (uint8_t*)cmd - fMachOData; + } + break; + case LC_RPATH: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + // do nothing, just prevent LC_REQ_DYLD exception from occuring + break; + default: + if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) + dyld::throwf("unknown required load command 0x%08X", cmd->cmd); } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } -} - -void ImageLoaderMachO::preFetch(int fd, uint64_t offsetInFat, const LinkContext& context) -{ - // always prefetch a subrange of __LINKEDIT pages - uintptr_t symbolTableOffset = (uintptr_t)fSymbolTable - (uintptr_t)fLinkEditBase; - uintptr_t stringTableOffset = (uintptr_t)fStrings - (uintptr_t)fLinkEditBase; - uintptr_t start; - // if image did not load at preferred address - if ( fSegmentsArray[0].getPreferredLoadAddress() != (uintptr_t)fMachOData ) { - // local relocations will be processed, so start pre-fetch at local symbols - start = offsetInFat + fDynamicInfo->locreloff; - } - else { - // otherwise start pre-fetch at global symbols section of symbol table - start = offsetInFat + symbolTableOffset + fDynamicInfo->iextdefsym * sizeof(macho_nlist); - } - // prefetch ends at end of last undefined string in string pool - uintptr_t end = offsetInFat + stringTableOffset; - if ( fDynamicInfo->nundefsym != 0 ) - end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx; - else if ( fDynamicInfo->nextdefsym != 0 ) - end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; - radvisory advice; - advice.ra_offset = start & (-4096); // page align - advice.ra_count = (end-advice.ra_offset+4095) & (-4096); - fgTotalBytesPreFetched += advice.ra_count; - fcntl(fd, F_RDADVISE, &advice); - if ( context.verboseMapping ) { - dyld::log("%18s prefetching 0x%0llX -> 0x%0llX\n", - "__LINKEDIT", advice.ra_offset+(uintptr_t)fLinkEditBase-offsetInFat, advice.ra_offset+advice.ra_count+(uintptr_t)fLinkEditBase-offsetInFat); - } + if ( dyldInfo != NULL ) + this->setDyldInfo(dyldInfo); + if ( symbolTable != NULL) + this->setSymbolTableInfo(symbolTable, symbolTableStrings, dynSymbolTable); - // prefetch __DATA/__OBJC pages during launch, but not for dynamically loaded code - if ( context.linkingMainExecutable ) { - for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( seg->writeable() && (seg->getFileSize() > 0) ) { - // prefetch writable segment that have mmap'ed regions - advice.ra_offset = offsetInFat + seg->getFileOffset(); - advice.ra_count = seg->getFileSize(); - // limit prefetch to 1MB (256 pages) - if ( advice.ra_count > 1024*1024 ) - advice.ra_count = 1024*1024; - fgTotalBytesPreFetched += advice.ra_count; - fcntl(fd, F_RDADVISE, &advice); - if ( context.verboseMapping ) { - dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", - seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+advice.ra_count-1); - } - } - } - } } -bool ImageLoaderMachO::segmentsMustSlideTogether() const +// don't do this work in destructor because we need object to be full subclass +// for UnmapSegments() to work +void ImageLoaderMachO::destroy() { - return true; + // keep count of images with weak exports + if ( this->participatesInCoalescing() ) { + --fgImagesRequiringCoalescing; + if ( this->hasCoalescedExports() ) + --fgImagesHasWeakDefinitions; + } + + // keep count of images used in shared cache + if ( fInSharedCache ) + --fgImagesUsedFromSharedCache; + + // unmap image when done + UnmapSegments(); } -bool ImageLoaderMachO::segmentsCanSlide() const + +unsigned int ImageLoaderMachO::segmentCount() const { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->filetype == MH_DYLIB) || (mh->filetype == MH_BUNDLE) ); + return fSegmentsCount; } -bool ImageLoaderMachO::isBundle() const + +const macho_segment_command* ImageLoaderMachO::segLoadCommand(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( mh->filetype == MH_BUNDLE ); + uint32_t* lcOffsets = this->segmentCommandOffsets(); + uint32_t lcOffset = lcOffsets[segIndex]; + return (macho_segment_command*)(&fMachOData[lcOffset]); } -bool ImageLoaderMachO::isDylib() const +const char* ImageLoaderMachO::segName(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( mh->filetype == MH_DYLIB ); + return segLoadCommand(segIndex)->segname; } -bool ImageLoaderMachO::forceFlat() const + +uintptr_t ImageLoaderMachO::segSize(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_FORCE_FLAT) != 0 ); + return segLoadCommand(segIndex)->vmsize; } -bool ImageLoaderMachO::usesTwoLevelNameSpace() const + +uintptr_t ImageLoaderMachO::segFileSize(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_TWOLEVEL) != 0 ); + return segLoadCommand(segIndex)->filesize; } -bool ImageLoaderMachO::isPrebindable() const + +bool ImageLoaderMachO::segHasTrailingZeroFill(unsigned int segIndex) { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_PREBOUND) != 0 ); + return ( segWriteable(segIndex) && (segSize(segIndex) > segFileSize(segIndex)) ); } -bool ImageLoaderMachO::hasCoalescedExports() const + +uintptr_t ImageLoaderMachO::segFileOffset(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_WEAK_DEFINES) != 0 ); + return segLoadCommand(segIndex)->fileoff; } -bool ImageLoaderMachO::needsCoalescing() const + +bool ImageLoaderMachO::segReadable(unsigned int segIndex) const { - const macho_header* mh = (macho_header*)fMachOData; - return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_READ) != 0); } +bool ImageLoaderMachO::segWriteable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_WRITE) != 0); +} +bool ImageLoaderMachO::segExecutable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_EXECUTE) != 0); +} -// hack until kernel headers and glue are in system -struct _shared_region_mapping_np { - mach_vm_address_t address; - mach_vm_size_t size; - mach_vm_offset_t file_offset; - vm_prot_t max_prot; /* read/write/execute/COW/ZF */ - vm_prot_t init_prot; /* read/write/execute/COW/ZF */ -}; -struct _shared_region_range_np { - mach_vm_address_t address; - mach_vm_size_t size; -}; - -#if SPLIT_SEG_SHARED_REGION_SUPPORT -// Called by dyld. -// Requests the kernel to map a number of regions from the fd into the -// shared sections address range (0x90000000-0xAFFFFFFF). -// If shared_region_make_private_np() has not been called by this process, -// the file mapped in is seen in the address space of all processes that -// participate in using the shared region. -// If shared_region_make_private_np() _has_ been called by this process, -// the file mapped in is only seen by this process. -// If the slide parameter is not NULL and then regions cannot be mapped -// as requested, the kernel will try to map the file in at a different -// address in the shared region and return the distance slid. -// If the mapping requesting cannot be fulfilled, returns non-zero. -static int -_shared_region_map_file_np( - int fd, // file descriptor to map into shared region - unsigned int regionCount, // number of entres in array of regions - const _shared_region_mapping_np regions[], // the array of regions to map - uint64_t* slide) // the amount all regions were slid, NULL means don't attempt to slide -{ - //dyld::log("%s(%i, %u, %8p, %8p)\n", __func__, fd, regionCount, regions, slide); - //for ( unsigned int i=0; i < regionCount; ++i) { - // dyld::log("\taddress=0x%08llX, size=0x%08llX\n", regions[i].address, regions[i].size); - //} - int r = syscall(299, fd, regionCount, regions, slide); -// if(0 != r) -// dyld::log("%s(%i, %u, %8p, %8p) errno=%i (%s)\n", __func__, fd, regionCount, regions, slide, errno, strerror(errno)); - return r; -} -// Called by dyld if shared_region_map_file() fails. -// Requests the kernel to take this process out of using the shared region. -// The specified ranges are created as private copies from the shared region for this process. -static int -_shared_region_make_private_np( - unsigned int rangeCount, // number of entres in array of msrp_range - const _shared_region_range_np ranges[]) // the array of shared regions to make private -{ - //dyld::log("%s(%u, %8p)\n", __func__, rangeCount, ranges); - int r = syscall(300, rangeCount, ranges); -// if(0 != r) -// dyld::log("%s(%u, %8p) errno=%i (%s)\n", __func__, rangeCount, ranges, errno, strerror(errno)); - return r; -} -#define KERN_SHREG_PRIVATIZABLE 54 - - -static int -_shared_region_map_file_with_mmap( - int fd, // file descriptor to map into shared region - unsigned int regionCount, // number of entres in array of regions - const _shared_region_mapping_np regions[]) // the array of regions to map -{ - // map in each region - for(unsigned int i=0; i < regionCount; ++i) { - void* mmapAddress = (void*)(uintptr_t)(regions[i].address); - size_t size = regions[i].size; - if ( (regions[i].init_prot & VM_PROT_ZF) != 0 ) { - // do nothing already vm_allocate() which zero fills - } - else { - int protection = 0; - if ( regions[i].init_prot & VM_PROT_EXECUTE ) - protection |= PROT_EXEC; - if ( regions[i].init_prot & VM_PROT_READ ) - protection |= PROT_READ; - if ( regions[i].init_prot & VM_PROT_WRITE ) - protection |= PROT_WRITE; - off_t offset = regions[i].file_offset; - //dyld::log("mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath); - mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); - if ( mmapAddress == ((void*)(-1)) ) - throw "mmap error"; - } - } - - return 0; +bool ImageLoaderMachO::segUnaccessible(unsigned int segIndex) const +{ + return (segLoadCommand(segIndex)->initprot == 0); } - -static -bool -hasSharedRegionMapFile(void) +bool ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int segIndex) const { - int mib[CTL_MAXNAME]; - int value = 0; - size_t size; + return (segLoadCommand(segIndex)->vmaddr != 0); +} - mib[0] = CTL_KERN; - mib[1] = KERN_SHREG_PRIVATIZABLE; - size = sizeof (int); - if (sysctl(mib, 2, &value, &size, NULL, 0) != 0) { - value = 0; - } +uintptr_t ImageLoaderMachO::segPreferredLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr; +} - return 0 != value; +uintptr_t ImageLoaderMachO::segActualLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr + fSlide; } -#endif // SPLIT_SEG_SHARED_REGION_SUPPORT +uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const +{ + return segActualLoadAddress(segIndex) + segSize(segIndex); +} -#if SPLIT_SEG_DYLIB_SUPPORT -unsigned int -ImageLoaderMachO::getExtraZeroFillEntriesCount() +bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const { - // calculate mapping entries - unsigned int extraZeroFillEntries = 0; - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( seg->hasTrailingZeroFill() ) - ++extraZeroFillEntries; - } - - return extraZeroFillEntries; -} - -void -ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, - _shared_region_mapping_np *mappingTable) -{ - unsigned int segmentCount = fSegmentsArrayCount; - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = &fSegmentsArray[segIndex]; - _shared_region_mapping_np* entry = &mappingTable[entryIndex]; - entry->address = seg->getActualLoadAddress(this); - entry->size = seg->getFileSize(); - entry->file_offset = seg->getFileOffset() + offsetInFat; - entry->init_prot = VM_PROT_NONE; - if ( !seg->unaccessible() ) { - if ( seg->executable() ) - entry->init_prot |= VM_PROT_EXECUTE; - if ( seg->readable() ) - entry->init_prot |= VM_PROT_READ; - if ( seg->writeable() ) - entry->init_prot |= VM_PROT_WRITE | VM_PROT_COW; - } - entry->max_prot = entry->init_prot; - if ( seg->hasTrailingZeroFill() ) { - _shared_region_mapping_np* zfentry = &mappingTable[++entryIndex]; - zfentry->address = entry->address + seg->getFileSize(); - zfentry->size = seg->getSize() - seg->getFileSize(); - zfentry->file_offset = 0; - zfentry->init_prot = entry->init_prot | VM_PROT_COW | VM_PROT_ZF; - zfentry->max_prot = zfentry->init_prot; - } + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) + return true; } + return false; } -int -ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, - uint64_t offsetInFat, - uint64_t lenInFat, - uint64_t fileLen, - const LinkContext& context) +bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const { - static uintptr_t sNextAltLoadAddress - #if __ppc_ - = 0xC0000000; - #else - = 0; - #endif + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) + return true; + } + return false; +} - const unsigned int segmentCount = fSegmentsArrayCount; - const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); - const unsigned int regionCount = segmentCount+extraZeroFillEntries; - _shared_region_mapping_np regions[regionCount]; - initMappingTable(offsetInFat, regions); - int r = -1; - // find space somewhere to allocate split seg - bool foundRoom = false; - vm_size_t biggestDiff = 0; - while ( ! foundRoom ) { - foundRoom = true; - for(unsigned int i=0; i < regionCount; ++i) { - vm_address_t addr = sNextAltLoadAddress + regions[i].address - regions[0].address; - vm_size_t size = regions[i].size ; - r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); - if ( 0 != r ) { - // no room here, deallocate what has succeeded so far - for(unsigned int j=0; j < i; ++j) { - vm_address_t addr = sNextAltLoadAddress + regions[j].address - regions[0].address; - vm_size_t size = regions[j].size ; - (void)vm_deallocate(mach_task_self(), addr, size); - } - sNextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again - if ( (sNextAltLoadAddress & 0xF0000000) == 0x90000000 ) - sNextAltLoadAddress = 0xB0000000; - if ( (sNextAltLoadAddress & 0xF0000000) == 0xF0000000 ) - throw "can't map split seg anywhere"; - foundRoom = false; - break; - } - vm_size_t high = (regions[i].address + size - regions[0].address) & 0x0FFFFFFF; - if ( high > biggestDiff ) - biggestDiff = high; - } - } - - // map in each region - uintptr_t slide = sNextAltLoadAddress - regions[0].address; - this->setSlide(slide); - for(unsigned int i=0; i < regionCount; ++i) { - if ( ((regions[i].init_prot & VM_PROT_ZF) != 0) || (regions[i].size == 0) ) { - // nothing to mmap for zero-fills areas, they are just vm_allocated - } - else { - void* mmapAddress = (void*)(uintptr_t)(regions[i].address + slide); - size_t size = regions[i].size; - int protection = 0; - if ( regions[i].init_prot & VM_PROT_EXECUTE ) - protection |= PROT_EXEC; - if ( regions[i].init_prot & VM_PROT_READ ) - protection |= PROT_READ; - if ( regions[i].init_prot & VM_PROT_WRITE ) - protection |= PROT_WRITE; - off_t offset = regions[i].file_offset; - //dyld::log("mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath); - mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); - if ( mmapAddress == ((void*)(-1)) ) - throw "mmap error"; - } - } - // set so next maps right after this one - sNextAltLoadAddress += biggestDiff; - sNextAltLoadAddress = (sNextAltLoadAddress + 4095) & (-4096); - - // logging - if ( context.verboseMapping ) { - dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = &fSegmentsArray[segIndex]; - const _shared_region_mapping_np* entry = ®ions[entryIndex]; - if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - dyld::log("%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getFileSize()-1); - if ( entryIndex < (regionCount-1) ) { - const _shared_region_mapping_np* nextEntry = ®ions[entryIndex+1]; - if ( (nextEntry->init_prot & VM_PROT_ZF) != 0 ) { - uint64_t segOffset = nextEntry->address - entry->address; - dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset + nextEntry->size - 1)); - ++entryIndex; - } - } - } - } - - return r; +#if __i386__ +bool ImageLoaderMachO::segIsReadOnlyImport(unsigned int segIndex) const +{ + const macho_segment_command* segCmd = segLoadCommand(segIndex); + return ( (segCmd->initprot & VM_PROT_EXECUTE) + && ((segCmd->initprot & VM_PROT_WRITE) == 0) + && (strcmp(segCmd->segname, "__IMPORT") == 0) ); } +#endif -void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +void ImageLoaderMachO::UnmapSegments() { - // non-split segment libraries handled by super class - if ( !fIsSplitSeg ) - return ImageLoader::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); - -#if SPLIT_SEG_SHARED_REGION_SUPPORT - enum SharedRegionState - { - kSharedRegionStartState = 0, - kSharedRegionMapFileState, - kSharedRegionMapFilePrivateState, - kSharedRegionMapFilePrivateMMapState, - kSharedRegionMapFilePrivateOutsideState, - }; - static SharedRegionState sSharedRegionState = kSharedRegionStartState; - - if ( kSharedRegionStartState == sSharedRegionState ) { - if ( hasSharedRegionMapFile() ) { - if ( context.sharedRegionMode == kUsePrivateSharedRegion ) { - sharedRegionMakePrivate(context); - sSharedRegionState = kSharedRegionMapFilePrivateState; - } - else if ( context.sharedRegionMode == kDontUseSharedRegion ) { - sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; - } - else if ( context.sharedRegionMode == kSharedRegionIsSharedCache ) { - sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; + // usually unmap image when done + if ( ! this->leaveMapped() && (this->getState() >= dyld_image_state_mapped) ) { + // unmap TEXT segment last because it contains load command being inspected + unsigned int textSegmentIndex = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + //dyld::log("unmap %s at 0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this)); + if ( strcmp(segName(i), "__TEXT") == 0 ) { + textSegmentIndex = i; } else { - sSharedRegionState = kSharedRegionMapFileState; + // update stats + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(i); + munmap((void*)segActualLoadAddress(i), segSize(i)); } } - else { - sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; - } - } - - if ( kSharedRegionMapFileState == sSharedRegionState ) { - if ( 0 != sharedRegionMapFile(fd, offsetInFat, lenInFat, fileLen, context) ) { - sharedRegionMakePrivate(context); - sSharedRegionState = kSharedRegionMapFilePrivateState; - } + // now unmap TEXT + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(textSegmentIndex); + munmap((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); } - - if ( (kSharedRegionMapFilePrivateState == sSharedRegionState) || (kSharedRegionMapFilePrivateMMapState == sSharedRegionState) ) { - if ( 0 != sharedRegionMapFilePrivate(fd, offsetInFat, lenInFat, fileLen, context, (kSharedRegionMapFilePrivateMMapState == sSharedRegionState)) ) { - sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; - } - } - - if ( kSharedRegionMapFilePrivateOutsideState == sSharedRegionState ) { - if ( 0 != sharedRegionMapFilePrivateOutside(fd, offsetInFat, lenInFat, fileLen, context) ) { - throw "mapping error"; +} + + +// prefetch __DATA/__OBJC pages during launch, but not for dynamically loaded code +void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context) +{ + if ( context.linkingMainExecutable ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( segWriteable(i) && (segFileSize(i) > 0) ) { + // prefetch writable segment that have mmap'ed regions + radvisory advice; + advice.ra_offset = offsetInFat + segFileOffset(i); + advice.ra_count = segFileSize(i); + // limit prefetch to 1MB (256 pages) + if ( advice.ra_count > 1024*1024 ) + advice.ra_count = 1024*1024; + // don't prefetch single pages, let them fault in + fgTotalBytesPreFetched += advice.ra_count; + fcntl(fd, F_RDADVISE, &advice); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", + segName(i), segActualLoadAddress(i), segActualLoadAddress(i)+advice.ra_count-1); + } + } } } -#else - // support old split-seg dylibs by mapping them where ever we find space - if ( sharedRegionMapFilePrivateOutside(fd, offsetInFat, lenInFat, fileLen, context) != 0 ) { - throw "mapping error"; - } -#endif } -#endif // SPLIT_SEG_DYLIB_SUPPORT -#if SPLIT_SEG_SHARED_REGION_SUPPORT -int ImageLoaderMachO::sharedRegionMakePrivate(const LinkContext& context) +bool ImageLoaderMachO::segmentsMustSlideTogether() const { - if ( context.verboseMapping ) - dyld::log("dyld: making shared regions private\n"); - - // shared mapping failed, so make private copy of shared region and try mapping private - MappedRegion allRegions[context.imageCount()*8]; // assume average of less that eight segments per image - MappedRegion* end = context.getAllMappedRegions(allRegions); - _shared_region_range_np splitSegRegions[end-allRegions]; - _shared_region_range_np* sp = splitSegRegions; - for (MappedRegion* p=allRegions; p < end; ++p) { - uint8_t highByte = p->address >> 28; - if ( (highByte == 9) || (highByte == 0xA) ) { - _shared_region_range_np splitRegion; - splitRegion.address = p->address; - splitRegion.size = p->size; - *sp++ = splitRegion; - } - } - int result = _shared_region_make_private_np(sp-splitSegRegions, splitSegRegions); - // notify gdb or other lurkers that this process is no longer using the shared region - dyld_all_image_infos.processDetachedFromSharedRegion = true; - return result; + return true; } -int -ImageLoaderMachO::sharedRegionMapFile(int fd, - uint64_t offsetInFat, - uint64_t lenInFat, - uint64_t fileLen, - const LinkContext& context) -{ - // build table of segments to map - const unsigned int segmentCount = fSegmentsArrayCount; - const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); - const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; - _shared_region_mapping_np mappingTable[mappingTableCount]; - initMappingTable(offsetInFat, mappingTable); -// uint64_t slide; - uint64_t *slidep = NULL; - - // try to map it in shared - int r = _shared_region_map_file_np(fd, mappingTableCount, mappingTable, slidep); - if ( 0 == r ) { - if(NULL != slidep && 0 != *slidep) { - // update with actual load addresses - } - this->setNeverUnload(); - if ( context.verboseMapping ) { - dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = &fSegmentsArray[segIndex]; - const _shared_region_mapping_np* entry = &mappingTable[entryIndex]; - if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - dyld::log("%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getFileSize()-1); - if ( entryIndex < (mappingTableCount-1) ) { - const _shared_region_mapping_np* nextEntry = &mappingTable[entryIndex+1]; - if ( (nextEntry->init_prot & VM_PROT_ZF) != 0 ) { - uint64_t segOffset = nextEntry->address - entry->address; - dyld::log("%18s at 0x%08lX->0x%08lX\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset + nextEntry->size - 1)); - ++entryIndex; - } - } - } - } - } - return r; +bool ImageLoaderMachO::segmentsCanSlide() const +{ + return (this->isDylib() || this->isBundle() || this->isPositionIndependentExecutable()); } - -int -ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, - uint64_t offsetInFat, - uint64_t lenInFat, - uint64_t fileLen, - const LinkContext& context, - bool usemmap) +bool ImageLoaderMachO::isBundle() const { - const unsigned int segmentCount = fSegmentsArrayCount; + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_BUNDLE ); +} - // build table of segments to map - const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); - const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; - _shared_region_mapping_np mappingTable[mappingTableCount]; - initMappingTable(offsetInFat, mappingTable); - uint64_t slide = 0; +bool ImageLoaderMachO::isDylib() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_DYLIB ); +} - // try map it in privately (don't allow sliding if we pre-calculated the load address to pack dylibs) - int r; - if ( usemmap ) - r = _shared_region_map_file_with_mmap(fd, mappingTableCount, mappingTable); - else - r = _shared_region_map_file_np(fd, mappingTableCount, mappingTable, &slide); - if ( 0 == r ) { - if ( 0 != slide ) { - slide = (slide) & (-4096); // round down to page boundary - this->setSlide(slide); - } - this->setNeverUnload(); - if ( context.verboseMapping ) { - if ( slide == 0 ) - dyld::log("dyld: Mapping split-seg un-shared %s\n", this->getPath()); - else - dyld::log("dyld: Mapping split-seg un-shared slid by 0x%08llX %s\n", slide, this->getPath()); - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = &fSegmentsArray[segIndex]; - const _shared_region_mapping_np* entry = &mappingTable[entryIndex]; - if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - dyld::log("%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(this), seg->getActualLoadAddress(this)+seg->getFileSize()-1); - if ( entryIndex < (mappingTableCount-1) ) { - const _shared_region_mapping_np* nextEntry = &mappingTable[entryIndex+1]; - if ( (nextEntry->init_prot & VM_PROT_ZF) != 0 ) { - uint64_t segOffset = nextEntry->address - entry->address; - dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset), (uintptr_t)(seg->getActualLoadAddress(this) + segOffset + nextEntry->size - 1)); - ++entryIndex; - } - } - } - } - } - if ( r != 0 ) - dyld::throwf("can't rebase split-seg dylib %s because shared_region_map_file_np() returned %d", this->getPath(), r); - - return r; -} - -void -ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, - sf_mapping *mappingTable, - uintptr_t baseAddress) -{ - unsigned int segmentCount = fSegmentsArrayCount; - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = &fSegmentsArray[segIndex]; - sf_mapping* entry = &mappingTable[entryIndex]; - entry->mapping_offset = seg->getPreferredLoadAddress() - baseAddress; - entry->size = seg->getFileSize(); - entry->file_offset = seg->getFileOffset() + offsetInFat; - entry->protection = VM_PROT_NONE; - if ( !seg->unaccessible() ) { - if ( seg->executable() ) - entry->protection |= VM_PROT_EXECUTE; - if ( seg->readable() ) - entry->protection |= VM_PROT_READ; - if ( seg->writeable() ) - entry->protection |= VM_PROT_WRITE | VM_PROT_COW; - } - - entry->cksum = 0; - if ( seg->hasTrailingZeroFill() ) { - sf_mapping* zfentry = &mappingTable[++entryIndex]; - zfentry->mapping_offset = entry->mapping_offset + seg->getFileSize(); - zfentry->size = seg->getSize() - seg->getFileSize(); - zfentry->file_offset = 0; - zfentry->protection = entry->protection | VM_PROT_COW | VM_PROT_ZF; - zfentry->cksum = 0; - } - } +bool ImageLoaderMachO::isExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_EXECUTE ); } -#endif // SPLIT_SEG_SHARED_REGION_SUPPORT +bool ImageLoaderMachO::isPositionIndependentExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->filetype == MH_EXECUTE) && ((mh->flags & MH_PIE) != 0) ); +} +bool ImageLoaderMachO::forceFlat() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_FORCE_FLAT) != 0 ); +} -void ImageLoaderMachO::setSlide(intptr_t slide) +bool ImageLoaderMachO::usesTwoLevelNameSpace() const { - fSlide = slide; + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_TWOLEVEL) != 0 ); } -void ImageLoaderMachO::parseLoadCmds() +bool ImageLoaderMachO::isPrebindable() const { - // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide - for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - // set up pointer to __LINKEDIT segment - if ( strcmp(seg->getName(),"__LINKEDIT") == 0 ) - fLinkEditBase = (uint8_t*)(seg->getActualLoadAddress(this) - seg->getFileOffset()); -#if TEXT_RELOC_SUPPORT - // __TEXT segment always starts at beginning of file and contains mach_header and load commands - if ( strcmp(seg->getName(),"__TEXT") == 0 ) { - if ( ((SegmentMachO*)seg)->hasFixUps() ) - fTextSegmentWithFixups = (SegmentMachO*)seg; - } -#endif -#if __i386__ - if ( seg->readOnlyImportStubs() ) - fReadOnlyImportSegment = (SegmentMachO*)seg; -#endif - // some segment always starts at beginning of file and contains mach_header and load commands - if ( (seg->getFileOffset() == 0) && (seg->getFileSize() != 0) ) { - fMachOData = (uint8_t*)(seg->getActualLoadAddress(this)); - } - #if __ppc64__ - // in 10.5 to support images that span 4GB (including pagezero) switch meaning of r_address - if ( ((seg->getPreferredLoadAddress() + seg->getSize() - fSegmentsArray[0].getPreferredLoadAddress()) > 0x100000000) - && seg->writeable() ) - f4GBWritable = true; - #endif - } - - // keep count of prebound images with weak exports - if ( this->hasCoalescedExports() ) - ++fgCountOfImagesWithWeakExports; + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_PREBOUND) != 0 ); +} - // keep count of images used in shared cache - if ( fInSharedCache ) - ++fgImagesUsedFromSharedCache; +bool ImageLoaderMachO::hasCoalescedExports() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_WEAK_DEFINES) != 0 ); +} - // walk load commands (mapped in at start of __TEXT segment) - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SYMTAB: - { - const struct symtab_command* symtab = (struct symtab_command*)cmd; - fStrings = (const char*)&fLinkEditBase[symtab->stroff]; - fSymbolTable = (struct macho_nlist*)(&fLinkEditBase[symtab->symoff]); - } - break; - case LC_DYSYMTAB: - fDynamicInfo = (struct dysymtab_command*)cmd; - break; - case LC_SUB_UMBRELLA: - fHasSubUmbrella = true; - break; - case LC_SUB_FRAMEWORK: - fInUmbrella = true; - break; - case LC_SUB_LIBRARY: - fHasSubLibraries = true; - break; - case LC_ROUTINES_COMMAND: - fHasDashInit = true; - break; - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; -#if IMAGE_NOTIFY_SUPPORT - const bool isDataSeg = (strcmp(seg->segname, "__DATA") == 0); -#endif - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - const uint8_t type = sect->flags & SECTION_TYPE; - if ( type == S_MOD_INIT_FUNC_POINTERS ) - fHasInitializers = true; - else if ( type == S_MOD_TERM_FUNC_POINTERS ) - fHasTerminators = true; - else if ( type == S_DTRACE_DOF ) - fHasDOFSections = true; -#if IMAGE_NOTIFY_SUPPORT - else if ( isDataSeg && (strcmp(sect->sectname, "__image_notify") == 0) ) - fHasImageNotifySection = true; -#endif - } - } - break; - case LC_TWOLEVEL_HINTS: - fTwoLevelHints = (struct twolevel_hints_command*)cmd; - break; - case LC_ID_DYLIB: - { - fDylibID = (struct dylib_command*)cmd; - } - break; - case LC_RPATH: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - // do nothing, just prevent LC_REQ_DYLD exception from occuring - break; - default: - if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) - dyld::throwf("unknown required load command 0x%08X", cmd->cmd); - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } +bool ImageLoaderMachO::hasReferencesToWeakSymbols() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); } +bool ImageLoaderMachO::participatesInCoalescing() const +{ + const macho_header* mh = (macho_header*)fMachOData; + // if image is loaded with RTLD_LOCAL, then its symbols' visibility + // is reduced and it can't coalesce with other images + if ( this->hasHiddenExports() ) + return false; + return ( (mh->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) != 0 ); +} -const char* ImageLoaderMachO::getInstallPath() const +void ImageLoaderMachO::setSlide(intptr_t slide) { - if ( fDylibID != NULL ) { - return (char*)fDylibID + fDylibID->dylib.name.offset; - } - return NULL; + fSlide = slide; } -// test if this image is re-exported through parent (the image that loaded this one) -bool ImageLoaderMachO::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const +#if CODESIGNING_SUPPORT +void ImageLoaderMachO::loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile) { - if ( fInUmbrella ) { - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if (cmd->cmd == LC_SUB_FRAMEWORK) { - const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; - const char* exportThruName = (char*)cmd + subf->umbrella.offset; - // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent... - const char* parentInstallPath = parent->getInstallPath(); - if ( parentInstallPath != NULL ) { - const char* lastSlash = strrchr(parentInstallPath, '/'); - if ( lastSlash != NULL ) { - if ( strcmp(&lastSlash[1], exportThruName) == 0 ) - return true; - if ( context.imageSuffix != NULL ) { - // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end - char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1]; - strcpy(reexportAndSuffix, exportThruName); - strcat(reexportAndSuffix, context.imageSuffix); - if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) - return true; - } - } + // 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; } } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + 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); } - return false; } +#endif -// test if child is re-exported -bool ImageLoaderMachO::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const -{ - if ( fHasSubLibraries ) { - // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child... - const char* childInstallPath = child->getInstallPath(); - if ( childInstallPath != NULL ) { - const char* lastSlash = strrchr(childInstallPath, '/'); - if ( lastSlash != NULL ) { - const char* firstDot = strchr(lastSlash, '.'); - int len; - if ( firstDot == NULL ) - len = strlen(lastSlash); - else - len = firstDot-lastSlash-1; - char childLeafName[len+1]; - strncpy(childLeafName, &lastSlash[1], len); - childLeafName[len] = '\0'; - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SUB_LIBRARY: - { - const struct sub_library_command* lib = (struct sub_library_command*)cmd; - const char* aSubLibName = (char*)cmd + lib->sub_library.offset; - if ( strcmp(aSubLibName, childLeafName) == 0 ) - return true; - if ( context.imageSuffix != NULL ) { - // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end - char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1]; - strcpy(aSubLibNameAndSuffix, aSubLibName); - strcat(aSubLibNameAndSuffix, context.imageSuffix); - if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 ) - return true; - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } - } - } - if ( fHasSubUmbrella ) { - // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child... - const char* childInstallPath = child->getInstallPath(); - if ( childInstallPath != NULL ) { - const char* lastSlash = strrchr(childInstallPath, '/'); - if ( lastSlash != NULL ) { - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SUB_UMBRELLA: - { - const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd; - const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset; - if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 ) - return true; - if ( context.imageSuffix != NULL ) { - // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end - char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1]; - strcpy(umbrellaAndSuffix, aSubUmbrellaName); - strcat(umbrellaAndSuffix, context.imageSuffix); - if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 ) - return true; - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } - } + +const char* ImageLoaderMachO::getInstallPath() const +{ + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + return (char*)dylibID + dylibID->dylib.name.offset; } - return false; + return NULL; } @@ -1304,7 +679,7 @@ void* ImageLoaderMachO::getMain() const const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { + for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_UNIXTHREAD: { @@ -1320,6 +695,9 @@ void* ImageLoaderMachO::getMain() const #elif __x86_64__ const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); return (void*)(registers->rip + fSlide); + #elif __arm__ + const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16); + return (void*)(registers->__pc + fSlide); #else #warning need processor specific code #endif @@ -1331,61 +709,90 @@ void* ImageLoaderMachO::getMain() const return NULL; } - -uint32_t ImageLoaderMachO::doGetDependentLibraryCount() +bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh) { - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - uint32_t count = 0; + // ensure that every image depends on something which depends on libSystem + if ( libCount > 1 ) + return false; + + // dyld implicit-libSystem breaks valgrind + if ( mh->filetype == MH_EXECUTE ) + return false; + + bool isNonOSdylib = false; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh+sizeof(macho_header)); const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { + for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_REEXPORT_DYLIB: - ++count; + return false; + case LC_ID_DYLIB: + { + const dylib_command* dylibID = (dylib_command*)cmd; + const char* installPath = (char*)cmd + dylibID->dylib.name.offset; + // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents + // but all other dylibs must depend on libSystem for initialization to initialize libSystem first + // rosetta circular dependency spew + isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) ); + } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - return count; + return isNonOSdylib; } + void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) { - uint32_t index = 0; - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_LOAD_DYLIB: - case LC_LOAD_WEAK_DYLIB: - case LC_REEXPORT_DYLIB: - { - const struct dylib_command* dylib = (struct dylib_command*)cmd; - DependentLibraryInfo* lib = &libs[index++]; - lib->name = (char*)cmd + dylib->dylib.name.offset; - //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); - lib->info.checksum = dylib->dylib.timestamp; - lib->info.minVersion = dylib->dylib.compatibility_version; - lib->info.maxVersion = dylib->dylib.current_version; - lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); - lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); + if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { + DependentLibraryInfo* lib = &libs[0]; + lib->name = "/usr/lib/libSystem.B.dylib"; + lib->info.checksum = 0; + lib->info.minVersion = 0; + lib->info.maxVersion = 0; + lib->required = false; + lib->reExported = false; + } + else { + uint32_t index = 0; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + { + const struct dylib_command* dylib = (struct dylib_command*)cmd; + DependentLibraryInfo* lib = &libs[index++]; + lib->name = (char*)cmd + dylib->dylib.name.offset; + //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); + lib->info.checksum = dylib->dylib.timestamp; + lib->info.minVersion = dylib->dylib.compatibility_version; + lib->info.maxVersion = dylib->dylib.current_version; + lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); + lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); + } + break; } - break; + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } } ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo() { LibraryInfo info; - if ( fDylibID != NULL ) { - info.minVersion = fDylibID->dylib.compatibility_version; - info.maxVersion = fDylibID->dylib.current_version; - info.checksum = fDylibID->dylib.timestamp; + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + info.minVersion = dylibID->dylib.compatibility_version; + info.maxVersion = dylibID->dylib.current_version; + info.checksum = dylibID->dylib.timestamp; } else { info.minVersion = 0; @@ -1400,13 +807,13 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; - for (unsigned long i = 0; i < cmd_count; ++i) { + for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_RPATH: const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset; if ( strncmp(path, "@loader_path/", 13) == 0 ) { - if ( issetugid() && (context.mainExecutable == this) ) { - dyld::warn("LC_RPATH %s in %s being ignored in setuid program because of @loader_path\n", path, this->getPath()); + if ( context.processIsRestricted && (context.mainExecutable == this) ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); break; } char resolvedPath[PATH_MAX]; @@ -1422,8 +829,8 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); + if ( context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath()); break; } char resolvedPath[PATH_MAX]; @@ -1438,108 +845,59 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); + else if ( (path[0] != '/') && context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath()); break; } - else { - // make copy so that all elements of 'paths' can be freed - path = strdup(path); - } - paths.push_back(path); - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } -} - -uintptr_t ImageLoaderMachO::getFirstWritableSegmentAddress() -{ - // in split segment libraries r_address is offset from first writable segment - for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - if ( seg->writeable() ) - return seg->getActualLoadAddress(this); - } - throw "no writable segment"; -} - -uintptr_t ImageLoaderMachO::getRelocBase() -{ - // r_address is either an offset from the first segment address - // or from the first writable segment address -#if __ppc__ || __i386__ - if ( fIsSplitSeg ) - return getFirstWritableSegmentAddress(); - else - return fSegmentsArray[0].getActualLoadAddress(this); -#elif __ppc64__ - if ( f4GBWritable ) - return getFirstWritableSegmentAddress(); - else - return fSegmentsArray[0].getActualLoadAddress(this); -#elif __x86_64__ - return getFirstWritableSegmentAddress(); -#endif -} - - -#if __ppc__ -static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationType, uint16_t otherHalf, uintptr_t slide) -{ - // low 16 bits of 32-bit ppc instructions need fixing - struct ppcInstruction { uint16_t opcode; int16_t immediateValue; }; - ppcInstruction* instruction = (ppcInstruction*)locationToFix; - //uint32_t before = *((uint32_t*)locationToFix); - switch ( relocationType ) - { - case PPC_RELOC_LO16: - instruction->immediateValue = ((otherHalf << 16) | instruction->immediateValue) + slide; - break; - case PPC_RELOC_HI16: - instruction->immediateValue = ((((instruction->immediateValue << 16) | otherHalf) + slide) >> 16); - break; - case PPC_RELOC_HA16: - int16_t signedOtherHalf = (int16_t)(otherHalf & 0xffff); - uint32_t temp = (instruction->immediateValue << 16) + signedOtherHalf + slide; - if ( (temp & 0x00008000) != 0 ) - temp += 0x00008000; - instruction->immediateValue = temp >> 16; + else if ( (path[0] == '/') && (context.rootPaths != NULL) ) { + // DYLD_ROOT_PATH should apply to LC_RPATH rpaths + // DYLD_ROOT_PATH can be a list of paths, but at this point we can only support one, so use first combination that exists + bool found = false; + for(const char** rp = context.rootPaths; *rp != NULL; ++rp) { + char newPath[PATH_MAX]; + strcpy(newPath, *rp); + strcat(newPath, path); + struct stat stat_buf; + if ( stat(newPath, &stat_buf) != -1 ) { + //dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath); + path = strdup(newPath); + found = true; + break; + } + } + if ( ! found ) { + // make copy so that all elements of 'paths' can be freed + path = strdup(path); + } + } + else { + // make copy so that all elements of 'paths' can be freed + path = strdup(path); + } + paths.push_back(path); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - //uint32_t after = *((uint32_t*)locationToFix); - //dyld::log("dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after); } -#endif -#if __ppc__ || __i386__ -void ImageLoaderMachO::resetPreboundLazyPointers(const LinkContext& context, uintptr_t relocBase) -{ - // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values - register const uintptr_t slide = this->fSlide; - const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); - const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; - for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address & R_SCATTERED) != 0 ) { - const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; - if (sreloc->r_length == RELOC_SIZE) { - uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); - switch(sreloc->r_type) { - #if __ppc__ - case PPC_RELOC_PB_LA_PTR: - *locationToFix = sreloc->r_value + slide; - break; - #endif - #if __i386__ - case GENERIC_RELOC_PB_LA_PTR: - *locationToFix = sreloc->r_value + slide; - break; - #endif - } - } +bool ImageLoaderMachO::getUUID(uuid_t uuid) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UUID: + uuid_command* uc = (uuid_command*)cmd; + memcpy(uuid, uc->uuid, 16); + return true; } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + bzero(uuid, 16); + return false; } -#endif void ImageLoaderMachO::doRebase(const LinkContext& context) { @@ -1549,7 +907,7 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) ++fgImagesWithUsedPrebinding; // bump totals for statistics return; } - + // print why prebinding was not used if ( context.verbosePrebinding ) { if ( !this->isPrebindable() ) { @@ -1569,375 +927,141 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) } } - // cache values that are used in the following loop - const uintptr_t relocBase = this->getRelocBase(); - register const uintptr_t slide = this->fSlide; - //dyld::log("slide=0x%08lX for %s\n", slide, this->getPath()); -#if __ppc__ || __i386__ +#if PREBOUND_IMAGE_SUPPORT // if prebound and we got here, then prebinding is not valid, so reset all lazy pointers // if this image is in the shared cache, do not reset, they will be bound in doBind() if ( this->isPrebindable() && !fInSharedCache ) - this->resetPreboundLazyPointers(context, relocBase); + this->resetPreboundLazyPointers(context); #endif - // if in shared cache and got here, then we depend on something not in the shared cache - if ( fInSharedCache ) - context.notifySharedCacheInvalid(); - // if loaded at preferred address, no rebasing necessary - if ( slide == 0 ) + if ( this->fSlide == 0 ) return; #if TEXT_RELOC_SUPPORT // if there are __TEXT fixups, temporarily make __TEXT writable - if ( fTextSegmentWithFixups != NULL ) - fTextSegmentWithFixups->tempWritable(context, this); + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, true); #endif - // loop through all local (internal) relocation records - const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); - const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; - for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { -#if LINKEDIT_USAGE_DEBUG - noteAccessedLinkEditAddress(reloc); -#endif - #if __x86_64__ - // only one kind of local relocation supported for x86_64 - if ( reloc->r_length != 3 ) - throw "bad local relocation length"; - if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) - throw "unknown local relocation type"; - if ( reloc->r_pcrel != 0 ) - throw "bad local relocation pc_rel"; - if ( reloc->r_extern != 0 ) - throw "extern relocation found with local relocations"; - *((uintptr_t*)(reloc->r_address + relocBase)) += slide; - #else - if ( (reloc->r_address & R_SCATTERED) == 0 ) { - if ( reloc->r_symbolnum == R_ABS ) { - // ignore absolute relocations - } - else if (reloc->r_length == RELOC_SIZE) { - switch(reloc->r_type) { - case GENERIC_RELOC_VANILLA: - *((uintptr_t*)(reloc->r_address + relocBase)) += slide; - break; - #if __ppc__ - case PPC_RELOC_HI16: - case PPC_RELOC_LO16: - case PPC_RELOC_HA16: - // some tools leave object file relocations in linked images - otherRelocsPPC((uintptr_t*)(reloc->r_address + relocBase), reloc->r_type, reloc[1].r_address, slide); - ++reloc; // these relocations come in pairs, skip next - break; - #endif - default: - throw "unknown local relocation type"; - } - } - else { - throw "bad local relocation length"; - } - } - else { - const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; - if (sreloc->r_length == RELOC_SIZE) { - uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); - switch(sreloc->r_type) { - case GENERIC_RELOC_VANILLA: - *locationToFix += slide; - break; - #if __ppc__ - case PPC_RELOC_HI16: - case PPC_RELOC_LO16: - case PPC_RELOC_HA16: - // Metrowerks compiler sometimes leaves object file relocations in linked images??? - ++reloc; // these relocations come in pairs, get next one - otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide); - break; - case PPC_RELOC_PB_LA_PTR: - // do nothing - break; - #elif __ppc64__ - case PPC_RELOC_PB_LA_PTR: - // needed for compatibility with ppc64 binaries built with the first ld64 - // which used PPC_RELOC_PB_LA_PTR relocs instead of GENERIC_RELOC_VANILLA for lazy pointers - *locationToFix += slide; - break; - #elif __i386__ - case GENERIC_RELOC_PB_LA_PTR: - // do nothing - break; - #endif - default: - throw "unknown local scattered relocation type"; - } - } - else { - throw "bad local scattered relocation length"; - } - } - #endif // x86_64 - } - + + // do actual rebasing + this->rebase(context); + #if TEXT_RELOC_SUPPORT // if there were __TEXT fixups, restore write protection - if ( fTextSegmentWithFixups != NULL ) { - fTextSegmentWithFixups->setPermissions(context,this); - sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(this), fTextSegmentWithFixups->getSize()); - } + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, false); + #endif - // update stats - fgTotalRebaseFixups += fDynamicInfo->nlocrel; } - -const struct macho_nlist* ImageLoaderMachO::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], - const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const +#if TEXT_RELOC_SUPPORT +void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) { - int32_t high = symbolCount-1; - int32_t mid = hintIndex; - - // handle out of range hint - if ( mid >= (int32_t)symbolCount ) { - mid = symbolCount/2; - ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs; - } - else { - ++ImageLoaderMachO::fgHintedBinaryTreeSearchs; - } - ++fgTotalBindImageSearches; - - //dyld::log("dyld: binarySearchWithToc for %s in %s\n", key, this->getShortName()); - - for (int32_t low = 0; low <= high; mid = (low+high)/2) { - const uint32_t index = toc[mid].symbol_index; - const struct macho_nlist* pivot = &symbols[index]; - const char* pivotStr = &stringPool[pivot->n_un.n_strx]; -#if LINKEDIT_USAGE_DEBUG - noteAccessedLinkEditAddress(&toc[mid]); - noteAccessedLinkEditAddress(pivot); - noteAccessedLinkEditAddress(pivotStr); -#endif - int cmp = astrcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - low = mid + 1; - } - else { - // key < pivot - high = mid - 1; + int textSegmentIndex = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( strcmp(segName(i), "__TEXT") == 0 ) { + textSegmentIndex = i; + break; } } - return NULL; -} -const struct macho_nlist* ImageLoaderMachO::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const -{ - // update stats - ++fgTotalBindImageSearches; - ++ImageLoaderMachO::fgUnhintedBinaryTreeSearchs; - - //dyld::log("dyld: binarySearch for %s in %s, stringpool=%p, symbols=%p, symbolCount=%u\n", - // key, this->getShortName(), stringPool, symbols, symbolCount); - - const struct macho_nlist* base = symbols; - for (uint32_t n = symbolCount; n > 0; n /= 2) { - const struct macho_nlist* pivot = &base[n/2]; - const char* pivotStr = &stringPool[pivot->n_un.n_strx]; -#if LINKEDIT_USAGE_DEBUG - noteAccessedLinkEditAddress(pivot); - noteAccessedLinkEditAddress(pivotStr); -#endif - int cmp = astrcmp(key, pivotStr); - if ( cmp == 0 ) - return pivot; - if ( cmp > 0 ) { - // key > pivot - // move base to symbol after pivot - base = &pivot[1]; - --n; - } - else { - // key < pivot - // keep same base - } + if ( writeable ) { + segMakeWritable(textSegmentIndex, context); + } + else { + segProtect(textSegmentIndex, context); + sys_icache_invalidate((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); } - return NULL; } +#endif -const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, const void* hint, bool searchReExports, const ImageLoader** foundIn) const +const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const { - const struct macho_nlist* sym = NULL; - const struct twolevel_hint* theHint = (struct twolevel_hint*)hint; - if ( fDynamicInfo->tocoff == 0 ) - sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym); - else { - uint32_t start = fDynamicInfo->nextdefsym; - if ( theHint != NULL ) - start = theHint->itoc; - if ( (theHint == NULL) || (theHint->isub_image == 0) ) { - sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], - fDynamicInfo->ntoc, start); - } - } - if ( sym != NULL ) { - if ( foundIn != NULL ) - *foundIn = (ImageLoader*)this; - - return (const Symbol*)sym; - } + // look in this image first + const ImageLoader::Symbol* result = this->findExportedSymbol(name, foundIn); + if ( result != NULL ) + return result; if ( searchReExports ) { - // hint might tell us to try a particular subimage - if ( (theHint != NULL) && (theHint->isub_image > 0) && (theHint->isub_image <= fLibrariesCount) ) { - // isub_image is an index into a list that is sorted non-rexported images first - uint32_t index = 0; - ImageLoader* target = NULL; - // pass one, only look at sub-frameworks - for (uint32_t i=0; i < fLibrariesCount; ++i) { - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.isSubFramework && (libInfo.image != NULL)) { - if ( ++index == theHint->isub_image ) { - target = libInfo.image; - break; - } - } - } - if (target != NULL) { - // pass two, only look at non-sub-framework-reexports - for (uint32_t i=0; i < fLibrariesCount; ++i) { - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.isReExported && !libInfo.isSubFramework && (libInfo.image != NULL) ) { - if ( ++index == theHint->isub_image ) { - target = libInfo.image; - break; - } - } + for(unsigned int i=0; i < libraryCount(); ++i){ + if ( libReExported(i) ) { + ImageLoader* image = libImage(i); + if ( image != NULL ) { + const Symbol* result = image->findExportedSymbol(name, searchReExports, foundIn); + if ( result != NULL ) + return result; } } - if (target != NULL) { - const Symbol* result = target->findExportedSymbol(name, NULL, searchReExports, foundIn); - if ( result != NULL ) - return result; - } - } - - // hint failed, try all sub images - // pass one, only look at sub-frameworks - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( (libInfo.image != NULL) && libInfo.isSubFramework ) { - const Symbol* result = libInfo.image->findExportedSymbol(name, NULL, searchReExports, foundIn); - if ( result != NULL ) - return result; - } - } - // pass two, only look at non-sub-framework-reexports - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( (libInfo.image != NULL) && libInfo.isReExported && !libInfo.isSubFramework ) { - const Symbol* result = libInfo.image->findExportedSymbol(name, NULL, searchReExports, foundIn); - if ( result != NULL ) - return result; - } } } - // last change: the hint is wrong (non-zero but actually in this image) - if ( (theHint != NULL) && (theHint->isub_image != 0) ) { - sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], - fDynamicInfo->ntoc, fDynamicInfo->nextdefsym); - if ( sym != NULL ) { - if ( foundIn != NULL ) - *foundIn = (ImageLoader*)this; - return (const Symbol*)sym; - } - } - return NULL; } - uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const { - return this->getSymbolAddress((const struct macho_nlist*)sym, requestor, context); + return this->getSymbolAddress(sym, requestor, context); } -uintptr_t ImageLoaderMachO::getSymbolAddress(const struct macho_nlist* sym, const ImageLoader* requestor, const LinkContext& context) const +uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const { - uintptr_t result = sym->n_value + fSlide; + uintptr_t result = exportedSymbolAddress(sym); return result; } ImageLoader::DefinitionFlags ImageLoaderMachO::getExportedSymbolInfo(const Symbol* sym) const { - const struct macho_nlist* nlistSym = (const struct macho_nlist*)sym; - if ( (nlistSym->n_desc & N_WEAK_DEF) != 0 ) + if ( exportedSymbolIsWeakDefintion(sym) ) return kWeakDefinition; - return kNoDefinitionOptions; + else + return kNoDefinitionOptions; } const char* ImageLoaderMachO::getExportedSymbolName(const Symbol* sym) const { - const struct macho_nlist* nlistSym = (const struct macho_nlist*)sym; - return &fStrings[nlistSym->n_un.n_strx]; + return exportedSymbolName(sym); } uint32_t ImageLoaderMachO::getExportedSymbolCount() const { - return fDynamicInfo->nextdefsym; + return exportedSymbolCount(); } const ImageLoader::Symbol* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index) const { - if ( index < fDynamicInfo->nextdefsym ) { - const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index]; - return (const ImageLoader::Symbol*)sym; - } - return NULL; + return exportedSymbolIndexed(index); } uint32_t ImageLoaderMachO::getImportedSymbolCount() const { - return fDynamicInfo->nundefsym; + return importedSymbolCount(); } const ImageLoader::Symbol* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index) const { - if ( index < fDynamicInfo->nundefsym ) { - const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index]; - return (const ImageLoader::Symbol*)sym; - } - return NULL; + return importedSymbolIndexed(index); } -ImageLoader::ReferenceFlags ImageLoaderMachO::geImportedSymbolInfo(const ImageLoader::Symbol* sym) const +ImageLoader::ReferenceFlags ImageLoaderMachO::getImportedSymbolInfo(const ImageLoader::Symbol* sym) const { - const struct macho_nlist* nlistSym = (const struct macho_nlist*)sym; ImageLoader::ReferenceFlags flags = kNoReferenceOptions; - if ( ((nlistSym->n_type & N_TYPE) == N_UNDF) && (nlistSym->n_value != 0) ) - flags |= ImageLoader::kTentativeDefinition; - if ( (nlistSym->n_desc & N_WEAK_REF) != 0 ) - flags |= ImageLoader::kWeakReference; return flags; } const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol* sym) const { - const struct macho_nlist* nlistSym = (const struct macho_nlist*)sym; - return &fStrings[nlistSym->n_un.n_strx]; + return importedSymbolName(sym); } @@ -1965,638 +1089,151 @@ bool ImageLoaderMachO::getSectionContent(const char* segmentName, const char* se } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + *start = NULL; + *length = 0; return false; } - -bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) -{ - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide(); - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( (unslidInteriorAddress >= seg->vmaddr) && (unslidInteriorAddress < (seg->vmaddr+seg->vmsize)) ) { - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) { - if ( segmentName != NULL ) - *segmentName = sect->segname; - if ( sectionName != NULL ) - *sectionName = sect->sectname; - if ( sectionOffset != NULL ) - *sectionOffset = unslidInteriorAddress - sect->addr; - return true; - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - return false; -} - - -bool ImageLoaderMachO::symbolRequiresCoalescing(const struct macho_nlist* symbol) -{ - // if a define and weak ==> coalesced - if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) ) - return true; - // if an undefine and not referencing a weak symbol ==> coalesced - if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) ) - return true; - - // regular symbol - return false; -} - - -static void __attribute__((noreturn)) throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn) -{ - dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn); -} - -uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, bool twoLevel, const ImageLoader** foundIn) -{ - ++fgTotalBindSymbolsResolved; - const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; - -#if LINKEDIT_USAGE_DEBUG - noteAccessedLinkEditAddress(undefinedSymbol); - noteAccessedLinkEditAddress(symbolName); -#endif - if ( context.bindFlat || !twoLevel ) { - // 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, this, context); - *foundIn = this; - return addr; - } - const Symbol* sym; - if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { - if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) - this->addDynamicReference(*foundIn); - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - } - // if a bundle is loaded privately the above will not find its exports - if ( this->isBundle() && this->hasHiddenExports() ) { - // look in self for needed symbol - sym = this->findExportedSymbol(symbolName, NULL, false, foundIn); - if ( sym != NULL ) - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - } - if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { - // definition can't be found anywhere - // if reference is weak_import, then it is ok, just return 0 - return 0; - } - throwSymbolNotFound(symbolName, this->getPath(), "flat namespace"); - } - else { - // symbol requires searching images with coalesced symbols (not done during prebinding) - if ( !context.prebinding && this->needsCoalescing() && symbolRequiresCoalescing(undefinedSymbol) ) { - const Symbol* sym; - if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { - if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) - this->addDynamicReference(*foundIn); - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - } - //throwSymbolNotFound(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, this, context); - *foundIn = this; - return addr; - } - - // two level lookup - void* hint = NULL; - ImageLoader* target = NULL; - uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc); - if ( ord == EXECUTABLE_ORDINAL ) { - target = context.mainExecutable; - } - else if ( ord == SELF_LIBRARY_ORDINAL ) { - target = this; - } - else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) { - // rnielsen: HACKHACK - // flat lookup - const Symbol* sym; - if ( context.flatExportFinder(symbolName, &sym, foundIn) ) - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - // no image has exports this symbol - // either report error or hope ZeroLink can just-in-time load an image - context.undefinedHandler(symbolName); - // try looking again - if ( context.flatExportFinder(symbolName, &sym, foundIn) ) - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - - throwSymbolNotFound(symbolName, this->getPath(), "dynamic lookup"); - } - else if ( ord <= fLibrariesCount ) { - DependentLibrary& libInfo = fLibraries[ord-1]; - target = libInfo.image; - if ( (target == NULL) && (((undefinedSymbol->n_desc & N_WEAK_REF) != 0) || !libInfo.required) ) { - // if target library not loaded and reference is weak or library is weak return 0 - return 0; - } - } - else { - dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", - ord, fLibrariesCount, symbolName, this->getPath()); - } - - if ( target == NULL ) { - //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); - throw "symbol not found"; - } - - // interpret hint - if ( fTwoLevelHints != NULL ) { - uint32_t symIndex = undefinedSymbol - fSymbolTable; - int32_t undefinedIndex = symIndex - fDynamicInfo->iundefsym; - if ( (undefinedIndex >= 0) && ((uint32_t)undefinedIndex < fDynamicInfo->nundefsym) ) { - const struct twolevel_hint* hints = (struct twolevel_hint*)(&fLinkEditBase[fTwoLevelHints->offset]); - const struct twolevel_hint* theHint = &hints[undefinedIndex]; - hint = (void*)theHint; - } - } - - const Symbol* sym = target->findExportedSymbol(symbolName, hint, true, foundIn); - if ( sym!= NULL ) { - return (*foundIn)->getExportedSymbolAddress(sym, context, this); - } - 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, this, context); - } - else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { - // if definition not found and reference is weak return 0 - return 0; - } - - // nowhere to be found - throwSymbolNotFound(symbolName, this->getPath(), target->getPath()); - } -} - -// returns if 'addr' is within the address range of section 'sectionIndex' -// fSlide is not used. 'addr' is assumed to be a prebound address in this image -bool ImageLoaderMachO::isAddrInSection(uintptr_t addr, uint8_t sectionIndex) -{ - uint8_t currentSectionIndex = 1; - 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 (unsigned long i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) { - // 'sectionIndex' is in this segment, get section info - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex]; - return ( (section->addr <= addr) && (addr < section->addr+section->size) ); - } - else { - // 'sectionIndex' not in this segment, skip to next segment - currentSectionIndex += seg->nsects; - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - return false; -} - -void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, bool onlyCoalescedSymbols) -{ - const uintptr_t relocBase = this->getRelocBase(); - const bool twoLevel = this->usesTwoLevelNameSpace(); - const bool prebound = this->isPrebindable(); - -#if TEXT_RELOC_SUPPORT - // if there are __TEXT fixups, temporarily make __TEXT writable - if ( fTextSegmentWithFixups != NULL ) - fTextSegmentWithFixups->tempWritable(context, this); -#endif - // cache last lookup - const struct macho_nlist* lastUndefinedSymbol = NULL; - uintptr_t symbolAddr = 0; - const ImageLoader* image = NULL; - - // loop through all external relocation records and bind each - 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: - { - const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; - // if only processing coalesced symbols and this one does not require coalesceing, skip to next - if ( onlyCoalescedSymbols && !symbolRequiresCoalescing(undefinedSymbol) ) - continue; - uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); - uintptr_t value = *location; - bool symbolAddrCached = true; - #if __i386__ - if ( reloc->r_pcrel ) { - value += (uintptr_t)location + 4 - fSlide; - } - #endif - if ( prebound ) { - // we are doing relocations, so prebinding was not usable - // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound - // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address - // if mach-o relocation structs had an "addend" field this complication would not be necessary. - if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { - // weak symbols need special casing, since *location may have been prebound to a definition in another image. - // If *location is currently prebound to somewhere in the same section as the weak definition, we assume - // that we can subtract off the weak symbol address to get the addend. - // If prebound elsewhere, we've lost the addend and have to assume it is zero. - // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs - if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) - value -= undefinedSymbol->n_value; - else - value = 0; - } - else { - // is undefined or non-weak symbol, so do subtraction to get addend - value -= undefinedSymbol->n_value; - } - } - // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too - if ( undefinedSymbol != lastUndefinedSymbol ) { - symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, &image); - lastUndefinedSymbol = undefinedSymbol; - symbolAddrCached = false; - } - if ( context.verboseBind ) { - const char *path = NULL; - if ( image != NULL ) { - path = image->getShortName(); - } - const char* cachedString = "(cached)"; - if ( !symbolAddrCached ) - cachedString = ""; - if ( value == 0 ) { - dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n", - this->getShortName(), (uintptr_t)location, - path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); - } - else { - dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n", - this->getShortName(), (uintptr_t)location, - path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); - } - } - value += symbolAddr; - #if __i386__ - if ( reloc->r_pcrel ) { - *location = value - ((uintptr_t)location + 4); - } - else { - // don't dirty page if prebound value was correct - if ( !prebound || (*location != value) ) - *location = value; - } - #else - // don't dirty page if prebound value was correct - if ( !prebound || (*location != value) ) - *location = value; - #endif - // update stats - ++fgTotalBindFixups; - } - break; - default: - throw "unknown external relocation type"; - } - } - else { - throw "bad external relocation length"; - } - } - -#if TEXT_RELOC_SUPPORT - // if there were __TEXT fixups, restore write protection - if ( fTextSegmentWithFixups != NULL ) { - fTextSegmentWithFixups->setPermissions(context, this); - sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(this), fTextSegmentWithFixups->getSize()); - } -#endif -} - -const mach_header* ImageLoaderMachO::machHeader() const -{ - return (mach_header*)fMachOData; -} - -uintptr_t ImageLoaderMachO::getSlide() const -{ - return fSlide; -} - -// hmm. maybe this should be up in ImageLoader?? -const void* ImageLoaderMachO::getEnd() const -{ - uintptr_t lastAddress = 0; - for (ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { - Segment* seg = *it; - uintptr_t segEnd = seg->getActualLoadAddress(this) + seg->getSize(); - if ( segEnd > lastAddress ) - lastAddress = segEnd; - } - return (const void*)lastAddress; -} - -uintptr_t ImageLoaderMachO::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context) +void ImageLoaderMachO::getUnwindInfo(dyld_unwind_sections* info) { - if ( context.verboseBind ) { - const char* path = NULL; - if ( targetImage != NULL ) - path = targetImage->getShortName(); - dyld::log("dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", - this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"), - path, symbolName, (uintptr_t)ptrToBind, targetAddr); + info->mh = this->machHeader(); + info->dwarf_section = 0; + info->dwarf_section_length = 0; + info->compact_unwind_section = 0; + info->compact_unwind_section_length = 0; + if ( fEHFrameSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fEHFrameSectionOffset]; + info->dwarf_section = (void*)(sect->addr + fSlide); + info->dwarf_section_length = sect->size; } - if ( context.bindingHandler != NULL ) { - const char* path = NULL; - if ( targetImage != NULL ) - path = targetImage->getShortName(); - targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr); - } -#if __i386__ - // i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32" - if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { - uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5); - // re-write instruction in a thread-safe manner - // use 8-byte compare-and-swap to alter 5-byte jump table entries - // loop is required in case the extra three bytes that cover the next entry are altered by another thread - bool done = false; - while ( !done ) { - volatile int64_t* jumpPtr = (int64_t*)ptrToBind; - int pad = 0; - // By default the three extra bytes swapped follow the 5-byte JMP. - // But, if the 5-byte jump is up against the end of the __IMPORT segment - // We don't want to access bytes off the end of the segment, so we shift - // the extra bytes to precede the 5-byte JMP. - if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) { - jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3); - pad = 3; - } - int64_t oldEntry = *jumpPtr; - union { - int64_t int64; - uint8_t bytes[8]; - } newEntry; - newEntry.int64 = oldEntry; - newEntry.bytes[pad+0] = 0xE9; // JMP rel32 - newEntry.bytes[pad+1] = rel32 & 0xFF; - newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF; - newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF; - newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF; - done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr); - } + if ( fUnwindInfoSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fUnwindInfoSectionOffset]; + info->compact_unwind_section = (void*)(sect->addr + fSlide); + info->compact_unwind_section_length = sect->size; } - else -#endif - *ptrToBind = targetAddr; - return targetAddr; } -uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { - // scan for all non-lazy-pointer sections - const bool twoLevel = this->usesTwoLevelNameSpace(); const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; - const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide(); for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_SEGMENT_COMMAND: { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - 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; - uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; - if ( type == S_LAZY_SYMBOL_POINTERS ) { - const uint32_t pointerCount = sect->size / sizeof(uintptr_t); - uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); - if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { - const uint32_t indirectTableOffset = sect->reserved1; - const uint32_t lazyIndex = lazyPointer - symbolPointers; - symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; - } - } - #if __i386__ - else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { - // 5 bytes stubs on i386 are new "fast stubs" - uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide); - uint8_t* const jmpTableEnd = jmpTableBase + sect->size; - // initial CALL instruction in jump table leaves pointer to next entry, so back up - uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5; - lazyPointer = (uintptr_t*)jmpTableEntryToPatch; - if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) { - const uint32_t indirectTableOffset = sect->reserved1; - const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5; - symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + if ( (unslidInteriorAddress >= seg->vmaddr) && (unslidInteriorAddress < (seg->vmaddr+seg->vmsize)) ) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) { + if ( segmentName != NULL ) + *segmentName = sect->segname; + if ( sectionName != NULL ) + *sectionName = sect->sectname; + if ( sectionOffset != NULL ) + *sectionOffset = unslidInteriorAddress - sect->addr; + return true; } } - #endif - if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { - const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; - const ImageLoader* image = NULL; - uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, &image); - #if __i386__ - this->makeImportSegmentWritable(context); - #endif - symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context); - ++fgTotalLazyBindFixups; - #if __i386__ - this->makeImportSegmentReadOnly(context); - #endif - return symbolAddr; - } } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); + return false; } -#if __i386__ -// -// For security the __IMPORT segments in the shared cache are normally not writable. -// Any image can also be linked with -read_only_stubs to make their __IMPORT segments normally not writable. -// For these images, dyld must change the page protection before updating them. -// The spin lock is required because lazy symbol binding is done without taking the global dyld lock. -// It keeps only one __IMPORT segment writable at a time. -// Pre-main(), the shared cache __IMPORT segments are always writable, so we don't need to change the protection. -// -void ImageLoaderMachO::makeImportSegmentWritable(const LinkContext& context) -{ - if ( fReadOnlyImportSegment != NULL ) { - if ( fInSharedCache ) { - if ( context.startedInitializingMainExecutable ) { - _spin_lock(&fgReadOnlyImportSpinLock); - context.makeSharedCacheImportSegmentsWritable(true); - } - } - else { - _spin_lock(&fgReadOnlyImportSpinLock); - fReadOnlyImportSegment->tempWritable(context, this); - } - } +void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn) +{ + dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn); } -void ImageLoaderMachO::makeImportSegmentReadOnly(const LinkContext& context) +const mach_header* ImageLoaderMachO::machHeader() const { - if ( fReadOnlyImportSegment != NULL ) { - if ( fInSharedCache ) { - if ( context.startedInitializingMainExecutable ) { - context.makeSharedCacheImportSegmentsWritable(false); - _spin_unlock(&fgReadOnlyImportSpinLock); - } - } - else { - fReadOnlyImportSegment->setPermissions(context, this); - _spin_unlock(&fgReadOnlyImportSpinLock); + return (mach_header*)fMachOData; +} + +uintptr_t ImageLoaderMachO::getSlide() const +{ + return fSlide; +} + +// hmm. maybe this should be up in ImageLoader?? +const void* ImageLoaderMachO::getEnd() const +{ + uintptr_t lastAddress = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + uintptr_t segEnd = segActualEndAddress(i); + if ( strcmp(segName(i), "__UNIXSTACK") != 0 ) { + if ( segEnd > lastAddress ) + lastAddress = segEnd; } } + return (const void*)lastAddress; } -#endif -void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys, bool onlyCoalescedSymbols) + +uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + const ImageLoader* targetImage, uint8_t type, const char* symbolName, + intptr_t addend, const char* msg) { -#if __i386__ - this->makeImportSegmentWritable(context); -#endif - // scan for all non-lazy-pointer sections - const bool twoLevel = this->usesTwoLevelNameSpace(); - const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; - const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; - const struct load_command* cmd = cmds; - const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; - 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; - uint32_t elementSize = sizeof(uintptr_t); - uint32_t elementCount = sect->size / elementSize; - if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { - if ( ! bindNonLazys ) - continue; - } - else if ( type == S_LAZY_SYMBOL_POINTERS ) { - // process each symbol pointer in this section - fgTotalPossibleLazyBindFixups += elementCount; - if ( ! bindLazys ) - continue; - } - #if __i386__ - else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { - // process each jmp entry in this section - elementCount = sect->size / 5; - elementSize = 5; - fgTotalPossibleLazyBindFixups += elementCount; - if ( ! bindLazys ) - continue; - } - #endif - else { - continue; - } - const uint32_t indirectTableOffset = sect->reserved1; - uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); - for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { - #if LINKEDIT_USAGE_DEBUG - noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); - #endif - uint32_t symbolIndex = indirectTable[indirectTableOffset + j]; - if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) { - *((uintptr_t*)ptrToBind) += this->fSlide; - } - else if ( symbolIndex == INDIRECT_SYMBOL_ABS) { - // do nothing since already has absolute address - } - else { - const struct macho_nlist* sym = &fSymbolTable[symbolIndex]; - if ( symbolIndex == 0 ) { - // This could be rdar://problem/3534709 - if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) { - static bool alreadyWarned = false; - if ( (sym->n_type & N_TYPE) != N_UNDF ) { - // The indirect table parallels the (non)lazy pointer sections. For - // instance, to find info about the fifth lazy pointer you look at the - // fifth entry in the indirect table. (try otool -Iv on a file). - // The entry in the indirect table contains an index into the symbol table. - - // The bug in ld caused the entry in the indirect table to be zero - // (instead of a magic value that means a local symbol). So, if the - // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid - // symbol table index. The check I put in place is to see if the zero'th - // symbol table entry is an import entry (usually it is a local symbol - // definition). - if ( context.verboseWarnings && !alreadyWarned ) { - dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", - this->getPath(), &fStrings[sym->n_un.n_strx]); - alreadyWarned = true; - } - continue; - } - } - } - const ImageLoader* image = NULL; - // if only processing coalesced symbols and this one does not require coalesceing, skip to next - if ( onlyCoalescedSymbols && !symbolRequiresCoalescing(sym) ) - continue; - uintptr_t symbolAddr; - symbolAddr = resolveUndefined(context, sym, twoLevel, &image); - - // update pointer - symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); - // update stats - ++fgTotalBindFixups; - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + // log + if ( context.verboseBind ) { + if ( addend != 0 ) + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n", + msg, this->getShortName(), (uintptr_t)location, + ((targetImage != NULL) ? targetImage->getShortName() : ""), + symbolName, (uintptr_t)location, value, addend); + else + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n", + msg, this->getShortName(), (uintptr_t)location, + ((targetImage != NULL) ? targetImage->getShortName() : "import-missing>"), + symbolName, (uintptr_t)location, value); } -#if __i386__ - this->makeImportSegmentReadOnly(context); -#endif + + // do actual update + uintptr_t* locationToFix = (uintptr_t*)location; + uint32_t* loc32; + uintptr_t newValue = value+addend; + uint32_t value32; + switch (type) { + case BIND_TYPE_POINTER: + // test first so we don't needless dirty pages + if ( *locationToFix != newValue ) + *locationToFix = newValue; + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)newValue; + if ( *loc32 != value32 ) + *loc32 = value32; + break; + case BIND_TYPE_TEXT_PCREL32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)newValue - (((uintptr_t)locationToFix) + 4); + if ( *loc32 != value32 ) + *loc32 = value32; + break; + default: + dyld::throwf("bad bind type %d", type); + } + + // update statistics + ++fgTotalBindFixups; + + return newValue; } + + + + #if SUPPORT_OLD_CRT_INITIALIZATION // first 16 bytes of "start" in crt1.o #if __ppc__ @@ -2616,7 +1253,6 @@ struct DATAdyld { // These are defined in dyldStartup.s extern "C" void stub_binding_helper(); extern "C" bool dyld_func_lookup(const char* name, uintptr_t* address); -extern "C" void fast_stub_binding_helper_interface(); void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) @@ -2672,81 +1308,17 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) } } } - } - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -#if __i386__ - if ( ! this->usablePrebinding(context) ) { - // reset all "fast" stubs - this->makeImportSegmentWritable(context); - 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_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { - // reset each jmp entry in this section - const uint32_t indirectTableOffset = sect->reserved1; - const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; - uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); - uint8_t* end = start + sect->size; - uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface; - uint32_t entryIndex = 0; - for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { - bool installLazyHandler = true; - // jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes - // if the instruction is updated by one thread while being executed by another - if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) { - // need to bind this now to avoid a potential problem if bound lazily - uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex]; - // the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used - if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { - const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; - const ImageLoader* image = NULL; - try { - uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), &image); - symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context); - ++fgTotalBindFixups; - uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5); - entry[0] = 0xE9; // JMP rel32 - entry[1] = rel32 & 0xFF; - entry[2] = (rel32 >> 8) & 0xFF; - entry[3] = (rel32 >> 16) & 0xFF; - entry[4] = (rel32 >> 24) & 0xFF; - installLazyHandler = false; - } - catch (const char* msg) { - // ignore errors when binding symbols early - // maybe the function is never called, and therefore erroring out now would be a regression - } - } - } - if ( installLazyHandler ) { - uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5); - entry[0] = 0xE8; // CALL rel32 - entry[1] = rel32 & 0xFF; - entry[2] = (rel32 >> 8) & 0xFF; - entry[3] = (rel32 >> 16) & 0xFF; - entry[4] = (rel32 >> 24) & 0xFF; - } - } + else if ( (strcmp(sect->sectname, "__program_vars" ) == 0) && (mh->filetype == MH_EXECUTE) ) { + // this is a Mac OS X 10.6 or later main executable + struct ProgramVars* pv = (struct ProgramVars*)(sect->addr + fSlide); + context.setNewProgramVars(*pv); } } } } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - this->makeImportSegmentReadOnly(context); } -#endif } @@ -2759,22 +1331,22 @@ void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const vars.mh = (macho_header*)fMachOData; // lookup _NXArgc - sym = this->findExportedSymbol("_NXArgc", NULL, false, NULL); + sym = this->findExportedSymbol("_NXArgc", false, NULL); if ( sym != NULL ) vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this); // lookup _NXArgv - sym = this->findExportedSymbol("_NXArgv", NULL, false, NULL); + sym = this->findExportedSymbol("_NXArgv", false, NULL); if ( sym != NULL ) vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); // lookup _environ - sym = this->findExportedSymbol("_environ", NULL, false, NULL); + sym = this->findExportedSymbol("_environ", false, NULL); if ( sym != NULL ) vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); // lookup __progname - sym = this->findExportedSymbol("___progname", NULL, false, NULL); + sym = this->findExportedSymbol("___progname", false, NULL); if ( sym != NULL ) vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this); @@ -2806,50 +1378,6 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const return false; } -void ImageLoaderMachO::doBindJustLazies(const LinkContext& context) -{ - // some API called requested that all lazy pointers in this image be force bound - this->doBindIndirectSymbolPointers(context, false, true, false); -} - -void ImageLoaderMachO::doBind(const LinkContext& context, bool forceLazysBound) -{ - // set dyld entry points in image - this->setupLazyPointerHandler(context); - - // 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) ) { - // if image has coalesced symbols, then these need to be rebound, unless this is the only image with weak symbols - if ( this->needsCoalescing() && (fgCountOfImagesWithWeakExports > 1) ) { - this->doBindExternalRelocations(context, true); - this->doBindIndirectSymbolPointers(context, true, true, true); - } - else { - ++fgImagesRequiringNoFixups; - } - - // skip binding because prebound and prebinding not disabled - return; - } - - // values bound by name are stored two different ways in mach-o: - - // 1) external relocations are used for data initialized to external symbols - this->doBindExternalRelocations(context, false); - - // 2) "indirect symbols" are used for code references to external symbols - // if this image is in the shared cache, there is noway to reset the lazy pointers, so bind them now - this->doBindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache, false); -} - -void ImageLoaderMachO::doUpdateMappingPermissions(const LinkContext& context) -{ -#if __i386__ - if ( (fReadOnlyImportSegment != NULL) && !fInSharedCache ) - fReadOnlyImportSegment->setPermissions(context, this); -#endif -} void ImageLoaderMachO::doImageInit(const LinkContext& context) { @@ -2857,7 +1385,7 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) 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 (unsigned long i = 0; i < cmd_count; ++i) { + for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_ROUTINES_COMMAND: Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); @@ -2877,7 +1405,7 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) 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 (unsigned long i = 0; i < cmd_count; ++i) { + 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)); @@ -2904,6 +1432,8 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) + + void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector& dofs) { if ( fHasDOFSections ) { @@ -2954,12 +1484,6 @@ bool ImageLoaderMachO::needsTermination() return fHasTerminators; } -#if IMAGE_NOTIFY_SUPPORT -bool ImageLoaderMachO::hasImageNotification() -{ - return fHasImageNotifySection; -} -#endif void ImageLoaderMachO::doTermination(const LinkContext& context) { @@ -2967,7 +1491,7 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) 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 (unsigned long i = 0; i < cmd_count; ++i) { + 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)); @@ -2980,7 +1504,7 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) for (uint32_t i=count; i > 0; --i) { Terminator func = terms[i-1]; if ( context.verboseInit ) - dyld::log("dyld: calling terminaton function %p in %s\n", func, this->getPath()); + dyld::log("dyld: calling termination function %p in %s\n", func, this->getPath()); func(); } } @@ -2991,169 +1515,203 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) } } -#if IMAGE_NOTIFY_SUPPORT -void ImageLoaderMachO::doNotification(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]) -{ - if ( fHasImageNotifySection ) { - 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 (unsigned long i = 0; i < cmd_count; ++i) { - if ( cmd->cmd == LC_SEGMENT_COMMAND ) { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( strcmp(seg->segname, "__DATA") == 0 ) { - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( strcmp(sect->sectname, "__image_notify") == 0 ) { - dyld_image_notifier* notes = (dyld_image_notifier*)(sect->addr + fSlide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { - dyld_image_notifier func = notes[i-1]; - func(mode, infoCount, info); - } - } - } - } - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - } -} -#endif void ImageLoaderMachO::printStatistics(unsigned int imageCount) { ImageLoader::printStatistics(imageCount); - //dyld::log("total hinted binary tree searches: %d\n", fgHintedBinaryTreeSearchs); - //dyld::log("total unhinted binary tree searches: %d\n", fgUnhintedBinaryTreeSearchs); - dyld::log("total images with weak exports: %d\n", fgCountOfImagesWithWeakExports); - -#if LINKEDIT_USAGE_DEBUG - dyld::log("linkedit pages accessed (%lu):\n", sLinkEditPageBuckets.size()); -#endif -} - - -ImageLoader::SegmentIterator ImageLoaderMachO::beginSegments() const -{ - return SegmentIterator(fSegmentsArray); -} - -ImageLoader::SegmentIterator ImageLoaderMachO::endSegments() const -{ - return SegmentIterator(&fSegmentsArray[fSegmentsArrayCount]); -} - -SegmentMachO::SegmentMachO(const struct macho_segment_command* cmd) - : fSegmentLoadCommand(cmd) -{ -} - -SegmentMachO::~SegmentMachO() -{ -} - -void SegmentMachO::adjust(const struct macho_segment_command* cmd) -{ - fSegmentLoadCommand = cmd; -} - -void SegmentMachO::unmap(const ImageLoader* image) -{ - // update stats - --ImageLoader::fgTotalSegmentsMapped; - ImageLoader::fgTotalBytesMapped -= fSegmentLoadCommand->vmsize; - munmap((void*)(this->getActualLoadAddress(image)), fSegmentLoadCommand->vmsize); -} - - -const char* SegmentMachO::getName() -{ - return fSegmentLoadCommand->segname; -} - -uintptr_t SegmentMachO::getSize() -{ - return fSegmentLoadCommand->vmsize; -} - -uintptr_t SegmentMachO::getFileSize() -{ - return fSegmentLoadCommand->filesize; + 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); +} + + +intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) +{ + // preflight and calculate slide if needed + const bool inPIE = (fgNextPIEDylibAddress != 0); + intptr_t slide = 0; + if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { + bool needsToSlide = false; + bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0); + uintptr_t lowAddr = (unsigned long)(-1); + uintptr_t highAddr = 0; + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uintptr_t segLow = segPreferredLoadAddress(i); + const uintptr_t segHigh = (segLow + segSize(i) + 4095) & -4096; + if ( segLow < lowAddr ) + lowAddr = segLow; + if ( segHigh > highAddr ) + highAddr = segHigh; + + if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + needsToSlide = true; + } + if ( needsToSlide ) { + // find a chunk of address space to hold all segments + uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context); + slide = addr - lowAddr; + } + } + else if ( ! this->segmentsCanSlide() ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( strcmp(segName(i), "__PAGEZERO") == 0 ) + continue; + if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + throw "can't map"; + } + } + else { + throw "mach-o does not support independently sliding segments"; + } + return slide; } -uintptr_t SegmentMachO::getFileOffset() -{ - return fSegmentLoadCommand->fileoff; -} -bool SegmentMachO::readable() +uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context) { - return ( (fSegmentLoadCommand->initprot & VM_PROT_READ) != 0); + vm_address_t addr = 0; + 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 + kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED); + if ( r == KERN_SUCCESS ) { + fgNextPIEDylibAddress = addr + size; + return addr; + } + fgNextPIEDylibAddress = 0; + } + kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) + throw "out of address space"; + + return addr; } -bool SegmentMachO::writeable() +bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) { - return ((fSegmentLoadCommand->initprot & VM_PROT_WRITE) != 0); + vm_address_t addr = start; + vm_size_t size = length; + kern_return_t r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + if ( r != KERN_SUCCESS ) + return false; + return true; } -bool SegmentMachO::executable() -{ - return ((fSegmentLoadCommand->initprot & VM_PROT_EXECUTE) != 0); -} -bool SegmentMachO::unaccessible() -{ - return (fSegmentLoadCommand->initprot == 0); -} -#if TEXT_RELOC_SUPPORT -bool SegmentMachO::hasFixUps() +void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) { - // scan sections for fix-up bit - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)fSegmentLoadCommand + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[fSegmentLoadCommand->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags & (S_ATTR_EXT_RELOC | S_ATTR_LOC_RELOC)) != 0 ) - return true; + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) + dyld::log("dyld: Mapping %s\n", this->getPath()); + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_offset_t fileOffset = segFileOffset(i) + offsetInFat; + vm_size_t size = segFileSize(i); + void* requestedLoadAddress = (void*)(segPreferredLoadAddress(i) + slide); + int protection = 0; + if ( !segUnaccessible(i) ) { + if ( segExecutable(i) ) + protection |= PROT_EXEC; + if ( segReadable(i) ) + protection |= PROT_READ; + if ( segWriteable(i) ) + protection |= PROT_WRITE; + } + #if __i386__ + // initially map __IMPORT segments R/W so dyld can update them + if ( segIsReadOnlyImport(i) ) + protection |= PROT_WRITE; + #endif + // wholly zero-fill segments have nothing to mmap() in + if ( size > 0 ) { + if ( (fileOffset+size) > fileLen ) { + dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", + segName(i), (uint64_t)(fileOffset+size), fileLen); + } + void* loadAddress = mmap(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()); + } + } + // 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), + (protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' ); } - return false; + // update slide to reflect load location + this->setSlide(slide); } -#endif -#if __i386__ -bool SegmentMachO::readOnlyImportStubs() -{ - return ( (fSegmentLoadCommand->initprot & VM_PROT_EXECUTE) - && ((fSegmentLoadCommand->initprot & VM_PROT_WRITE) == 0) - && (strcmp(fSegmentLoadCommand->segname, "__IMPORT") == 0) ); -} -#endif - -uintptr_t SegmentMachO::getActualLoadAddress(const ImageLoader* inImage) +void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) { - return fSegmentLoadCommand->vmaddr + inImage->getSlide(); + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) + dyld::log("dyld: Mapping memory %p\n", memoryImage); + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_address_t loadAddress = segPreferredLoadAddress(i) + slide; + vm_address_t srcAddr = (uintptr_t)memoryImage + segFileOffset(i); + vm_size_t size = segFileSize(i); + kern_return_t r = vm_copy(mach_task_self(), srcAddr, size, loadAddress); + if ( r != KERN_SUCCESS ) + throw "can't map segment"; + if ( context.verboseMapping ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", segName(i), (uintptr_t)loadAddress, (uintptr_t)loadAddress+size-1); + } + // update slide to reflect load location + this->setSlide(slide); + // set R/W permissions on all segments at slide location + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + segProtect(i, context); + } } -uintptr_t SegmentMachO::getPreferredLoadAddress() -{ - return fSegmentLoadCommand->vmaddr; -} -bool SegmentMachO::hasPreferredLoadAddress() +void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context) { - return (fSegmentLoadCommand->vmaddr != 0); -} - + vm_prot_t protection = 0; + if ( !segUnaccessible(segIndex) ) { + if ( segExecutable(segIndex) ) + protection |= PROT_EXEC; + if ( segReadable(segIndex) ) + protection |= PROT_READ; + if ( segWriteable(segIndex) ) + protection |= PROT_WRITE; + } + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); + if ( r != KERN_SUCCESS ) + throw "can't set vm permissions for mapped segment"; + 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' : '.' ); + } +} -Segment* SegmentMachO::next(Segment* location) -{ - return &((SegmentMachO*)location)[1]; +void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context) +{ + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ; + if ( segExecutable(segIndex) ) + 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 ( 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 e28f672..5d735bf 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -27,27 +27,25 @@ #define __IMAGELOADERMACHO__ #include +#include +#include #include "ImageLoader.h" #include "mach-o/dyld_images.h" -struct sf_mapping; -struct _shared_region_mapping_np; - // -// ImageLoaderMachO is the concrete subclass of ImageLoader which loads mach-o format files. -// The class is written to be 64-bit clean and support both 32-bit and 64-bit executables in -// mach-o. +// ImageLoaderMachO is a subclass of ImageLoader which loads mach-o format files. // // class ImageLoaderMachO : public ImageLoader { public: - ImageLoaderMachO(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offset, uint64_t len, const struct stat& info, const LinkContext& context); - ImageLoaderMachO(const char* moduleName, const struct mach_header* mh, uint64_t len, const LinkContext& context); - ImageLoaderMachO(const struct mach_header* mh, const char* path, const struct stat& info, const LinkContext& context); - ImageLoaderMachO(const struct mach_header* executableHeader, uintptr_t executableSlide, const char* path, const LinkContext& context); - virtual ~ImageLoaderMachO(); + 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* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context); + const char* getInstallPath() const; virtual void* getMain() const; @@ -55,7 +53,7 @@ public: virtual uintptr_t getSlide() const; virtual const void* getEnd() const; virtual bool hasCoalescedExports() const; - virtual const Symbol* findExportedSymbol(const char* name, const void* hint, bool searchReExports, const ImageLoader** foundIn) 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 DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const; virtual const char* getExportedSymbolName(const Symbol* sym) const; @@ -63,122 +61,148 @@ public: virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const; virtual uint32_t getImportedSymbolCount() const; virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const; - virtual ReferenceFlags geImportedSymbolInfo(const Symbol* sym) const; + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const; virtual const char* getImportedSymbolName(const Symbol* sym) const; virtual bool isBundle() const; virtual bool isDylib() const; + virtual bool isExecutable() const; + virtual bool isPositionIndependentExecutable() const; virtual bool forceFlat() const; - virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); + virtual bool participatesInCoalescing() const; + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0; + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0; virtual void doTermination(const LinkContext& context); -#if IMAGE_NOTIFY_SUPPORT - virtual void doNotification(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]); -#endif virtual bool needsInitialization(); -#if IMAGE_NOTIFY_SUPPORT - virtual bool hasImageNotification(); -#endif virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length); + virtual void getUnwindInfo(dyld_unwind_sections* info); virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset); virtual bool usablePrebinding(const LinkContext& context) const; + virtual unsigned int segmentCount() const; + virtual const char* segName(unsigned int) const; + virtual uintptr_t segSize(unsigned int) const; + virtual uintptr_t segFileSize(unsigned int) const; + virtual bool segHasTrailingZeroFill(unsigned int); + virtual uintptr_t segFileOffset(unsigned int) const; + virtual bool segReadable(unsigned int) const; + virtual bool segWriteable(unsigned int) const; + virtual bool segExecutable(unsigned int) const; + virtual bool segUnaccessible(unsigned int) const; + virtual bool segHasPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualLoadAddress(unsigned int) const; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualEndAddress(unsigned int) const; static void printStatistics(unsigned int imageCount); protected: ImageLoaderMachO(const ImageLoaderMachO&); + ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + virtual ~ImageLoaderMachO() {} + void operator=(const ImageLoaderMachO&); - virtual uint32_t doGetDependentLibraryCount(); + virtual void setDyldInfo(const 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; + virtual uint32_t* segmentCommandOffsets() const = 0; + virtual void rebase(const LinkContext& context) = 0; + virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0; + virtual bool containsSymbol(const void* addr) const = 0; + virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) 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; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const = 0; + virtual unsigned int importedSymbolCount() const = 0; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const = 0; + virtual const char* importedSymbolName(const Symbol* symbol) const = 0; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context) = 0; +#endif + + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]); virtual LibraryInfo doGetLibraryInfo(); virtual void getRPaths(const LinkContext& context, std::vector&) const; + virtual bool getUUID(uuid_t) const; virtual void doRebase(const LinkContext& context); - virtual void doBind(const LinkContext& context, bool forceLazysBound); - virtual void doBindJustLazies(const LinkContext& context); - virtual void doUpdateMappingPermissions(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 void doGetDOFSections(const LinkContext& context, std::vector& dofs); virtual bool needsTermination(); - virtual void instantiateSegments(const uint8_t* fileData); virtual bool segmentsMustSlideTogether() const; virtual bool segmentsCanSlide() const; virtual void setSlide(intptr_t slide); virtual bool usesTwoLevelNameSpace() const; - virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; - virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const; virtual bool isPrebindable() const; - virtual SegmentIterator beginSegments() const; - virtual SegmentIterator endSegments() const; -#if SPLIT_SEG_DYLIB_SUPPORT - virtual void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); -#endif -private: - friend class SegmentMachO; +protected: - void init(); + void destroy(); + static void sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, + unsigned int* segCount, unsigned int* libCount); + static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); +#if CODESIGNING_SUPPORT + void loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile); +#endif + const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; void parseLoadCmds(); - uintptr_t bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context); - void doBindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys, bool onlyCoalescedSymbols); - void doBindExternalRelocations(const LinkContext& context, bool onlyCoalescedSymbols); - uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel, const ImageLoader **foundIn); - uintptr_t getRelocBase(); - uintptr_t getFirstWritableSegmentAddress(); - void resetPreboundLazyPointers(const LinkContext& context, uintptr_t relocBase); + bool segHasRebaseFixUps(unsigned int) const; + bool segHasBindFixUps(unsigned int) const; + void segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context); + void segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context); +#if __i386__ + bool segIsReadOnlyImport(unsigned int) const; +#endif + intptr_t assignSegmentAddresses(const LinkContext& context); + uintptr_t reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context); + bool reserveAddressRange(uintptr_t start, size_t length); + void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + 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 doImageInit(const LinkContext& context); void doModInitFunctions(const LinkContext& context); void setupLazyPointerHandler(const LinkContext& context); void lookupProgramVars(const LinkContext& context) const; -#if SPLIT_SEG_DYLIB_SUPPORT - unsigned int getExtraZeroFillEntriesCount(); - void initMappingTable(uint64_t offsetInFat, _shared_region_mapping_np *mappingTable); - int sharedRegionMapFilePrivateOutside(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); -#endif -#if SPLIT_SEG_SHARED_REGION_SUPPORT - int sharedRegionLoadFile(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); - int sharedRegionMapFile(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); - int sharedRegionMakePrivate(const LinkContext& context); - int sharedRegionMapFilePrivate(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context, bool usemmap); - void initMappingTable(uint64_t offsetInFat, sf_mapping *mappingTable, uintptr_t baseAddress); -#endif - bool needsCoalescing() const; - bool isAddrInSection(uintptr_t addr, uint8_t sectionIndex); - void adjustSegments(); - uintptr_t getSymbolAddress(const struct macho_nlist* sym, const ImageLoader* requestor, const LinkContext& context) const; - void preFetch(int fd, uint64_t offsetInFat, const LinkContext& context); -#if __i386__ - void makeImportSegmentWritable(const LinkContext& context); - void makeImportSegmentReadOnly(const LinkContext& context); -#endif + uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + const ImageLoader* targetImage, uint8_t type, const char* symbolName, + intptr_t addend, const char* msg); - static bool symbolRequiresCoalescing(const struct macho_nlist* symbol); - static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); - const struct macho_nlist* binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const; - const struct macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], - const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const; + void makeTextSegmentWritable(const LinkContext& context, bool writeable); + void preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context); + + + bool hasReferencesToWeakSymbols() const; + uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const; + static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); +protected: const uint8_t* fMachOData; - const uint8_t* fLinkEditBase; // add any internal "offset" to this to get actual address - const struct macho_nlist* fSymbolTable; - const char* fStrings; - const struct dysymtab_command* fDynamicInfo; + const uint8_t* fLinkEditBase; // add any internal "offset" to this to get mapped address uintptr_t fSlide; - const struct twolevel_hints_command* fTwoLevelHints; - const struct dylib_command* fDylibID; - class SegmentMachO* fSegmentsArray; + uint32_t fEHFrameSectionOffset; + uint32_t fUnwindInfoSectionOffset; + uint32_t fDylibIDOffset; + uint32_t fSegmentsCount : 8, + fIsSplitSeg : 1, + fInSharedCache : 1, #if TEXT_RELOC_SUPPORT - class SegmentMachO* fTextSegmentWithFixups; // NULL unless __TEXT segment has fixups + fTextSegmentRebases : 1, + fTextSegmentBinds : 1, #endif #if __i386__ - class SegmentMachO* fReadOnlyImportSegment; // NULL unless __IMPORT segment built with -read_only_stubs - static uint32_t fgReadOnlyImportSpinLock; -#endif - uint32_t fSegmentsArrayCount : 8, - fIsSplitSeg : 1, - fInSharedCache : 1, -#if __ppc64__ - f4GBWritable : 1, + fReadOnlyImportSegment : 1, #endif fHasSubLibraries : 1, fHasSubUmbrella : 1, @@ -186,51 +210,11 @@ private: fHasDOFSections : 1, fHasDashInit : 1, fHasInitializers : 1, -#if IMAGE_NOTIFY_SUPPORT - fHasImageNotifySection : 1, -#endif fHasTerminators : 1; - static uint32_t fgHintedBinaryTreeSearchs; - static uint32_t fgUnhintedBinaryTreeSearchs; - static uint32_t fgCountOfImagesWithWeakExports; -}; - - -class SegmentMachO : public Segment -{ -public: - SegmentMachO(const struct macho_segment_command* cmd); - virtual ~SegmentMachO(); - - virtual const char* getName(); - virtual uintptr_t getSize(); - virtual uintptr_t getFileSize(); - virtual uintptr_t getFileOffset(); - virtual bool readable(); - virtual bool writeable(); - virtual bool executable(); - virtual bool unaccessible(); - virtual uintptr_t getActualLoadAddress(const ImageLoader*); - virtual uintptr_t getPreferredLoadAddress(); - virtual void unmap(const ImageLoader*); - virtual Segment* next(Segment*); -#if TEXT_RELOC_SUPPORT - virtual bool hasFixUps(); -#endif -#if __i386__ - virtual bool readOnlyImportStubs(); -#endif -protected: - virtual bool hasPreferredLoadAddress(); - -private: - SegmentMachO(const SegmentMachO&); - void operator=(const SegmentMachO&); - void adjust(const struct macho_segment_command* cmd); - - friend class ImageLoaderMachO; - - const struct macho_segment_command* fSegmentLoadCommand; + + + static uint32_t fgSymbolTableBinarySearchs; + static uint32_t fgSymbolTrieSearchs; }; diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp new file mode 100644 index 0000000..182047e --- /dev/null +++ b/src/ImageLoaderMachOClassic.cpp @@ -0,0 +1,1979 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// work around until conformance work is complete rdar://problem/4508801 +#define __srr0 srr0 +#define __eip eip +#define __rip rip + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __ppc__ || __ppc64__ + #include +#endif +#if __x86_64__ + #include +#endif +#if __arm__ + #include +#endif + +#include "ImageLoaderMachOClassic.h" +#include "mach-o/dyld_images.h" + +// optimize strcmp for ppc +#if __ppc__ + #include +#else + #define astrcmp(a,b) strcmp(a,b) +#endif + + +// in dyldStartup.s +extern "C" void fast_stub_binding_helper_interface(); + + +#if __x86_64__ + #define POINTER_RELOC X86_64_RELOC_UNSIGNED +#else + #define POINTER_RELOC GENERIC_RELOC_VANILLA +#endif + + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + + + +// create image for main executable +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( (mh->flags & MH_PIE) && !context.noPIE ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->instantiateFinish(context); + +#if __i386__ + // kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding + if ( image->fReadOnlyImportSegment ) { + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + if ( image->segIsReadOnlyImport(i) ) + image->segMakeWritable(i, context); + } + } +#endif + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// 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) +{ + 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); + #endif + + // 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] == '/') ) + image->setPathUnowned(installName); + else if ( path[0] != '/' ) { + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( realpath(path, realPath) != NULL ) + image->setPath(realPath); + else + image->setPath(path); + } + else + image->setPath(path); + + // 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 + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by using cached mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->ImageLoaderMachO::mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOClassic::ImageLoaderMachOClassic(const macho_header* mh, const char* path, + unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fStrings(NULL), fSymbolTable(NULL), fDynamicInfo(NULL) +{ +} + +// construct ImageLoaderMachOClassic using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOClassic) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOClassic* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOClassic))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOClassic(mh, path, segCount, segOffsets, libCount); +} + + + +// common code to finish initializing object +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() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + +uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic))); +} + + +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)); +} + +bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + + +void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported) +{ + uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + uintptr_t value = (uintptr_t)image; + if ( reExported ) + value |= 1; + images[libIndex] = value; +} + + +void ImageLoaderMachOClassic::setSymbolTableInfo(const macho_nlist* symbols, const char* strings, const dysymtab_command* dynSym) +{ + fSymbolTable = symbols; + fStrings = strings; + fDynamicInfo = dynSym; +} + +void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context) +{ + // always prefetch a subrange of __LINKEDIT pages + uintptr_t symbolTableStart = (uintptr_t)fSymbolTable; + uintptr_t stringTableStart = (uintptr_t)fStrings; + uintptr_t start; + // if image did not load at preferred address + if ( segPreferredLoadAddress(0) != (uintptr_t)fMachOData ) { + // local relocations will be processed, so start pre-fetch at local symbols + start = (uintptr_t)fMachOData + fDynamicInfo->locreloff; + } + else { + // otherwise start pre-fetch at global symbols section of symbol table + start = symbolTableStart + fDynamicInfo->iextdefsym * sizeof(macho_nlist); + } + // prefetch ends at end of last undefined string in string pool + uintptr_t end = stringTableStart; + if ( fDynamicInfo->nundefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx; + else if ( fDynamicInfo->nextdefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; + + // round to whole pages + start = start & (-4096); + end = (end + 4095) & (-4096); + + // skip if there is only one page + if ( (end-start) > 4096 ) { + madvise((void*)start, end-start, MADV_WILLNEED); + fgTotalBytesPreFetched += (end-start); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", "__LINKEDIT", start, end-1); + } + } +} + + +#if SPLIT_SEG_DYLIB_SUPPORT +unsigned int +ImageLoaderMachOClassic::getExtraZeroFillEntriesCount() +{ + // calculate mapping entries + unsigned int extraZeroFillEntries = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segHasTrailingZeroFill(i) ) + ++extraZeroFillEntries; + } + + return extraZeroFillEntries; +} + +void +ImageLoaderMachOClassic::initMappingTable(uint64_t offsetInFat, + shared_file_mapping_np *mappingTable) +{ + for(unsigned int i=0,entryIndex=0; i < fSegmentsCount; ++i, ++entryIndex) { + shared_file_mapping_np* entry = &mappingTable[entryIndex]; + entry->sfm_address = segActualLoadAddress(i); + entry->sfm_size = segFileSize(i); + entry->sfm_file_offset = segFileOffset(i) + offsetInFat; + entry->sfm_init_prot = VM_PROT_NONE; + if ( !segUnaccessible(i) ) { + if ( segExecutable(i) ) + entry->sfm_init_prot |= VM_PROT_EXECUTE; + if ( segReadable(i) ) + entry->sfm_init_prot |= VM_PROT_READ; + if ( segWriteable(i) ) + entry->sfm_init_prot |= VM_PROT_WRITE | VM_PROT_COW; + } + entry->sfm_max_prot = entry->sfm_init_prot; + if ( segHasTrailingZeroFill(i) ) { + shared_file_mapping_np* zfentry = &mappingTable[++entryIndex]; + zfentry->sfm_address = entry->sfm_address + segFileSize(i); + zfentry->sfm_size = segSize(i) - segFileSize(i); + zfentry->sfm_file_offset = 0; + zfentry->sfm_init_prot = entry->sfm_init_prot | VM_PROT_COW | VM_PROT_ZF; + zfentry->sfm_max_prot = zfentry->sfm_init_prot; + } + } +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + uintptr_t nextAltLoadAddress = 0; + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int regionCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np regions[regionCount]; + initMappingTable(offsetInFat, regions); + int r = -1; + // find space somewhere to allocate split seg + bool foundRoom = false; + while ( ! foundRoom ) { + foundRoom = true; + for(unsigned int i=0; i < regionCount; ++i) { + vm_address_t addr = nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address; + vm_size_t size = regions[i].sfm_size ; + r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + if ( 0 != r ) { + // no room here, deallocate what has succeeded so far + for(unsigned int j=0; j < i; ++j) { + vm_address_t addr = nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address; + vm_size_t size = regions[j].sfm_size ; + (void)vm_deallocate(mach_task_self(), addr, size); + } + nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again + // skip over shared region + if ( (SHARED_REGION_BASE <= nextAltLoadAddress) && (nextAltLoadAddress < (SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) + nextAltLoadAddress = (SHARED_REGION_BASE + SHARED_REGION_SIZE); + if ( nextAltLoadAddress > 0xFF000000 ) + throw "can't map split seg anywhere"; + foundRoom = false; + break; + } + } + } + + // map in each region + uintptr_t slide = nextAltLoadAddress - regions[0].sfm_address; + this->setSlide(slide); + for(unsigned int i=0; i < regionCount; ++i) { + if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) { + // nothing to mmap for zero-fills areas, they are just vm_allocated + } + else { + void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide); + size_t size = regions[i].sfm_size; + int protection = 0; + if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( regions[i].sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( regions[i].sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = regions[i].sfm_file_offset; + //dyld::log("mmap(%p, 0x%08lX, %s\n", mmapAddress, size, fPath); + mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); + if ( mmapAddress == ((void*)(-1)) ) + throw "mmap error"; + } + } + + // logging + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = ®ions[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (regionCount-1) ) { + const shared_file_mapping_np* nextEntry = ®ions[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + + return r; +} +#endif // SPLIT_SEG_DYLIB_SUPPORT + + +void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +{ + // non-split segment libraries handled by super class + if ( !fIsSplitSeg ) + 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; + // if there is a problem, fall into case where we map file somewhere outside the shared region +#endif + +#if SPLIT_SEG_DYLIB_SUPPORT + // support old split-seg dylibs by mapping them where ever we find space + if ( mapSplitSegDylibOutsideSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) != 0 ) +#endif + throw "mapping error"; +} + + +#if SPLIT_SEG_SHARED_REGION_SUPPORT +static int _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) +{ + return syscall(295, fd, count, mappings); +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibInfoSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + // build table of segments to map + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np mappingTable[mappingTableCount]; + initMappingTable(offsetInFat, mappingTable); + + // try to map it in shared + int r = _shared_region_map_np(fd, mappingTableCount, mappingTable); + if ( 0 == r ) { + this->setNeverUnload(); + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = &mappingTable[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (mappingTableCount-1) ) { + const shared_file_mapping_np* nextEntry = &mappingTable[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), + (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + } + return r; +} + +#endif // SPLIT_SEG_SHARED_REGION_SUPPORT + +// test if this image is re-exported through parent (the image that loaded this one) +bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const +{ + if ( fInUmbrella ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_SUB_FRAMEWORK) { + const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; + const char* exportThruName = (char*)cmd + subf->umbrella.offset; + // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent... + const char* parentInstallPath = parent->getInstallPath(); + if ( parentInstallPath != NULL ) { + const char* lastSlash = strrchr(parentInstallPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], exportThruName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1]; + strcpy(reexportAndSuffix, exportThruName); + strcat(reexportAndSuffix, context.imageSuffix); + if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) + return true; + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + return false; +} + +// test if child is re-exported +bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const +{ + if ( fHasSubLibraries ) { + // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* firstDot = strchr(lastSlash, '.'); + int len; + if ( firstDot == NULL ) + len = strlen(lastSlash); + else + len = firstDot-lastSlash-1; + char childLeafName[len+1]; + strncpy(childLeafName, &lastSlash[1], len); + childLeafName[len] = '\0'; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_LIBRARY: + { + const struct sub_library_command* lib = (struct sub_library_command*)cmd; + const char* aSubLibName = (char*)cmd + lib->sub_library.offset; + if ( strcmp(aSubLibName, childLeafName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end + char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1]; + strcpy(aSubLibNameAndSuffix, aSubLibName); + strcat(aSubLibNameAndSuffix, context.imageSuffix); + if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + if ( fHasSubUmbrella ) { + // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_UMBRELLA: + { + const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd; + const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset; + if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1]; + strcpy(umbrellaAndSuffix, aSubUmbrellaName); + strcat(umbrellaAndSuffix, context.imageSuffix); + if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + return false; +} + + +uintptr_t ImageLoaderMachOClassic::getFirstWritableSegmentAddress() +{ + // in split segment libraries r_address is offset from first writable segment + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segWriteable(i) ) + return segActualLoadAddress(i); + } + throw "no writable segment"; +} + +uintptr_t ImageLoaderMachOClassic::getRelocBase() +{ + // r_address is either an offset from the first segment address + // or from the first writable segment address +#if __x86_64__ + return getFirstWritableSegmentAddress(); +#else + if ( fIsSplitSeg ) + return getFirstWritableSegmentAddress(); + else + return segActualLoadAddress(0); +#endif +} + + +#if __ppc__ +static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationType, uint16_t otherHalf, uintptr_t slide) +{ + // low 16 bits of 32-bit ppc instructions need fixing + struct ppcInstruction { uint16_t opcode; int16_t immediateValue; }; + ppcInstruction* instruction = (ppcInstruction*)locationToFix; + //uint32_t before = *((uint32_t*)locationToFix); + switch ( relocationType ) + { + case PPC_RELOC_LO16: + instruction->immediateValue = ((otherHalf << 16) | instruction->immediateValue) + slide; + break; + case PPC_RELOC_HI16: + instruction->immediateValue = ((((instruction->immediateValue << 16) | otherHalf) + slide) >> 16); + break; + case PPC_RELOC_HA16: + int16_t signedOtherHalf = (int16_t)(otherHalf & 0xffff); + uint32_t temp = (instruction->immediateValue << 16) + signedOtherHalf + slide; + if ( (temp & 0x00008000) != 0 ) + temp += 0x00008000; + instruction->immediateValue = temp >> 16; + } + //uint32_t after = *((uint32_t*)locationToFix); + //dyld::log("dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after); +} +#endif + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& context) +{ + // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values + const uintptr_t relocBase = this->getRelocBase(); + register const uintptr_t slide = this->fSlide; + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address & R_SCATTERED) != 0 ) { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + #if __ppc__ + case PPC_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + #if __i386__ + case GENERIC_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + #if __arm__ + case ARM_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + } + } + } + } +} +#endif + + + + +void ImageLoaderMachOClassic::rebase(const LinkContext& context) +{ + register const uintptr_t slide = this->fSlide; + const uintptr_t relocBase = this->getRelocBase(); + + // prefetch any LINKEDIT pages needed + if ( !context.preFetchDisabled && !this->isPrebindable()) + this->prefetchLINKEDIT(context); + + // loop through all local (internal) relocation records + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + try { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(reloc); + #endif + #if __x86_64__ + // only one kind of local relocation supported for x86_64 + if ( reloc->r_length != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern != 0 ) + throw "extern relocation found with local relocations"; + *((uintptr_t*)(reloc->r_address + relocBase)) += slide; + #else + if ( (reloc->r_address & R_SCATTERED) == 0 ) { + if ( reloc->r_symbolnum == R_ABS ) { + // ignore absolute relocations + } + else if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case GENERIC_RELOC_VANILLA: + *((uintptr_t*)(reloc->r_address + relocBase)) += slide; + break; + #if __ppc__ + case PPC_RELOC_HI16: + case PPC_RELOC_LO16: + case PPC_RELOC_HA16: + // some tools leave object file relocations in linked images + otherRelocsPPC((uintptr_t*)(reloc->r_address + relocBase), reloc->r_type, reloc[1].r_address, slide); + ++reloc; // these relocations come in pairs, skip next + break; + #endif + default: + throw "unknown local relocation type"; + } + } + else { + throw "bad local relocation length"; + } + } + else { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + case GENERIC_RELOC_VANILLA: + *locationToFix += slide; + break; + #if __ppc__ + case PPC_RELOC_HI16: + case PPC_RELOC_LO16: + case PPC_RELOC_HA16: + // Metrowerks compiler sometimes leaves object file relocations in linked images??? + ++reloc; // these relocations come in pairs, get next one + otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide); + break; + case PPC_RELOC_PB_LA_PTR: + // do nothing + break; + #elif __ppc64__ + case PPC_RELOC_PB_LA_PTR: + // needed for compatibility with ppc64 binaries built with the first ld64 + // which used PPC_RELOC_PB_LA_PTR relocs instead of GENERIC_RELOC_VANILLA for lazy pointers + *locationToFix += slide; + break; + #elif __i386__ + case GENERIC_RELOC_PB_LA_PTR: + // do nothing + break; + #elif __arm__ + case ARM_RELOC_PB_LA_PTR: + // do nothing + break; + #endif + default: + throw "unknown local scattered relocation type"; + } + } + else { + throw "bad local scattered relocation length"; + } + } + #endif // x86_64 + } + catch (const char* msg) { + const uint8_t* r = (uint8_t*)reloc; + dyld::throwf("%s in %s. reloc record at %p: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + msg, this->getPath(), reloc, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); + } + } + + // update stats + fgTotalRebaseFixups += fDynamicInfo->nlocrel; +} + + + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const +{ + int32_t high = symbolCount-1; + int32_t mid = hintIndex; + + // handle out of range hint + if ( mid >= (int32_t)symbolCount ) + mid = symbolCount/2; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + ++fgTotalBindImageSearches; + + //dyld::log("dyld: binarySearchWithToc for %s in %s\n", key, this->getShortName()); + + for (int32_t low = 0; low <= high; mid = (low+high)/2) { + const uint32_t index = toc[mid].symbol_index; + const struct macho_nlist* pivot = &symbols[index]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&toc[mid]); + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = astrcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + low = mid + 1; + } + else { + // key < pivot + high = mid - 1; + } + } + return NULL; +} + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const +{ + // update stats + ++fgTotalBindImageSearches; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + + //dyld::log("dyld: binarySearch for %s in %s, stringpool=%p, symbols=%p, symbolCount=%u\n", + // key, this->getShortName(), stringPool, symbols, symbolCount); + + const struct macho_nlist* base = symbols; + for (uint32_t n = symbolCount; n > 0; n /= 2) { + const struct macho_nlist* pivot = &base[n/2]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = astrcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + + +const ImageLoader::Symbol* ImageLoaderMachOClassic::findExportedSymbol(const char* name, const ImageLoader** foundIn) const +{ + const struct macho_nlist* sym = NULL; + if ( fDynamicInfo->tocoff == 0 ) + sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym); + else + sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], + fDynamicInfo->ntoc, fDynamicInfo->nextdefsym); + if ( sym != NULL ) { + if ( foundIn != NULL ) + *foundIn = (ImageLoader*)this; + return (const Symbol*)sym; + } + return NULL; +} + + + +bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const +{ + return ( (fSymbolTable <= addr) && (addr < fStrings) ); +} + + +uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const Symbol* symbol) const +{ + const struct macho_nlist* sym = (macho_nlist*)symbol; + uintptr_t result = sym->n_value + fSlide; + #if __arm__ + // processor assumes code address with low bit set is thumb + if (sym->n_desc & N_ARM_THUMB_DEF) + result |= 1; + #endif + return result; +} + +bool ImageLoaderMachOClassic::exportedSymbolIsWeakDefintion(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return ( (nlistSym->n_desc & N_WEAK_DEF) != 0 ); +} + +const char* ImageLoaderMachOClassic::exportedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + +unsigned int ImageLoaderMachOClassic::exportedSymbolCount() const +{ + return fDynamicInfo->nextdefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::exportedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nextdefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +unsigned int ImageLoaderMachOClassic::importedSymbolCount() const +{ + return fDynamicInfo->nundefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::importedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nundefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +const char* ImageLoaderMachOClassic::importedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + + + +bool ImageLoaderMachOClassic::symbolIsWeakDefinition(const struct macho_nlist* symbol) +{ + // if a define and weak ==> coalesced + if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) ) + return true; + + // regular symbol + return false; +} + +bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* symbol) +{ + // if an undefine and not referencing a weak symbol ==> coalesced + if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) ) + return true; + + // regular symbol + return false; +} + +uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context) const +{ + return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context); +} + +uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, + bool twoLevel, bool dontCoalesce, const ImageLoader** foundIn) +{ + ++fgTotalBindSymbolsResolved; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(undefinedSymbol); + noteAccessedLinkEditAddress(symbolName); +#endif + if ( context.bindFlat || !twoLevel ) { + // 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); + *foundIn = this; + return addr; + } + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { + if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) + this->addDynamicReference(*foundIn); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + // if a bundle is loaded privately the above will not find its exports + if ( this->isBundle() && this->hasHiddenExports() ) { + // look in self for needed symbol + sym = this->findExportedSymbol(symbolName, foundIn); + if ( sym != NULL ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // definition can't be found anywhere + // if reference is weak_import, then it is ok, just return 0 + return 0; + } + throwSymbolNotFound(symbolName, this->getPath(), "flat namespace"); + } + else { + // symbol requires searching images with coalesced symbols (not done during prebinding) + if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) { + const Symbol* sym; + if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { + if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) + this->addDynamicReference(*foundIn); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + //throwSymbolNotFound(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); + *foundIn = this; + return addr; + } + + // two level lookup + ImageLoader* target = NULL; + uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc); + if ( ord == EXECUTABLE_ORDINAL ) { + target = context.mainExecutable; + } + else if ( ord == SELF_LIBRARY_ORDINAL ) { + target = this; + } + else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) { + // rnielsen: HACKHACK + // flat lookup + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + // no image has exports this symbol + // report error + context.undefinedHandler(symbolName); + // try looking again + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + + throwSymbolNotFound(symbolName, this->getPath(), "dynamic lookup"); + } + else if ( ord <= libraryCount() ) { + target = libImage(ord-1); + if ( target == NULL ) { + // if target library not loaded and reference is weak or library is weak return 0 + return 0; + } + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", + ord, libraryCount(), symbolName, this->getPath()); + } + + if ( target == NULL ) { + //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); + throw "symbol not found"; + } + + const Symbol* sym = target->findExportedSymbol(symbolName, true, foundIn); + if ( sym!= NULL ) { + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) { + // 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); + } + else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // if definition not found and reference is weak return 0 + return 0; + } + + // nowhere to be found + throwSymbolNotFound(symbolName, this->getPath(), target->getPath()); + } +} + + + +// returns if 'addr' is within the address range of section 'sectionIndex' +// fSlide is not used. 'addr' is assumed to be a prebound address in this image +bool ImageLoaderMachOClassic::isAddrInSection(uintptr_t addr, uint8_t sectionIndex) +{ + uint8_t currentSectionIndex = 1; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) { + // 'sectionIndex' is in this segment, get section info + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex]; + return ( (section->addr <= addr) && (addr < section->addr+section->size) ); + } + else { + // 'sectionIndex' not in this segment, skip to next segment + currentSectionIndex += seg->nsects; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} + +void ImageLoaderMachOClassic::doBindExternalRelocations(const LinkContext& context) +{ + const uintptr_t relocBase = this->getRelocBase(); + const bool twoLevel = this->usesTwoLevelNameSpace(); + const bool prebound = this->isPrebindable(); + +#if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); +#endif + // cache last lookup + const struct macho_nlist* lastUndefinedSymbol = NULL; + uintptr_t symbolAddr = 0; + const ImageLoader* image = NULL; + + // loop through all external relocation records and bind each + 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: + { + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + uintptr_t value = *location; + bool symbolAddrCached = true; + #if __i386__ + if ( reloc->r_pcrel ) { + value += (uintptr_t)location + 4 - fSlide; + } + #endif + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) { + value -= undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + value -= 1; + #endif + } + else + value = 0; + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + value -= (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + value -= undefinedSymbol->n_value; + } + } + // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too + if ( undefinedSymbol != lastUndefinedSymbol ) { + bool dontCoalesce = true; + if ( symbolIsWeakReference(undefinedSymbol) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, &image); + lastUndefinedSymbol = undefinedSymbol; + symbolAddrCached = false; + } + if ( context.verboseBind ) { + const char *path = NULL; + if ( image != NULL ) { + path = image->getShortName(); + } + const char* cachedString = "(cached)"; + if ( !symbolAddrCached ) + cachedString = ""; + if ( value == 0 ) { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); + } + else { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); + } + } + value += symbolAddr; + #if __i386__ + if ( reloc->r_pcrel ) { + *location = value - ((uintptr_t)location + 4); + } + else { + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + } + #else + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + #endif + // update stats + ++fgTotalBindFixups; + } + break; + default: + throw "unknown external relocation type"; + } + } + else { + throw "bad external relocation length"; + } + } + +#if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) { + this->makeTextSegmentWritable(context, true); + } +#endif +} + + + +uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context) +{ + if ( context.verboseBind ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + dyld::log("dyld: bind indirect sym: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", + this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"), + ((path != NULL) ? path : ""), symbolName, (uintptr_t)ptrToBind, targetAddr); + } + if ( context.bindingHandler != NULL ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr); + } +#if __i386__ + // i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32" + if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { + uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5); + // re-write instruction in a thread-safe manner + // use 8-byte compare-and-swap to alter 5-byte jump table entries + // loop is required in case the extra three bytes that cover the next entry are altered by another thread + bool done = false; + while ( !done ) { + volatile int64_t* jumpPtr = (int64_t*)ptrToBind; + int pad = 0; + // By default the three extra bytes swapped follow the 5-byte JMP. + // But, if the 5-byte jump is up against the end of the __IMPORT segment + // We don't want to access bytes off the end of the segment, so we shift + // the extra bytes to precede the 5-byte JMP. + if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) { + jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3); + pad = 3; + } + int64_t oldEntry = *jumpPtr; + union { + int64_t int64; + uint8_t bytes[8]; + } newEntry; + newEntry.int64 = oldEntry; + newEntry.bytes[pad+0] = 0xE9; // JMP rel32 + newEntry.bytes[pad+1] = rel32 & 0xFF; + newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF; + newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF; + newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF; + done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr); + } + } + else +#endif + *ptrToBind = targetAddr; + return targetAddr; +} + +uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) +{ + throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; +} + +uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + 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; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // 5 bytes stubs on i386 are new "fast stubs" + uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide); + uint8_t* const jmpTableEnd = jmpTableBase + sect->size; + // initial CALL instruction in jump table leaves pointer to next entry, so back up + uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5; + lazyPointer = (uintptr_t*)jmpTableEntryToPatch; + if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5; + symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + } + } + #endif + if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, &image); + symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + + +void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.type = 0; + if ( fDynamicInfo->tocoff != 0 ) { + it.curIndex = 0; + it.endIndex = fDynamicInfo->ntoc; + } + else { + it.curIndex = 0; + it.endIndex = fDynamicInfo->nextdefsym; + } +} + + +bool ImageLoaderMachOClassic::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( fDynamicInfo->tocoff != 0 ) { + if ( it.curIndex >= fDynamicInfo->ntoc ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + const uint32_t index = toc[it.curIndex].symbol_index; + const struct macho_nlist* sym = &fSymbolTable[index]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + else { + if ( it.curIndex >= fDynamicInfo->nextdefsym ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym+it.curIndex]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + + return false; +} + +uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; + } + 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()); + return sym->n_value + fSlide; +} + + +void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) +{ + // flat_namespace images with classic LINKEDIT do not need late coalescing. + // They still need to be iterated becuase they may implement + // something needed by other coalescing images. + // But they need no updating because during the bind phase every symbol lookup is a full scan. + if ( !this->usesTwoLevelNameSpace() ) + return; + + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; + } + + // if this image's copy of the symbol is not a weak definition nor a weak reference then nothing to coalesce here + if ( !symbolIsWeakReference(&fSymbolTable[symbol_index]) && !symbolIsWeakDefinition(&fSymbolTable[symbol_index]) ) { + return; + } + + // malformed dylib with duplicate weak symbols causes re-binding + if ( it.type ) + return; + + bool boundSomething = false; + // scan external relocations for uses of symbol_index + const uintptr_t relocBase = this->getRelocBase(); + const bool prebound = this->isPrebindable(); + 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_symbolnum == symbol_index ) { + //dyld::log("found external reloc using symbol_index=%d in %s\n",symbol_index, this->getPath()); + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + const uintptr_t initialValue = *location; + uintptr_t addend = 0; + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (initialValue == undefinedSymbol->n_value) || this->isAddrInSection(initialValue, undefinedSymbol->n_sect) ) { + addend = initialValue - undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + addend += 1; + #endif + } + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + addend = initialValue - (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + addend = initialValue - undefinedSymbol->n_value; + } + } + else { + // non-prebound case + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // if target is weak-def in same linkage unit, then bind phase has already set initialValue + // 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); + } + else { + // nothing fixed up yet, addend is just initial value + //dyld::log("addend=0x%lX\n", initialValue); + addend = initialValue; + } + } + + uint8_t type = BIND_TYPE_POINTER; + #if __i386__ + if ( reloc->r_pcrel ) + type = BIND_TYPE_TEXT_PCREL32; + #endif + this->bindLocation(context, (uintptr_t)location, value, targetImage, type, symbolName, addend, "weak "); + boundSomething = true; + } + } + + // scan lazy and non-lazy pointers for uses of symbol_index + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + 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) { + uint32_t elementSize = sizeof(uintptr_t); + switch ( sect->flags & SECTION_TYPE ) { + #if __i386__ + case S_SYMBOL_STUBS: + if ( ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) ==0) || (sect->reserved2 != 5) ) + continue; + elementSize = 5; + #endif + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + { + uint32_t elementCount = sect->size / elementSize; + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + //dyld::log(" scanning section %s of %s starting at %p\n", sect->sectname, this->getShortName(), ptrToBind); + for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + if ( indirectTable[indirectTableOffset + j] == symbol_index ) { + //dyld::log(" found symbol index match at %d/%d, ptrToBind=%p\n", j, elementCount, ptrToBind); + // update pointer + this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, it.symbolName, value, targetImage, context); + boundSomething = true; + } + } + } + break; + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() ) + this->addDynamicReference(targetImage); + + // mark that this symbol has already been bound, so we don't try to bind again + it.type = 1; +} + + +void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys) +{ + // scan for all non-lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + 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) { + bool isLazySymbol = false; + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t elementSize = sizeof(uintptr_t); + uint32_t elementCount = sect->size / elementSize; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + if ( ! bindNonLazys ) + continue; + } + else if ( type == S_LAZY_SYMBOL_POINTERS ) { + // process each symbol pointer in this section + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // process each jmp entry in this section + elementCount = sect->size / 5; + elementSize = 5; + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #endif + else { + continue; + } + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); + #endif + uint32_t symbolIndex = indirectTable[indirectTableOffset + j]; + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) { + *((uintptr_t*)ptrToBind) += this->fSlide; + } + else if ( symbolIndex == INDIRECT_SYMBOL_ABS) { + // do nothing since already has absolute address + } + else { + const struct macho_nlist* sym = &fSymbolTable[symbolIndex]; + if ( symbolIndex == 0 ) { + // This could be rdar://problem/3534709 + if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) { + static bool alreadyWarned = false; + if ( (sym->n_type & N_TYPE) != N_UNDF ) { + // The indirect table parallels the (non)lazy pointer sections. For + // instance, to find info about the fifth lazy pointer you look at the + // fifth entry in the indirect table. (try otool -Iv on a file). + // The entry in the indirect table contains an index into the symbol table. + + // The bug in ld caused the entry in the indirect table to be zero + // (instead of a magic value that means a local symbol). So, if the + // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid + // symbol table index. The check I put in place is to see if the zero'th + // symbol table entry is an import entry (usually it is a local symbol + // definition). + if ( context.verboseWarnings && !alreadyWarned ) { + dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", + this->getPath(), &fStrings[sym->n_un.n_strx]); + alreadyWarned = true; + } + continue; + } + } + } + const ImageLoader* image = NULL; + // let weak definitions resolve to themselves, later coalescing may overwrite them + bool dontCoalesce = true; + if ( bindLazys && isLazySymbol ) { + // if this is something normally lazy bound, but we are forcing + // it to be bound now, do coalescing + dontCoalesce = false; + } + if ( symbolIsWeakReference(sym) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, &image); + // update pointer + symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); + // update stats + ++fgTotalBindFixups; + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + + +#if __i386__ +void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) +{ + if ( ! this->usablePrebinding(context) ) { + // reset all "fast" stubs + 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) { + 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_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // reset each jmp entry in this section + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); + uint8_t* end = start + sect->size; + uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface; + uint32_t entryIndex = 0; + for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { + bool installLazyHandler = true; + // jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes + // if the instruction is updated by one thread while being executed by another + if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) { + // need to bind this now to avoid a potential problem if bound lazily + uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + // the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used + if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + try { + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, &image); + symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context); + ++fgTotalBindFixups; + uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5); + entry[0] = 0xE9; // JMP rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + installLazyHandler = false; + } + catch (const char* msg) { + // ignore errors when binding symbols early + // maybe the function is never called, and therefore erroring out now would be a regression + } + } + } + if ( installLazyHandler ) { + uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5); + entry[0] = 0xE8; // CALL rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + } + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} +#endif // __i386__ + + +void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) +{ +#if __i386__ + this->initializeLazyStubs(context); +#endif + + // 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) ) { + // binding already up to date + } + else { + // no valid prebinding, so bind symbols. + // values bound by name are stored two different ways in classic mach-o: + + // 1) external relocations are used for data initialized to external symbols + this->doBindExternalRelocations(context); + + // 2) "indirect symbols" are used for code references to external symbols + // 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); + + } + + // set up dyld entry points in image + this->setupLazyPointerHandler(context); +} + +void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context) +{ + // some API called requested that all lazy pointers in this image be force bound + this->bindIndirectSymbolPointers(context, false, true); +} + +const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + uintptr_t targetAddress = (uintptr_t)addr - fSlide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &fSymbolTable[fDynamicInfo->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[fDynamicInfo->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &fSymbolTable[fDynamicInfo->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[fDynamicInfo->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { + *closestAddr = (void*)(bestSymbol->n_value + fSlide); + return &fStrings[bestSymbol->n_un.n_strx]; + } + return NULL; +} + + diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h new file mode 100644 index 0000000..6a339ec --- /dev/null +++ b/src/ImageLoaderMachOClassic.h @@ -0,0 +1,134 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __IMAGELOADER_MACHO_CLASSIC__ +#define __IMAGELOADER_MACHO_CLASSIC__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOClassic is the concrete subclass of ImageLoader which loads mach-o files +// that use the traditional LINKEDIT format. +// +class ImageLoaderMachOClassic : public ImageLoaderMachO { +public: + static ImageLoaderMachOClassic* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + 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 LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + + virtual ~ImageLoaderMachOClassic(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + virtual void setLibImage(unsigned int, ImageLoader*, 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 const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context); + +protected: + 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; + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const; + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context); + virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + ImageLoaderMachOClassic(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOClassic* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + uintptr_t getRelocBase(); + void mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + uintptr_t getFirstWritableSegmentAddress(); + const struct macho_nlist* binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const; + const struct macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const; + static bool symbolIsWeakReference(const struct macho_nlist* symbol); + static bool symbolIsWeakDefinition(const struct macho_nlist* symbol); + uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel, + bool dontCoalesce, const ImageLoader **foundIn); + uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context) 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, + const char* symbolName, uintptr_t targetAddr, + const ImageLoader* targetImage, const LinkContext& context); + void bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys); + void initializeLazyStubs(const LinkContext& context); + void prefetchLINKEDIT(const LinkContext& context); +#if SPLIT_SEG_DYLIB_SUPPORT + unsigned int getExtraZeroFillEntriesCount(); + void initMappingTable(uint64_t offsetInFat, shared_file_mapping_np *mappingTable); +#endif + int mapSplitSegDylibOutsideSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#if SPLIT_SEG_SHARED_REGION_SUPPORT + int mapSplitSegDylibInfoSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#endif + + + const char* fStrings; + const struct macho_nlist* fSymbolTable; + const struct dysymtab_command* fDynamicInfo; + +}; + + + + +#endif // __IMAGELOADER_MACHO_CLASSIC__ + + + + diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp new file mode 100644 index 0000000..7d3812b --- /dev/null +++ b/src/ImageLoaderMachOCompressed.cpp @@ -0,0 +1,1370 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoaderMachOCompressed.h" +#include "mach-o/dyld_images.h" + + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + +static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + dyld::throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + dyld::throwf("uleb128 too big"); + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= ((byte & 0x7f) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + + +// create image for main executable +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( (mh->flags & MH_PIE) && !context.noPIE ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->setNeverUnload(); + image->instantiateFinish(context); + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// 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) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::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->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); + + // finish construction + 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] == '/') ) + image->setPathUnowned(installName); +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // app crashes when libSystem cannot be found + else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) + image->setPathUnowned("/usr/lib/libSystem.B.dylib"); +#endif + else if ( path[0] != '/' ) { + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( realpath(path, realPath) != NULL ) + image->setPath(realPath); + else + image->setPath(path); + } + else + image->setPath(path); + + // 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()) { + image->preFetchDATA(fd, offsetInFat, context); + image->markSequentialLINKEDIT(context); + } + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// 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* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL) +{ +} + +ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + + + +// construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOCompressed* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); +} + + +// common code to finish initializing object +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 +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); +} + + +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)); +} + +bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + + +void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported) +{ + uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + uintptr_t value = (uintptr_t)image; + if ( reExported ) + value |= 1; + images[libIndex] = value; +} + + +void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) +{ + // mark that we are done with rebase and bind info + markLINKEDIT(context, MADV_FREE); +} + +void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) +{ + // mark the rebase and bind info and using sequential access + markLINKEDIT(context, MADV_SEQUENTIAL); +} + +void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise) +{ + // if not loaded at preferred address, mark rebase info + uintptr_t start = 0; + if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off; + else if ( fDyldInfo->bind_off != 0 ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off; + else + return; // no binding info to prefetch + + // end is at end of bind info + uintptr_t end = 0; + if ( fDyldInfo->bind_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size; + else if ( fDyldInfo->rebase_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size; + else + return; + + + // round to whole pages + start = start & (-4096); + end = (end + 4095) & (-4096); + + // do nothing if only one page of rebase/bind info + if ( (end-start) <= 4096 ) + return; + + // tell kernel about our access to these pages + madvise((void*)start, end-start, advise); + if ( context.verboseMapping ) { + const char* adstr = "sequential"; + if ( advise == MADV_FREE ) + adstr = "free"; + dyld::log("%18s %s 0x%0lX -> 0x%0lX\n", "__LINKEDIT", adstr, start, end-1); + } +} + + + +void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) +{ + //dyld::log("0x%08lX type=%d\n", addr, type); + uintptr_t* locationToFix = (uintptr_t*)addr; + switch (type) { + case REBASE_TYPE_POINTER: + *locationToFix += slide; + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + *locationToFix += slide; + break; + default: + dyld::throwf("bad rebase type %d", type); + } +} + +void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + +void ImageLoaderMachOCompressed::rebase(const LinkContext& context) +{ + const uintptr_t slide = this->fSlide; + const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; + const uint8_t* const end = &start[fDyldInfo->rebase_size]; + const uint8_t* p = start; + + try { + uint8_t type = 0; + int segmentIndex = 0; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + uint32_t count; + uint32_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( segmentIndex > fSegmentsCount ) + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + segmentIndex, fSegmentsCount); + address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); + segmentEndAddress = segActualEndAddress(segmentIndex); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + address += immediate*sizeof(uintptr_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + if ( address >= segmentEndAddress ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += immediate; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( address >= segmentEndAddress ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( address >= segmentEndAddress ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += read_uleb128(p, end) + sizeof(uintptr_t); + ++fgTotalRebaseFixups; + 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) { + if ( address >= segmentEndAddress ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += skip + sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + default: + dyld::throwf("bad rebase opcode %d", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + + + + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const +{ + //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; + 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; + } + const uint8_t childrenCount = *children++; + const uint8_t* e = children; + const uint8_t* newNode = NULL; + for (uint8_t i=0; i < childrenCount; ++i) { + const char* ss = s; + 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' ) { + if ( !wrongEdge ) { + if ( *e != *ss++ ) + wrongEdge = true; + } + ++e; + } + if ( wrongEdge ) { + // advance to next child + ++e; + read_uleb128(e, end); + } + else { + // 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]; + s = ss; + //dyld::log("findExportedSymbol() found matching edge advancing to node 0x%x\n", nodeOffset); + break; + } + } + if ( newNode != NULL ) + p = newNode; + else { + //dyld::log("findExportedSymbol(%s) in %s failed\n", symbol, this->getShortName()); + return NULL; + } + } while ( true ); +} + + +bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const +{ + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; + const uint8_t* end = &start[fDyldInfo->export_size]; + return ( (start <= addr) && (addr < end) ); +} + + +uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const Symbol* symbol) 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"; + uint32_t flags = read_uleb128(exportNode, exportTrieEnd); + if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + else + throw "unsupported exported symbol kind"; +} + +bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) 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"; + uint32_t flags = read_uleb128(exportNode, exportTrieEnd); + return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); +} + + +const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const +{ + throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const +{ + throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const +{ + throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + + + +uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, 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); + } + // 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); + } + 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"); +} + + +uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, + const char* symbolName, const ImageLoader** foundIn) +{ + // two level lookup + const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn); + if ( sym != NULL ) { + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + + if ( weak_import ) { + // definition can't be found anywhere, ok because it is weak, just return 0 + return 0; + } + + // nowhere to be found + throwSymbolNotFound(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) +{ + *targetImage = NULL; + + // only clients that benefit from caching last lookup pass in a LastLookup struct + if ( last != NULL ) { + if ( (last->ordinal == libraryOrdinal) + && (last->flags == symboFlags) + && (last->name == symbolName) ) { + *targetImage = last->foundIn; + return last->result; + } + } + + 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); + } + else { + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + *targetImage = context.mainExecutable; + } + else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + *targetImage = this; + } + else if ( libraryOrdinal <= 0 ) { + dyld::throwf("bad mach-o binary, unknown special library ordinal (%u) too big for symbol %s in %s", + libraryOrdinal, symbolName, this->getPath()); + } + else if ( (unsigned)libraryOrdinal <= libraryCount() ) { + *targetImage = libImage(libraryOrdinal-1); + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", + libraryOrdinal, libraryCount(), symbolName, this->getPath()); + } + if ( *targetImage == NULL ) { + if ( weak_import ) { + // if target library not loaded and reference is weak or library is weak return 0 + symbolAddress = 0; + } + else { + dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%d could not be loaded", + symbolName, this->getPath(), libraryOrdinal); + } + } + else { + symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, targetImage); + } + } + + // save off lookup results if client wants + if ( last != NULL ) { + last->ordinal = libraryOrdinal; + last->flags = symboFlags; + last->name = symbolName; + last->foundIn = *targetImage; + last->result = symbolAddress; + } + + return symbolAddress; +} + +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) +{ + const ImageLoader* targetImage; + uintptr_t symbolAddress; + + // resolve symbol + symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last); + + // do actual update + return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg); +} + +void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is beyond end of segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + + +void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) +{ + // 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 { + // 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, + // there is no way to reset the lazy pointers, so force bind them now + if ( forceLazysBound || fInSharedCache ) + this->doBindJustLazies(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); +} + + +void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) +{ + eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); +} + +void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) +{ + try { + uint8_t type = 0; + int segmentIndex = 0; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + const char* symbolName = NULL; + uint8_t symboFlags = 0; + int libraryOrdinal = 0; + intptr_t addend = 0; + uint32_t count; + uint32_t skip; + LastLookup last = { 0, 0, NULL, 0, NULL }; + const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; + const uint8_t* const end = &start[fDyldInfo->bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( segmentIndex > fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + segmentIndex, fSegmentsCount); + address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); + segmentEndAddress = segActualEndAddress(segmentIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + 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); + 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); + 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); + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( address >= segmentEndAddress ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last); + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in bind info", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) +{ + try { + uint8_t type = BIND_TYPE_POINTER; + int segmentIndex = 0; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + const char* symbolName = NULL; + uint8_t symboFlags = 0; + int libraryOrdinal = 0; + intptr_t addend = 0; + const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; + const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( segmentIndex > fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + segmentIndex, fSegmentsCount); + address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); + segmentEndAddress = segActualEndAddress(segmentIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + 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); + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + dyld::throwf("bad lazy bind opcode %d", *p); + } + } + } + + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +// A program built targeting 10.5 will have hybrid stubs. When used with weak symbols +// the classic lazy loader is used even when running on 10.6 +uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // only works with compressed LINKEDIT if classic symbol table is also present + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) + dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); + + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + 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; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { + const macho_nlist* symbol = &symbolTable[symbolIndex]; + const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; + int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); + if ( !twoLevel || context.bindFlat ) + libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + uintptr_t ptrToBind = (uintptr_t)lazyPointer; + uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + +uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) +{ + 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"; + + uint8_t type = BIND_TYPE_POINTER; + uintptr_t address = 0; + const char* symbolName = NULL; + uint8_t symboFlags = 0; + int libraryOrdinal = 0; + bool done = false; + uintptr_t result = 0; + const uint8_t* p = &start[lazyBindingInfoOffset]; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate > fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + immediate, fSegmentsCount); + 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); + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + dyld::throwf("bad lazy bind opcode %d", *p); + } + } + return result; +} + +void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.curIndex = 0; + it.endIndex = this->fDyldInfo->weak_bind_size; + it.address = 0; + it.type = 0; + it.addend = 0; +} + + +bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( this->fDyldInfo->weak_bind_size == 0 ) { + /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info + it.done = true; + it.symbolName = "~~~"; + return true; + } + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + uint32_t count; + uint32_t skip; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + it.done = true; + it.curIndex = p - start; + it.symbolName = "~~~"; // sorts to end + return true; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + it.symbolName = (char*)p; + it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); + it.symbolMatches = false; + while (*p != '\0') + ++p; + ++p; + it.curIndex = p - start; + return false; + case BIND_OPCODE_SET_TYPE_IMM: + it.type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + it.addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate > fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + immediate, fSegmentsCount); + it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + it.address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + it.address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad weak bind opcode %d", *p); + } + } + /// hmmm, BIND_OPCODE_DONE is missing... + it.done = true; + it.symbolName = "~~~"; + //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); + return true; +} + +uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); + const ImageLoader* foundIn = NULL; + const ImageLoader::Symbol* sym = this->findExportedSymbol(it.symbolName, &foundIn); + if ( sym != NULL ) { + //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); + return foundIn->getExportedSymbolAddress(sym, context, this); + } + return 0; +} + + +void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, const LinkContext& context) +{ + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + + uint8_t type = it.type; + uintptr_t address = it.address; + const char* symbolName = it.symbolName; + intptr_t addend = it.addend; + uint32_t count; + uint32_t skip; + bool done = false; + bool boundSomething = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + done = true; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate > fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", + immediate, fSegmentsCount); + address = segActualLoadAddress(immediate) + read_uleb128(p, end); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); + boundSomething = true; + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); + boundSomething = true; + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); + boundSomething = true; + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + bindLocation(context, address, value, targetImage, type, symbolName, addend, "weak "); + boundSomething = true; + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in weak binding info", *p); + } + } + if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() ) + this->addDynamicReference(targetImage); +} + + + + + +const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + // called by dladdr() + // only works with compressed LINKEDIT if classic symbol table is also present + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) + return NULL; + + uintptr_t targetAddress = (uintptr_t)addr - fSlide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { + *closestAddr = (void*)(bestSymbol->n_value + fSlide); + return &symbolTableStrings[bestSymbol->n_un.n_strx]; + } + return NULL; +} + + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) +{ + // no way to back off a prebound compress image +} +#endif + diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h new file mode 100644 index 0000000..ae137c0 --- /dev/null +++ b/src/ImageLoaderMachOCompressed.h @@ -0,0 +1,135 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __IMAGELOADER_MACHO_COMPRESSED__ +#define __IMAGELOADER_MACHO_COMPRESSED__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOCompressed is the concrete subclass of ImageLoader which loads mach-o files +// that use the compressed LINKEDIT format. +// +class ImageLoaderMachOCompressed : public ImageLoaderMachO { +public: + static ImageLoaderMachOCompressed* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + 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 LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + + + virtual ~ImageLoaderMachOCompressed(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + virtual void setLibImage(unsigned int, ImageLoader*, 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 const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context); + + +protected: + 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; } + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; } + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context); + virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + struct LastLookup { int ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; }; + + + typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, + const char* symbolName, uint8_t symboFlags, intptr_t addend, int libraryOrdinal, + const char* msg, LastLookup* last); + + void eachLazyBind(const LinkContext& context, bind_handler); + void eachBind(const LinkContext& context, bind_handler); + + + ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOCompressed* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + void markSequentialLINKEDIT(const LinkContext& context); + void markFreeLINKEDIT(const LinkContext& context); + void markLINKEDIT(const LinkContext& context, int advise); + + void rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type); + void throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); + uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, + LastLookup* last); + 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, + 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 struct dyld_info_command* fDyldInfo; +}; + + + +#endif // __IMAGELOADER_MACHO_COMPRESSED__ + + + + diff --git a/src/ImageLoaderPE.cpp b/src/ImageLoaderPE.cpp deleted file mode 100644 index 173dda8..0000000 --- a/src/ImageLoaderPE.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- -* -* Copyright (c) 2007 AppleInc. All rights reserved. -* -* @APPLE_LICENSE_HEADER_START@ -* -* This file contains Original Code and/or Modifications of Original Code -* as defined in and that are subject to the Apple Public Source License -* Version 2.0 (the 'License'). You may not use this file except in -* compliance with the License. Please obtain a copy of the License at -* http://www.opensource.apple.com/apsl/ and read it before using this -* file. -* -* The Original Code and all software distributed under the License are -* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -* Please see the License for the specific language governing rights and -* limitations under the License. -* -* @APPLE_LICENSE_HEADER_END@ -*/ - diff --git a/src/dyld.cpp b/src/dyld.cpp index 93cfaaa..3d5bcd0 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -39,6 +39,17 @@ #include #include #include +#include + +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif #include #include @@ -49,7 +60,12 @@ #include "ImageLoader.h" #include "ImageLoaderMachO.h" #include "dyldLibSystemInterface.h" +#if DYLD_SHARED_CACHE_SUPPORT #include "dyld_cache_format.h" +#endif +#if CORESYMBOLICATION_SUPPORT +#include "coreSymbolicationDyldSupport.hpp" +#endif // from _simple.h in libc typedef struct _SIMPLE* _SIMPLE_STRING; @@ -62,11 +78,9 @@ extern "C" char * _simple_string(_SIMPLE_STRING __b); -// 32-bit ppc is only architecture that uses cpu-sub-types -#define CPU_SUBTYPES_SUPPORTED __ppc__ - +// 32-bit ppc and ARM are the only architecture that use cpu-sub-types +#define CPU_SUBTYPES_SUPPORTED __ppc__ || __arm__ -#define OLD_GDB_DYLD_INTERFACE __ppc__ || __i386__ #define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ @@ -75,10 +89,7 @@ extern "C" char * _simple_string(_SIMPLE_STRING __b); /* implemented in dyld_gdb.cpp */ extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); extern void removeImageFromAllImages(const mach_header* mh); -#if OLD_GDB_DYLD_INTERFACE -extern void addImageForgdb(const mach_header* mh, uintptr_t slide, const char* physicalPath, const char* logicalPath); -extern void removeImageForgdb(const struct mach_header* mh); -#endif +extern void setAlImageInfosHalt(const char* message, uintptr_t flags); // magic so CrashReporter logs message extern "C" { @@ -110,7 +121,6 @@ struct EnvironmentVariables { const char* const * DYLD_FALLBACK_FRAMEWORK_PATH; const char* const * DYLD_LIBRARY_PATH; const char* const * DYLD_FALLBACK_LIBRARY_PATH; - const char* const * DYLD_ROOT_PATH; const char* const * DYLD_INSERT_LIBRARIES; const char* const * LD_LIBRARY_PATH; // for unix conformance bool DYLD_PRINT_LIBRARIES; @@ -120,6 +130,9 @@ struct EnvironmentVariables { bool DYLD_PRINT_OPTS; bool DYLD_PRINT_ENV; bool DYLD_DISABLE_DOFS; + // DYLD_SHARED_CACHE_DONT_VALIDATE ==> sSharedCacheIgnoreInodeAndTimeStamp + // DYLD_SHARED_CACHE_DIR ==> sSharedCacheDir + // DYLD_ROOT_PATH ==> gLinkContext.rootPaths // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv @@ -127,6 +140,7 @@ struct EnvironmentVariables { // DYLD_PRINT_INITIALIZERS ==> gLinkContext.verboseInit // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind + // DYLD_PRINT_WEAK_BINDINGS ==> gLinkContext.verboseWeakBind // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF // DYLD_PRINT_APIS ==> gLogAPIs @@ -136,25 +150,22 @@ struct EnvironmentVariables { // DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode // DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings }; - + typedef std::vector StateHandlers; struct RegisteredDOF { const mach_header* mh; int registrationID; }; // all global state static const char* sExecPath = NULL; -static const struct mach_header* sMainExecutableMachHeader = NULL; +static const macho_header* sMainExecutableMachHeader = NULL; static cpu_type_t sHostCPU; static cpu_subtype_t sHostCPUsubtype; static ImageLoader* sMainExecutable = NULL; -static bool sMainExecutableIsSetuid = false; +static bool sProcessIsRestricted = false; static unsigned int sInsertedDylibCount = 0; static std::vector sAllImages; static std::vector sImageRoots; static std::vector sImageFilesNeedingTermination; static std::vector sImageFilesNeedingDOFUnregistration; -#if IMAGE_NOTIFY_SUPPORT -static std::vector sImagesToNotifyAboutOtherImages; -#endif static std::vector sAddImageCallbacks; static std::vector sRemoveImageCallbacks; static StateHandlers sSingleHandlers[7]; @@ -163,15 +174,12 @@ static ImageLoader* sLastImageByAddressCache; static EnvironmentVariables sEnv; static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; -static BundleNotificationCallBack sBundleNotifier = NULL; -static BundleLocatorCallBack sBundleLocation = NULL; static UndefinedHandler sUndefinedHandler = NULL; static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked #if DYLD_SHARED_CACHE_SUPPORT static const dyld_cache_header* sSharedCache = NULL; -bool gSharedCacheNotFound = false; -bool gSharedCacheNeedsUpdating = false; -bool gSharedCacheDontNotify = false; +static bool sSharedCacheIgnoreInodeAndTimeStamp = false; +static const char* sSharedCacheDir = DYLD_SHARED_CACHE_DIR; #endif ImageLoader::LinkContext gLinkContext; bool gLogAPIs = false; @@ -179,52 +187,139 @@ const struct LibSystemHelpers* gLibSystemHelpers = NULL; #if SUPPORT_OLD_CRT_INITIALIZATION bool gRunInitializersOldWay = false; #endif -#if __i386__ -static uint32_t sImportSegmentsStart = 0; -static uint32_t sImportSegmentsSize = 0; -#endif + + +// +// The MappedRanges structure is used for fast address->image lookups. +// The table is only updated when the dyld lock is held, so we don't +// need to worry about multiple writers. But readers may look at this +// data without holding the lock. Therefore, all updates must be done +// in an order that will never cause readers to see inconsistent data. +// The general rule is that if the image field is non-NULL then +// the other fields are valid. +// +struct MappedRanges +{ + enum { count=400 }; + struct { + ImageLoader* image; + uintptr_t start; + uintptr_t end; + } array[count]; + MappedRanges* next; +}; + +static MappedRanges sMappedRangesStart; + +void addMappedRange(ImageLoader* image, uintptr_t start, uintptr_t end) +{ + //dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName()); + for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) { + for (int i=0; i < MappedRanges::count; ++i) { + if ( p->array[i].image == NULL ) { + p->array[i].start = start; + p->array[i].end = end; + // add image field last with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = image; + return; + } + } + } + // table must be full, chain another + MappedRanges* newRanges = (MappedRanges*)malloc(sizeof(MappedRanges)); + bzero(newRanges, sizeof(MappedRanges)); + newRanges->array[0].start = start; + newRanges->array[0].end = end; + newRanges->array[0].image = image; + for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) { + if ( p->next == NULL ) { + OSMemoryBarrier(); + p->next = newRanges; + break; + } + } +} + +void removedMappedRanges(ImageLoader* image) +{ + for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) { + for (int i=0; i < MappedRanges::count; ++i) { + if ( p->array[i].image == image ) { + // clear with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = NULL; + } + } + } +} + +ImageLoader* findMappedRange(uintptr_t target) +{ + for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) { + for (int i=0; i < MappedRanges::count; ++i) { + if ( p->array[i].image != NULL ) { + if ( (p->array[i].start <= target) && (target < p->array[i].end) ) + return p->array[i].image; + } + } + } + return NULL; +} const char* mkstringf(const char* format, ...) { - va_list list; - va_start(list, format); _SIMPLE_STRING buf = _simple_salloc(); - _simple_vsprintf(buf, format, list); - va_end(list); - const char* t = strdup(_simple_string(buf)); - _simple_sfree(buf); - return t; + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + return t; + } + return "mkstringf, out of memory error"; } void throwf(const char* format, ...) { - va_list list; - va_start(list, format); _SIMPLE_STRING buf = _simple_salloc(); - _simple_vsprintf(buf, format, list); - va_end(list); - const char* t = strdup(_simple_string(buf)); - _simple_sfree(buf); - throw t; + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + throw t; + } + throw "throwf, out of memory error"; } + +//#define ALTERNATIVE_LOGFILE "/dev/console" +static int sLogfile = STDERR_FILENO; + void log(const char* format, ...) { va_list list; va_start(list, format); - _simple_vdprintf(STDERR_FILENO, format, list); + _simple_vdprintf(sLogfile, format, list); va_end(list); } void warn(const char* format, ...) { - _simple_dprintf(STDERR_FILENO, "dyld: warning, "); + _simple_dprintf(sLogfile, "dyld: warning, "); va_list list; va_start(list, format); - _simple_vdprintf(STDERR_FILENO, format, list); + _simple_vdprintf(sLogfile, format, list); va_end(list); } @@ -344,29 +439,6 @@ static const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, c return NULL; } -#if IMAGE_NOTIFY_SUPPORT -// notify objc about these new images -static void notifyAdding(const ImageLoader* const * images, unsigned int count) -{ - // build array - if ( count != 0 ) { - dyld_image_info infos[count]; - for (unsigned int i=0; i < count; ++i) { - dyld_image_info* p = &infos[i]; - const ImageLoader* image = images[i]; - p->imageLoadAddress = image->machHeader(); - p->imageFilePath = image->getPath(); - p->imageFileModDate = image->lastModified(); - //dyld::log("notifying objc about %s\n", image->getPath()); - } - - // tell all interested images (after gdb, so you can debug anything the notification does) - for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { - (*it)->doNotification(dyld_image_adding, count, infos); - } - } -} -#endif static StateHandlers* stateToHandlers(dyld_image_states state, StateHandlers handlersArray[8]) { @@ -395,14 +467,16 @@ static StateHandlers* stateToHandlers(dyld_image_states state, StateHandlers han return NULL; } -static void notifySingle(dyld_image_states state, const struct mach_header* mh, const char* path, time_t modDate) + +static void notifySingle(dyld_image_states state, const ImageLoader* image) { + //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); std::vector* handlers = stateToHandlers(state, sSingleHandlers); if ( handlers != NULL ) { dyld_image_info info; - info.imageLoadAddress = mh; - info.imageFilePath = path; - info.imageFileModDate = modDate; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getPath(); + info.imageFileModDate = image->lastModified(); for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { const char* result = (*it)(state, 1, &info); if ( (result != NULL) && (state == dyld_image_state_mapped) ) { @@ -413,10 +487,20 @@ static void notifySingle(dyld_image_states state, const struct mach_header* mh, } } } +#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 ) { + coresymbolication_unload_image(connection, image); + } + } + } +#endif } - static int imageSorter(const void* l, const void* r) { const ImageLoader* left = *((ImageLoader**)l); @@ -482,6 +566,22 @@ 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 ) { + // This needs to be captured now + uint64_t load_timestamp = mach_absolute_time(); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + dyld_image_states imageState = (*it)->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + coresymbolication_load_image(connection, *it, load_timestamp); + } + } + } + } +#endif } static void notifyBatch(dyld_image_states state) @@ -500,14 +600,6 @@ static void addRootImage(ImageLoader* image) sImageRoots.push_back(image); } -#if IMAGE_NOTIFY_SUPPORT -// Objective-C will contain a __DATA/__image_notify section which contains pointers to a function to call -// whenever any new image is loaded. -static void addImageNeedingNotification(ImageLoader* image) -{ - sImagesToNotifyAboutOtherImages.push_back(image); -} -#endif static void clearAllDepths() { @@ -520,29 +612,6 @@ static unsigned int imageCount() return sAllImages.size(); } -static void notifySharedCacheInvalid() -{ - gSharedCacheNeedsUpdating = true; -} - - - -#if __i386__ -static void makeSharedCacheImportSegmentsWritable(bool writable) -{ - // if cache was built with read-only __IMPORT segments - if ( sImportSegmentsSize != 0 ) { - vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ; - if ( writable ) - prot |= VM_PROT_WRITE; - vm_protect(mach_task_self(), sImportSegmentsStart, sImportSegmentsSize, false, prot); - if ( gLinkContext.verboseMapping ) { - dyld::log("%18s at %p->%p altered permissions to %c%c%c\n", "", (char*)sImportSegmentsStart, (char*)sImportSegmentsStart+sImportSegmentsSize-1, - (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' ); - } - } -} -#endif static void setNewProgramVars(const ProgramVars& newVars) { @@ -568,14 +637,34 @@ static void addImage(ImageLoader* image) // add to master list sAllImages.push_back(image); + // update mapped ranges + uintptr_t lastSegStart = 0; + uintptr_t lastSegEnd = 0; + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + if ( image->segUnaccessible(i) ) + continue; + uintptr_t start = image->segActualLoadAddress(i); + uintptr_t end = image->segActualEndAddress(i); + if ( start == lastSegEnd ) { + // two segments are contiguous, just record combined segments + lastSegEnd = end; + } + else { + // non-contiguous segments, record last (if any) + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + lastSegStart = start; + lastSegEnd = end; + } + } + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + + if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { dyld::log("dyld: loaded: %s\n", image->getPath()); } -#if OLD_GDB_DYLD_INTERFACE - // let gdb find out about this - addImageForgdb(image->machHeader(), image->getSlide(), image->getPath(), image->getLogicalPath()); -#endif } void removeImage(ImageLoader* image) @@ -601,7 +690,7 @@ void removeImage(ImageLoader* image) } } - // tell all register add image handlers about this + // tell all registered remove image handlers about this // do this before removing image from internal data structures so that the callback can query dyld about the image if ( image->getState() >= dyld_image_state_bound ) { for (std::vector::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { @@ -609,16 +698,11 @@ void removeImage(ImageLoader* image) } } -#if IMAGE_NOTIFY_SUPPORT - // tell all interested images - for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { - dyld_image_info info; - info.imageLoadAddress = image->machHeader(); - info.imageFilePath = image->getPath(); - info.imageFileModDate = image->lastModified(); - (*it)->doNotification(dyld_image_removing, 1, &info); - } -#endif + // notify + notifySingle(dyld_image_state_terminated, image); + + // remove from mapped images table + removedMappedRanges(image); // remove from master list for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -632,16 +716,6 @@ void removeImage(ImageLoader* image) if ( sLastImageByAddressCache == image ) sLastImageByAddressCache = NULL; -#if IMAGE_NOTIFY_SUPPORT - // if in announcement list, pull it out - for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { - if ( *it == image ) { - sImagesToNotifyAboutOtherImages.erase(it); - break; - } - } -#endif - // if in root list, pull it out for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { if ( *it == image ) { @@ -657,12 +731,6 @@ void removeImage(ImageLoader* image) // tell gdb, new way removeImageFromAllImages(image->machHeader()); - -#if OLD_GDB_DYLD_INTERFACE - // tell gdb, old way - removeImageForgdb(image->machHeader()); - gdb_dyld_state_changed(); -#endif } @@ -683,12 +751,6 @@ void initializeMainExecutable() // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true; -#if __i386__ - // make all __IMPORT segments in the shared cache read-only - // before executing any code - makeSharedCacheImportSegmentsWritable(false); -#endif - // run initialzers for any inserted dylibs const int rootCount = sImageRoots.size(); if ( rootCount > 1 ) { @@ -725,7 +787,6 @@ void runTerminators(void* extra) for(unsigned int i=imageCount; i > 0; --i){ ImageLoader* image = sImageFilesNeedingTermination[i-1]; image->doTermination(gLinkContext); - notifySingle(dyld_image_state_terminated, image->machHeader(), image->getPath(), image->lastModified()); } sImageFilesNeedingTermination.clear(); notifyBatch(dyld_image_state_terminated); @@ -739,9 +800,11 @@ void runTerminators(void* extra) // static const char** parseColonList(const char* list) { - if ( list[0] == '\0' ) - return NULL; + static const char* sEmptyList[] = { NULL }; + if ( list[0] == '\0' ) + return sEmptyList; + int colonCount = 0; for(const char* s=list; *s != '\0'; ++s) { if (*s == ':') @@ -839,40 +902,33 @@ void processDyldEnvironmentVarible(const char* key, const char* value) { if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value); - gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value); - gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { sEnv.DYLD_LIBRARY_PATH = parseColonList(value); - gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value); - gSharedCacheDontNotify = true; } else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) { - gSharedCacheDontNotify = true; if ( strcmp(value, "/") != 0 ) { - sEnv.DYLD_ROOT_PATH = parseColonList(value); - for (int i=0; sEnv.DYLD_ROOT_PATH[i] != NULL; ++i) { - if ( sEnv.DYLD_ROOT_PATH[i][0] != '/' ) { + gLinkContext.rootPaths = parseColonList(value); + 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"); - sEnv.DYLD_ROOT_PATH = NULL; + gLinkContext.rootPaths = NULL; break; } } } } else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) { - gSharedCacheDontNotify = true; gLinkContext.imageSuffix = value; } else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value); - gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { sEnv.DYLD_PRINT_OPTS = true; @@ -902,7 +958,6 @@ void processDyldEnvironmentVarible(const char* key, const char* value) // ignore, no longer relevant but some scripts still set it } else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) { - gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) { gLinkContext.verbosePrebinding = true; @@ -922,6 +977,9 @@ void processDyldEnvironmentVarible(const char* key, const char* value) else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) { gLinkContext.verboseBind = true; } + else if ( strcmp(key, "DYLD_PRINT_WEAK_BINDINGS") == 0 ) { + gLinkContext.verboseWeakBind = true; + } else if ( strcmp(key, "DYLD_PRINT_REBASINGS") == 0 ) { gLinkContext.verboseRebase = true; } @@ -931,8 +989,10 @@ 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_SHARED_REGION") == 0 ) { - gSharedCacheDontNotify = true; if ( strcmp(value, "private") == 0 ) { gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; } @@ -949,8 +1009,15 @@ void processDyldEnvironmentVarible(const char* key, const char* value) dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); } } +#if DYLD_SHARED_CACHE_SUPPORT + else if ( strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0 ) { + sSharedCacheDir = value; + } + else if ( strcmp(key, "DYLD_SHARED_CACHE_DONT_VALIDATE") == 0 ) { + sSharedCacheIgnoreInodeAndTimeStamp = true; + } +#endif else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) { - gSharedCacheDontNotify = true; if ( strcmp(value, "all") == 0 ) { gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; } @@ -980,9 +1047,6 @@ void processDyldEnvironmentVarible(const char* key, const char* value) // static void pruneEnvironmentVariables(const char* envp[], const char*** applep) { - // setuit binaries don't trigger a cache rebuild - gSharedCacheDontNotify = true; - // delete all DYLD_* and LD_LIBRARY_PATH environment variables int removedCount = 0; const char** d = envp; @@ -1065,7 +1129,6 @@ static void getHostInfo() mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; mach_port_t hostPort = mach_host_self(); kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); - mach_port_deallocate(mach_task_self(), hostPort); if ( result != KERN_SUCCESS ) throw "host_info() failed"; @@ -1083,10 +1146,10 @@ static void getHostInfo() static void checkSharedRegionDisable() { - #if __ppc__ || __i386__ + #if !__LP64__ // if main executable has segments that overlap the shared region, // then disable using the shared region - if ( sMainExecutable->overlapsWithAddressRange((void*)0x90000000, (void*)0xAFFFFFFF) ) { + if ( sMainExecutable->overlapsWithAddressRange((void*)(uintptr_t)SHARED_REGION_BASE, (void*)(uintptr_t)(SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) { gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; if ( gLinkContext.verboseMapping ) dyld::warn("disabling shared region because main executable overlaps\n"); @@ -1119,66 +1182,28 @@ ImageLoader* getIndexedImage(unsigned int index) ImageLoader* findImageByMachHeader(const struct mach_header* target) { - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { - ImageLoader* anImage = sAllImages[i]; - if ( anImage->machHeader() == target ) - return anImage; - } - return NULL; + return findMappedRange((uintptr_t)target); } ImageLoader* findImageContainingAddress(const void* addr) { -#if FIND_STATS - static int cacheHit = 0; - static int cacheMiss = 0; - static int cacheNotMacho = 0; - if ( ((cacheHit+cacheMiss+cacheNotMacho) % 100) == 0 ) - dyld::log("findImageContainingAddress(): cache hit = %d, miss = %d, unknown = %d\n", cacheHit, cacheMiss, cacheNotMacho); -#endif - // first look in image where last address was found rdar://problem/3685517 - if ( (sLastImageByAddressCache != NULL) && sLastImageByAddressCache->containsAddress(addr) ) { -#if FIND_STATS - ++cacheHit; -#endif - return sLastImageByAddressCache; - } - // do exhastive search - // todo: consider maintaining a list sorted by address ranges and do a binary search on that - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { - ImageLoader* anImage = sAllImages[i]; - if ( anImage->containsAddress(addr) ) { - sLastImageByAddressCache = anImage; -#if FIND_STATS - ++cacheMiss; -#endif - return anImage; - } - } -#if FIND_STATS - ++cacheNotMacho; -#endif - return NULL; + return findMappedRange((uintptr_t)addr); } -ImageLoader* findImageContainingAddressThreadSafe(const void* addr) + +ImageLoader* findImageContainingSymbol(const void* symbol) { - // do exhastive search - // todo: consider maintaining a list sorted by address ranges and do a binary search on that - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { - ImageLoader* anImage = sAllImages[i]; - if ( anImage->containsAddress(addr) ) { + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + if ( anImage->containsSymbol(symbol) ) return anImage; - } } return NULL; } + void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData) { const unsigned int imageCount = sAllImages.size(); @@ -1285,6 +1310,7 @@ const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1; // +#if __ppc__ // // 32-bit PowerPC sub-type lists // @@ -1300,19 +1326,53 @@ static const cpu_subtype_t kPPC32[kPPC_RowCount][6] = { // G3 cannot run G4 or G5 code { CPU_SUBTYPE_POWERPC_750, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST } }; +#endif +#if __arm__ +// +// ARM sub-type lists +// +const int kARM_RowCount = 5; +static const cpu_subtype_t kARM[kARM_RowCount][6] = { + // armv7 can run: v7, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv6 can run: v6, v5, and v4 + { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // xscale can run: xscale, v5, and v4 + { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv5 can run: v5 and v4 + { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv4 can run: v4 + { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, +}; +#endif + // scan the tables above to find the cpu-sub-type-list for this machine static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype) { switch (cpu) { +#if __ppc__ case CPU_TYPE_POWERPC: for (int i=0; i < kPPC_RowCount ; ++i) { if ( kPPC32[i][0] == subtype ) return kPPC32[i]; } break; +#endif +#if __arm__ + case CPU_TYPE_ARM: + for (int i=0; i < kARM_RowCount ; ++i) { + if ( kARM[i][0] == subtype ) + return kARM[i]; + } + break; +#endif } return NULL; } @@ -1359,6 +1419,7 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) { switch (cpu) { +#if __ppc__ case CPU_TYPE_POWERPC: if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_POWERPC_ALL ) { *offset = OSSwapBigToHostInt32(archs[i].offset); @@ -1366,6 +1427,16 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* return true; } break; +#endif +#if __arm__ + case CPU_TYPE_ARM: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif } } } @@ -1420,7 +1491,7 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) // // This is used to validate if a non-fat (aka thin or raw) mach-o file can be used // on the current processor. // -bool isCompatibleMachO(const uint8_t* firstPage) +bool isCompatibleMachO(const uint8_t* firstPage, const char* path) { #if CPU_SUBTYPES_SUPPORTED // It is deemed compatible if any of the following are true: @@ -1440,7 +1511,7 @@ bool isCompatibleMachO(const uint8_t* firstPage) return true; } // have list and not in list, so not compatible - throw "incompatible cpu-subtype"; + throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path); } // unknown cpu sub-type, but if exact match for current subtype then ok to use if ( mh->cpusubtype == sHostCPUsubtype ) @@ -1480,11 +1551,11 @@ bool isCompatibleMachO(const uint8_t* firstPage) // The kernel maps in main executable before dyld gets control. We need to // make an ImageLoader* for the already mapped in main executable. -static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, uintptr_t slide, const char* path) +static ImageLoader* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) { // try mach-o loader - if ( isCompatibleMachO((const uint8_t*)mh) ) { - ImageLoader* image = new ImageLoaderMachO(mh, slide, path, gLinkContext); + if ( isCompatibleMachO((const uint8_t*)mh, path) ) { + ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); addImage(image); return image; } @@ -1493,6 +1564,40 @@ static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, uin } #if DYLD_SHARED_CACHE_SUPPORT +bool inSharedCache(const char* path) +{ + if ( sSharedCache != NULL ) { + struct stat stat_buf; + if ( stat(path, &stat_buf) == -1 ) + return false; + + // 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 + return true; + } + } + } + } + return false; +} + static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char* path) { if ( sSharedCache != NULL ) { @@ -1501,7 +1606,8 @@ static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char 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 ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { + 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); @@ -1514,7 +1620,7 @@ static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char } if ( cacheHit ) { // found image in cache, instantiate an ImageLoader with it - return new ImageLoaderMachO((struct mach_header*)(p->address), pathInCache, stat_buf, gLinkContext); + return ImageLoaderMachO::instantiateFromCache((macho_header*)(p->address), pathInCache, stat_buf, gLinkContext); } } } @@ -1534,7 +1640,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); - delete image; + ImageLoader::deleteImage(image); return anImage; } } @@ -1547,6 +1653,12 @@ static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& cont if ( context.mustBeDylib && !image->isDylib() ) throw "not a dylib"; + // regular main executables cannot be loaded + if ( image->isExecutable() ) { + if ( !context.canBePIE || !image->isPositionIndependentExecutable() ) + throw "can't load a main executable"; + } + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list if ( ! image->isBundle() ) addImage(image); @@ -1570,18 +1682,23 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, // min mach-o file is 4K if ( fileLength < 4096 ) { - pread(fd, firstPage, fileLength, 0); + if ( pread(fd, firstPage, fileLength, 0) != (ssize_t)fileLength ) + throwf("pread of short file failed: %d", errno); shortPage = true; } else { - pread(fd, firstPage, 4096,0); + if ( pread(fd, firstPage, 4096,0) != 4096 ) + throwf("pread of first 4K failed: %d", errno); } // if fat wrapper, find usable sub-file const fat_header* fileStartAsFat = (fat_header*)firstPage; if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { - pread(fd, firstPage, 4096, fileOffset); + if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) + throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); + if (pread(fd, firstPage, 4096, fileOffset) != 4096) + throwf("pread of fat file failed: %d", errno); } else { throw "no matching architecture in universal wrapper"; @@ -1589,12 +1706,12 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, } // try mach-o loader - if ( isCompatibleMachO(firstPage) ) { + if ( isCompatibleMachO(firstPage, path) ) { if ( shortPage ) throw "file too short"; // instantiate an image - ImageLoader* image = new ImageLoaderMachO(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext); + ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext); // validate return checkandAddImage(image, context); @@ -1653,9 +1770,15 @@ static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, // open file (automagically closed when this function exits) FileOpener file(path); - // just return NULL if file not found - if ( file.getFileDescriptor() == -1 ) - return NULL; + // 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); @@ -1674,9 +1797,11 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context //dyld::log("%s(%s)\n", __func__ , path); // search path against load-path and install-path of all already loaded images uint32_t hash = ImageLoader::hash(path); + //dyld::log("check() hash=%d, path=%s\n", hash, path); for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* anImage = *it; - // check has first to cut down on strcmp calls + // check hash first to cut down on strcmp calls + //dyld::log(" check() hash=%d, path=%s\n", anImage->getPathHash(), anImage->getPath()); if ( anImage->getPathHash() == hash ) if ( strcmp(path, anImage->getPath()) == 0 ) { // if we are looking for a dylib don't return something else @@ -1737,9 +1862,8 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std ImageLoader* image = NULL; if ( strncmp(path, "@executable_path/", 17) == 0 ) { // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305 - if ( sMainExecutableIsSetuid ) { - throwf("unsafe use of @executable_path in %s with setuid binary", context.origin); - } + if ( sProcessIsRestricted ) + throwf("unsafe use of @executable_path in %s with restricted binary", context.origin); // handle @executable_path path prefix const char* executablePath = sExecPath; char newPath[strlen(executablePath) + strlen(path)]; @@ -1770,9 +1894,8 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std } else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) { // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305 - if ( sMainExecutableIsSetuid && (strcmp(context.origin, sExecPath) == 0) ) - throwf("unsafe use of @loader_path in %s with setuid binary", context.origin); - + if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) ) + throwf("unsafe use of @loader_path in %s with restricted binary", context.origin); // handle @loader_path path prefix char newPath[strlen(context.origin) + strlen(path)]; strcpy(newPath, context.origin); @@ -1829,8 +1952,8 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std if ( (exceptions != NULL) && (trailingPath != path) ) return NULL; } - else if ( sMainExecutableIsSetuid && (path[0] != '/') ) { - throwf("unsafe use of relative rpath %s in %s with setuid binary", path, context.origin); + else if (sProcessIsRestricted && (path[0] != '/' )) { + throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); } return loadPhase4(path, context, exceptions); @@ -1903,8 +2026,11 @@ static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std return image; // try fallback paths during second time (will open file) - if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL)) ) { - image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, sEnv.DYLD_FALLBACK_LIBRARY_PATH, exceptions); + const char* const* fallbackLibraryPaths = sEnv.DYLD_FALLBACK_LIBRARY_PATH; + if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths ) + fallbackLibraryPaths = NULL; + if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) { + image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions); if ( image != NULL ) return image; } @@ -1918,8 +2044,8 @@ static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); // handle DYLD_ROOT_PATH which forces absolute paths to use a new root - if ( (sEnv.DYLD_ROOT_PATH != NULL) && (path[0] == '/') ) { - for(const char* const* rootPath = sEnv.DYLD_ROOT_PATH ; *rootPath != NULL; ++rootPath) { + if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) { + for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) { char newPath[strlen(*rootPath) + strlen(path)+2]; strcpy(newPath, *rootPath); strcat(newPath, path); @@ -1967,8 +2093,12 @@ ImageLoader* load(const char* path, const LoadContext& context) image = loadPhase0(path, context, &exceptions); if ( image != NULL ) return image; - else if ( exceptions.size() == 0 ) - throw "image not found"; + else if ( exceptions.size() == 0 ) { + if ( context.dontLoad ) + return NULL; + else + throw "image not found"; + } else { const char* msgStart = "no suitable image found. Did find:"; const char* delim = "\n\t"; @@ -2021,13 +2151,7 @@ static int __attribute__((noinline)) _shared_region_map_np(int fd, uint32_t coun } // remove the shared region sub-map -#if __ppc__ || __i386__ - vm_address_t addr = (vm_address_t)0x90000000; - vm_deallocate(mach_task_self(), addr, 0x20000000); -#elif __ppc64__ || __x86_64__ - vm_address_t addr = (vm_address_t)0x7FFF60000000; - vm_deallocate(mach_task_self(), addr, 0x80000000); -#endif + vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); // map cache just for this process with mmap() bool failed = false; @@ -2090,17 +2214,21 @@ const void* imMemorySharedCacheHeader() int openSharedCacheFile() { + char path[1024]; + strcpy(path, sSharedCacheDir); + strcat(path, "/"); #if __ppc__ - // rosetta cannot handle optimized _ppc cache, so it use _rosetta cache instead, rdar://problem/5495438 - if ( isRosetta() ) - return ::open(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_ROSETTA, O_RDONLY); - else + // rosetta cannot handle optimized _ppc cache, so it use _rosetta cache instead, rdar://problem/5495438 + if ( isRosetta() ) + strcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_ROSETTA); + else #endif - return ::open(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, O_RDONLY); + strcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); + return ::open(path, O_RDONLY); } static void mapSharedCache() -{ +{ uint64_t cacheBaseAddress; // quick check if a cache is alreay mapped into shared region if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { @@ -2113,14 +2241,16 @@ static void mapSharedCache() } } else { +#if __i386__ || __x86_64__ // Safe Boot should disable dyld shared cache // if we are in safe-boot mode and the cache was not made during this boot cycle, - // delete the cache file and let it be regenerated + // delete the cache file uint32_t safeBootValue = 0; size_t safeBootValueSize = sizeof(safeBootValue); if ( (sysctlbyname("kern.safeboot", &safeBootValue, &safeBootValueSize, NULL, 0) == 0) && (safeBootValue != 0) ) { // user booted machine in safe-boot mode struct stat dyldCacheStatInfo; + // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path if ( ::stat(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { struct timeval bootTimeValue; size_t bootTimeValueSize = sizeof(bootTimeValue); @@ -2129,20 +2259,20 @@ static void mapSharedCache() if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) { ::unlink(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - gSharedCacheNotFound = true; return; } } } } +#endif // map in shared cache to shared region int fd = openSharedCacheFile(); if ( fd != -1 ) { - uint8_t firstPage[4096]; - if ( ::read(fd, firstPage, 4096) == 4096 ) { - dyld_cache_header* header = (dyld_cache_header*)firstPage; + uint8_t firstPages[8192]; + 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*)&firstPage[header->mappingOffset]; + const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPages[header->mappingOffset]; const shared_file_mapping_np* const end = &mappings[header->mappingCount]; // validate that the cache file has not been truncated bool goodCache = false; @@ -2152,37 +2282,51 @@ static void mapSharedCache() for (const shared_file_mapping_np* p = mappings; p < end; ++p) { // 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->sfm_file_offset+p->sfm_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; + } + } + } + // sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache + if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) { + bool foundLibSystem = false; + if ( stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { + const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset]; + const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount]; + for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) { + if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { + foundLibSystem = true; + break; + } + } + } + if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) { + dyld::log("dyld: shared cached file was build against a different libSystem.dylib, ignoring cache\n"); + goodCache = false; } } + if ( goodCache ) { - const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPage[header->mappingOffset]; + 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 sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; } } - else { - gSharedCacheNeedsUpdating = true; - dyld::log("dyld: shared cached file is corrupt: " DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n"); - } } else { - gSharedCacheNeedsUpdating = true; if ( gLinkContext.verboseMapping ) dyld::log("dyld: shared cached file is invalid\n"); } } else { - gSharedCacheNeedsUpdating = true; if ( gLinkContext.verboseMapping ) dyld::log("dyld: shared cached file cannot be read\n"); } close(fd); } else { - gSharedCacheNotFound = true; if ( gLinkContext.verboseMapping ) dyld::log("dyld: shared cached file cannot be opened\n"); } @@ -2202,9 +2346,9 @@ static void mapSharedCache() dyld_shared_cache_ranges.sharedRegionsCount = 4; if ( gLinkContext.verboseMapping ) { if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) - dyld::log("dyld: Mapping shared cache\n"); + 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\n"); + 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]; int index = 0; @@ -2218,12 +2362,18 @@ static void mapSharedCache() ((p->sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), p->sfm_init_prot, p->sfm_max_prot); } #if __i386__ - // record if a non-writable and executable region is found in the R/W shared region - // this is the __IMPORT segments. dyld will turn write protection on and off as needed + // 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) ) { - sImportSegmentsStart = p->sfm_address; - sImportSegmentsSize = p->sfm_size; - makeSharedCacheImportSegmentsWritable(true); + if ( p->sfm_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); + 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, + (prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' ); + } + } } #endif } @@ -2237,8 +2387,6 @@ static void mapSharedCache() // create when NSLinkModule is called for a second time on a bundle ImageLoader* cloneImage(ImageLoader* image) { - const uint64_t offsetInFat = image->getOffsetInFatFile(); - // open file (automagically closed when this function exits) FileOpener file(image->getPath()); @@ -2246,24 +2394,19 @@ ImageLoader* cloneImage(ImageLoader* image) if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) throw "stat error"; - // read first page of file - uint8_t firstPage[4096]; - pread(file.getFileDescriptor(), firstPage, 4096, offsetInFat); - - // fat length is only used for sanity checking, since this image was already loaded once, just use upper bound - uint64_t lenInFat = stat_buf.st_size - offsetInFat; - - // try mach-o loader - if ( isCompatibleMachO(firstPage) ) { - ImageLoader* clone = new ImageLoaderMachO(image->getPath(), file.getFileDescriptor(), firstPage, offsetInFat, lenInFat, stat_buf, gLinkContext); - // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list - if ( ! image->isBundle() ) - addImage(clone); - return clone; - } - - // try other file formats... - throw "can't clone image"; + dyld::LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = true; + context.mustBeDylib = false; + context.canBePIE = false; + context.origin = false; + context.rpath = false; + return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); } @@ -2284,8 +2427,8 @@ ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* module } // try each loader - if ( isCompatibleMachO(mem) ) { - ImageLoader* image = new ImageLoaderMachO(moduleName, (mach_header*)mem, len, gLinkContext); + if ( isCompatibleMachO(mem, moduleName) ) { + ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list if ( ! image->isBundle() ) addImage(image); @@ -2334,8 +2477,7 @@ void clearErrorMessage() void setErrorMessage(const char* message) { // save off error message in global buffer for CrashReporter to find - strncpy(error_string, message, sizeof(error_string)-1); - error_string[sizeof(error_string)-1] = '\0'; + strlcpy(error_string, message, sizeof(error_string)); } const char* getErrorMessage() @@ -2344,12 +2486,14 @@ const char* getErrorMessage() } -void halt(const char* message) +void halt(const char* message) { dyld::log("dyld: %s\n", message); setErrorMessage(message); - strncpy(error_string, message, sizeof(error_string)-1); - error_string[sizeof(error_string)-1] = '\0'; + uintptr_t terminationFlags = 0; + if ( !gLinkContext.startedInitializingMainExecutable ) + terminationFlags = 1; + setAlImageInfosHalt(error_string, terminationFlags); dyld_fatal_error(error_string); } @@ -2368,7 +2512,7 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) #if __i386__ // fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer) if ( mh == NULL ) - target = dyld::findImageContainingAddressThreadSafe(lazyPointer); + target = dyld::findImageContainingAddress(lazyPointer); else target = dyld::findImageByMachHeader(mh); #else @@ -2393,12 +2537,36 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) } -// SPI used by ZeroLink to lazy load bundles -void registerZeroLinkHandlers(BundleNotificationCallBack notify, BundleLocatorCallBack locate) +#if COMPRESSED_DYLD_INFO_SUPPORT +uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) { - sBundleNotifier = notify; - sBundleLocation = locate; + uintptr_t result = 0; + // get image + if ( *imageLoaderCache == NULL ) { + // save in cache + *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache); + if ( *imageLoaderCache == NULL ) { + const char* message = "fast lazy binding from unknown image"; + dyld::log("dyld: %s\n", message); + halt(message); + } + } + + // bind lazy pointer and return it + try { + result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext); + } + catch (const char* message) { + dyld::log("dyld: lazy symbol binding failed: %s\n", message); + halt(message); + } + + // return target address to glue which jumps to it with real parameters restored + return result; } +#endif // COMPRESSED_DYLD_INFO_SUPPORT + + void registerUndefinedHandler(UndefinedHandler handler) { @@ -2414,48 +2582,6 @@ static void undefinedHandler(const char* symboName) static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) { - // try ZeroLink short cut to finding bundle which exports this symbol - if ( sBundleLocation != NULL ) { - ImageLoader* zlImage = (*sBundleLocation)(name); - if ( zlImage == ((ImageLoader*)(-1)) ) { - // -1 is magic value that request symbol is in a bundle not yet linked into process - // try calling handler to link in that symbol - undefinedHandler(name); - // call locator again - zlImage = (*sBundleLocation)(name); - } - // if still not found, then ZeroLink has no idea where to find it - if ( zlImage == ((ImageLoader*)(-1)) ) - return false; - if ( zlImage != NULL ) { - // ZeroLink cache knows where the symbol is - if ( onlyInCoalesced ) { - // but ZeroLink does not know about coalescing weak symbols, so ignore ZeroLink's hint when onlyInCoalesced==true - } - else { - *sym = zlImage->findExportedSymbol(name, NULL, false, image); - if ( *sym != NULL ) { - *image = zlImage; - return true; - } - } - } - else { - // ZeroLink says it is in some bundle already loaded, but not linked, walk them all - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i){ - ImageLoader* anImage = sAllImages[i]; - if ( anImage->isBundle() && !anImage->hasHiddenExports() ) { - //dyld::log("dyld: search for %s in %s\n", name, anImage->getPath()); - *sym = anImage->findExportedSymbol(name, NULL, false, image); - if ( *sym != NULL ) { - return true; - } - } - } - } - } - // search all images in order const ImageLoader* firstWeakImage = NULL; const ImageLoader::Symbol* firstWeakSym = NULL; @@ -2471,7 +2597,7 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima anImage = sAllImages[0]; } if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) { - *sym = anImage->findExportedSymbol(name, NULL, false, image); + *sym = anImage->findExportedSymbol(name, false, image); if ( *sym != NULL ) { // if weak definition found, record first one found if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) { @@ -2516,7 +2642,7 @@ bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstri ImageLoader* anImage = sAllImages[i]; // only look at images whose paths contain the hint string (NULL hint string is wildcard) if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) { - *sym = anImage->findExportedSymbol(name, NULL, false, image); + *sym = anImage->findExportedSymbol(name, false, image); if ( *sym != NULL ) { return true; } @@ -2525,6 +2651,20 @@ bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstri return false; } +unsigned int getCoalescedImages(ImageLoader* images[]) +{ + unsigned int count = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->participatesInCoalescing() ) { + *images++ = *it; + ++count; + } + } + return count; +} + + static ImageLoader::MappedRegion* getMappedRegions(ImageLoader::MappedRegion* regions) { ImageLoader::MappedRegion* end = regions; @@ -2584,17 +2724,18 @@ void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_st } } -static ImageLoader* libraryLocator(const char* libraryName, bool search, bool findDLL, const char* origin, const ImageLoader::RPathChain* rpaths) +static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths) { dyld::LoadContext context; context.useSearchPaths = search; + context.useFallbackPaths = search; context.useLdLibraryPath = false; context.implicitRPath = false; context.matchByInstallName = false; context.dontLoad = false; context.mustBeBundle = false; context.mustBeDylib = true; - context.findDLL = findDLL; + context.canBePIE = false; context.origin = origin; context.rpath = rpaths; return load(libraryName, context); @@ -2610,17 +2751,14 @@ static const char* basename(const char* path) return last; } -static void setContext(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +static void setContext(const macho_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) { gLinkContext.loadLibrary = &libraryLocator; gLinkContext.terminationRecorder = &terminationRecorder; gLinkContext.flatExportFinder = &flatFindExportedSymbol; gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol; + gLinkContext.getCoalescedImages = &getCoalescedImages; gLinkContext.undefinedHandler = &undefinedHandler; -#if IMAGE_NOTIFY_SUPPORT - gLinkContext.addImageNeedingNotification = &addImageNeedingNotification; - gLinkContext.notifyAdding = ¬ifyAdding; -#endif gLinkContext.getAllMappedRegions = &getMappedRegions; gLinkContext.bindingHandler = NULL; gLinkContext.notifySingle = ¬ifySingle; @@ -2629,11 +2767,10 @@ static void setContext(const struct mach_header* mainExecutableMH, int argc, con gLinkContext.registerDOFs = ®isterDOFs; gLinkContext.clearAllDepths = &clearAllDepths; gLinkContext.imageCount = &imageCount; - gLinkContext.notifySharedCacheInvalid= ¬ifySharedCacheInvalid; -#if __i386__ - gLinkContext.makeSharedCacheImportSegmentsWritable = &makeSharedCacheImportSegmentsWritable; -#endif gLinkContext.setNewProgramVars = &setNewProgramVars; +#if DYLD_SHARED_CACHE_SUPPORT + gLinkContext.inSharedCache = &inSharedCache; +#endif #if SUPPORT_OLD_CRT_INITIALIZATION gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; #endif @@ -2669,6 +2806,53 @@ bool isRosetta() } #endif + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_segment_command segment_command + #define macho_section section +#endif + + +// +// Look for a special segment in the mach header. +// Its presences means that the binary wants to have DYLD ignore +// DYLD_ environment variables. +// +static bool hasRestrictedSegment(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + + //dyld::log("seg name: %s\n", seg->segname); + if (strcmp(seg->segname, "__RESTRICT") == 0) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (strcmp(sect->sectname, "__restrict") == 0) + return true; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} + + #if 0 static void printAllImages() { @@ -2693,15 +2877,6 @@ void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChai if ( !image->isLinked() ) addRootImage(image); - // notify ZeroLink of new image with concat of logical and physical name - if ( sBundleNotifier != NULL && image->isBundle() ) { - const int logicalLen = strlen(image->getLogicalPath()); - char logAndPhys[strlen(image->getPath())+logicalLen+2]; - strcpy(logAndPhys, image->getLogicalPath()); - strcpy(&logAndPhys[logicalLen+1], image->getPath()); - (*sBundleNotifier)(logAndPhys, image); - } - // process images try { image->link(gLinkContext, forceLazysBound, false, loaderRPaths); @@ -2710,11 +2885,6 @@ void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChai garbageCollectImages(); throw; } - -#if OLD_GDB_DYLD_INTERFACE - // notify gdb that loaded libraries have changed - gdb_dyld_state_changed(); -#endif } @@ -2737,7 +2907,7 @@ void garbageCollectImages() //dyld::log("garbageCollectImages: deleting %s\n", image->getPath()); image->setBeingRemoved(); removeImage(image); - delete image; + ImageLoader::deleteImage(image); } catch (const char* msg) { dyld::warn("problem deleting image: %s\n", msg); @@ -2755,7 +2925,7 @@ static void preflight_finally(ImageLoader* image) { if ( image->isBundle() ) { removeImageFromAllImages(image->machHeader()); - delete image; + ImageLoader::deleteImage(image); } sBundleBeingLoaded = NULL; dyld::garbageCollectImages(); @@ -2782,13 +2952,14 @@ static void loadInsertedDylib(const char* path) try { LoadContext context; context.useSearchPaths = false; + context.useFallbackPaths = false; context.useLdLibraryPath = false; context.implicitRPath = false; context.matchByInstallName = false; context.dontLoad = false; context.mustBeBundle = false; context.mustBeDylib = true; - context.findDLL = false; + context.canBePIE = false; context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES context.rpath = NULL; image = load(path, context); @@ -2806,10 +2977,18 @@ static void loadInsertedDylib(const char* path) // Returns address of main() in target program which __dyld_start jumps to // uintptr_t -_main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]) +_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]) { - setContext(mainExecutableMH, argc, argv, envp, apple); +#ifdef ALTERNATIVE_LOGFILE + sLogfile = open(ALTERNATIVE_LOGFILE, O_WRONLY | O_CREAT | O_APPEND); + if ( sLogfile == -1 ) { + sLogfile = STDERR_FILENO; + dyld::log("error opening alternate log file %s, errno = %d\n", ALTERNATIVE_LOGFILE, errno); + } +#endif + setContext(mainExecutableMH, argc, argv, envp, apple); + // Pickup the pointer to the exec path. sExecPath = apple[0]; bool ignoreEnvironmentVariables = false; @@ -2837,8 +3016,12 @@ _main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, } uintptr_t result = 0; sMainExecutableMachHeader = mainExecutableMH; - sMainExecutableIsSetuid = issetugid(); - if ( sMainExecutableIsSetuid ) + 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 ) pruneEnvironmentVariables(envp, &apple); else checkEnvironmentVariables(envp, ignoreEnvironmentVariables); @@ -2850,18 +3033,24 @@ _main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, // install gdb notifier stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB); // make initial allocations large enough that it is unlikely to need to be re-alloced - sAllImages.reserve(200); + sAllImages.reserve(INITIAL_IMAGE_COUNT); sImageRoots.reserve(16); sAddImageCallbacks.reserve(4); sRemoveImageCallbacks.reserve(4); sImageFilesNeedingTermination.reserve(16); sImageFilesNeedingDOFUnregistration.reserve(8); +#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE + // Add gating mechanism to dyld support system order file generation process + WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld_all_image_infos.systemOrderFlag); +#endif + try { // instantiate ImageLoader for main executable sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); sMainExecutable->setNeverUnload(); gLinkContext.mainExecutable = sMainExecutable; + gLinkContext.processIsRestricted = sProcessIsRestricted; // load shared cache checkSharedRegionDisable(); #if DYLD_SHARED_CACHE_SUPPORT @@ -2910,6 +3099,14 @@ _main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, dyld::log("dyld: launch failed\n"); } +#ifdef ALTERNATIVE_LOGFILE + // only use alternate log during launch, otherwise file is open forever + if ( sLogfile != STDERR_FILENO ) { + close(sLogfile); + sLogfile = STDERR_FILENO; + } +#endif + return result; } diff --git a/src/dyld.exp b/src/dyld.exp index 4412747..22a1f6c 100644 --- a/src/dyld.exp +++ b/src/dyld.exp @@ -1,5 +1,5 @@ # -# Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2004-2008 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,10 +23,10 @@ # # Only the following symbols should be "global". -# gdb and vmutils lookup these symbols in dyld in order to determine what images the process is using +# gdb and Symbolication lookup these symbols in dyld in order to determine what images the process is using # -# Mac OS X 10.4 way to discover a process's images +# gdb and Symbolication look at these to discover a process's loaded images _dyld_all_image_infos _dyld_shared_cache_ranges @@ -38,17 +38,3 @@ _dyld_fatal_error _dyldVersionString _dyldVersionNumber -# Entry points into dyld needed by loaded every image -_stub_binding_helper -_dyld_func_lookup - -# Old symbols left for compatibility -_gdb_dyld_version -_gdb_dyld_state_changed -_gdb_nobject_images -_gdb_object_image_size -_gdb_nlibrary_images -_gdb_library_image_size -_object_images -_library_images -_send_event diff --git a/src/dyld.h b/src/dyld.h index 1c030c3..2deab7d 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -38,13 +38,14 @@ namespace dyld { struct LoadContext { bool useSearchPaths; + bool useFallbackPaths; bool useLdLibraryPath; bool implicitRPath; bool matchByInstallName; bool dontLoad; bool mustBeBundle; bool mustBeDylib; - bool findDLL; + bool canBePIE; const char* origin; // path for expanding @loader_path const ImageLoader::RPathChain* rpath; // paths for expanding @rpath }; @@ -52,24 +53,18 @@ namespace dyld { typedef void (*ImageCallback)(const struct mach_header* mh, intptr_t slide); - typedef void (*BundleNotificationCallBack)(const char* imageName, ImageLoader* image); - typedef ImageLoader* (*BundleLocatorCallBack)(const char* symbolName); typedef void (*UndefinedHandler)(const char* symbolName); typedef const char* (*ImageLocator)(const char* dllName); extern ImageLoader::LinkContext gLinkContext; extern bool gLogAPIs; - extern bool gSharedCacheNotFound; - extern bool gSharedCacheNeedsUpdating; - extern bool gSharedCacheDontNotify; extern const struct LibSystemHelpers* gLibSystemHelpers; #if SUPPORT_OLD_CRT_INITIALIZATION extern bool gRunInitializersOldWay; #endif extern void registerAddCallback(ImageCallback func); extern void registerRemoveCallback(ImageCallback func); - extern void registerZeroLinkHandlers(BundleNotificationCallBack, BundleLocatorCallBack); extern void registerUndefinedHandler(UndefinedHandler); extern void initializeMainExecutable(); extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths); @@ -82,6 +77,7 @@ namespace dyld { extern uint32_t getImageCount(); extern ImageLoader* findImageByMachHeader(const struct mach_header* target); extern ImageLoader* findImageContainingAddress(const void* addr); + extern ImageLoader* findImageContainingSymbol(const void* symbol); extern ImageLoader* findImageByName(const char* path); extern ImageLoader* findLoadedImageByInstallPath(const char* path); extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image); @@ -91,7 +87,7 @@ namespace dyld { extern void removeImage(ImageLoader* image); extern ImageLoader* cloneImage(ImageLoader* image); extern void forEachImageDo( void (*)(ImageLoader*, void*), void*); - extern uintptr_t _main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]); + extern uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]); extern void halt(const char* message) __attribute__((noreturn)); extern void setErrorMessage(const char* msg); extern const char* getErrorMessage(); @@ -102,9 +98,10 @@ namespace dyld { 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(); - extern void registerWinImageLocator(ImageLocator); extern int openSharedCacheFile(); extern const void* imMemorySharedCacheHeader(); + extern uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset); + extern bool inSharedCache(const char* path); }; diff --git a/src/dyld64.exp b/src/dyld64.exp deleted file mode 100644 index 1c2a031..0000000 --- a/src/dyld64.exp +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright (c) 2005-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@ -# - -# -# Only the following symbols should be "global". -# gdb and vmutils lookup these symbols in dyld in order to determine what images the process is using -# - -# Mac OS X 10.4 way to discover a process's images -_dyld_all_image_infos -_dyld_shared_cache_ranges - -# CrashReporter uses this to get message as to why dyld terminated the process -_error_string -_dyld_fatal_error - -# Used by various tools to see build number of dyld -_dyldVersionString -_dyldVersionNumber diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index c9f0e97..609f0b3 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.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-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -42,13 +43,12 @@ #include #include #include +#include // for task_self_trap() -extern "C" mach_port_name_t task_self_trap(void); // can't include because it is missing extern C #include "mach-o/dyld_images.h" #include "mach-o/dyld.h" #include "mach-o/dyld_priv.h" -#include "mach-o/dyld-update-prebinding.h" #include "ImageLoader.h" #include "dyld.h" @@ -58,14 +58,19 @@ extern "C" mach_port_name_t task_self_trap(void); // can't include sObjectFileImages; // -static void dyldAPIhalt(const char* apiName, const char* errorMsg) -{ - dyld::log("dyld: %s() error\n", apiName); - dyld::halt(errorMsg); -} - - static void setLastError(NSLinkEditErrors code, int errnum, const char* file, const char* message) { @@ -236,6 +253,7 @@ static void setLastError(NSLinkEditErrors code, int errnum, const char* file, co sLastErrorNo = errnum; } +#endif // DEPRECATED_APIS_SUPPORTED /* *_dyld_NSGetExecutablePath is the dyld side of _NSGetExecutablePath which @@ -260,6 +278,100 @@ int _NSGetExecutablePath(char* buf, uint32_t *bufsize) return 0; } +uint32_t _dyld_image_count(void) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + return dyld::getImageCount(); +} + +const struct mach_header* _dyld_get_image_header(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + ImageLoader* image = dyld::getIndexedImage(image_index); + if ( image != NULL ) + return (struct mach_header*)image->machHeader(); + else + return NULL; +} + +intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + ImageLoader* image = dyld::getIndexedImage(image_index); + if ( image != NULL ) + return image->getSlide(); + else + return 0; +} + +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, mh); + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image != NULL ) + return image->getSlide(); + else + return 0; +} + + +const char* _dyld_get_image_name(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + ImageLoader* image = dyld::getIndexedImage(image_index); + if ( image != NULL ) + return image->getPath(); + else + return NULL; +} + +const struct mach_header * _dyld_get_image_header_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->machHeader(); + return NULL; +} + + +void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerAddCallback(func); +} + +void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerRemoveCallback(func); +} + + + +// called by crt before main() by programs linked with 10.4 or earlier crt1.o +static void _dyld_make_delayed_module_initializer_calls() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + +#if SUPPORT_OLD_CRT_INITIALIZATION + if ( dyld::gRunInitializersOldWay ) + dyld::initializeMainExecutable(); +#endif +} + + + +#if DEPRECATED_APIS_SUPPORTED // // _dyld_call_module_initializers_for_dylib() is the dyld side of @@ -387,23 +499,7 @@ NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libra return NULL; } -uint32_t _dyld_image_count(void) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - return dyld::getImageCount(); -} -const struct mach_header* _dyld_get_image_header(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - ImageLoader* image = dyld::getIndexedImage(image_index); - if ( image != NULL ) - return (struct mach_header*)image->machHeader(); - else - return NULL; -} static __attribute__((noinline)) @@ -423,13 +519,14 @@ const struct mach_header* addImage(void* callerAddress, const char* path, bool s } dyld::LoadContext context; context.useSearchPaths = search; + context.useFallbackPaths = search; context.useLdLibraryPath = false; context.implicitRPath = false; context.matchByInstallName = matchInstallName; context.dontLoad = dontLoad; context.mustBeBundle = false; context.mustBeDylib = true; - context.findDLL = false; + context.canBePIE = false; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path context.rpath = &callersRPaths; // rpaths from caller and main executable @@ -509,12 +606,13 @@ bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symb dyld::log("%s(%p, \"%s\")\n", __func__, (void *)mh, symbolName); ImageLoader* image = dyld::findImageByMachHeader(mh); if ( image != NULL ) { - if ( image->findExportedSymbol(symbolName, NULL, true, NULL) != NULL) + if ( image->findExportedSymbol(symbolName, true, NULL) != NULL) return true; } return false; } + NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) { if ( dyld::gLogAPIs ) @@ -536,7 +634,7 @@ NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolN dyldAPIhalt(__func__, msg); } } - symbol = image->findExportedSymbol(symbolName, NULL, true, NULL); + symbol = image->findExportedSymbol(symbolName, true, NULL); } if ( dyld::gLogAPIs && (symbol == NULL) ) dyld::log("%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); @@ -576,7 +674,7 @@ const char* NSNameOfSymbol(NSSymbol symbol) if ( dyld::gLogAPIs ) dyld::log("%s(%p)\n", __func__, (void *)symbol); const char* result = NULL; - ImageLoader* image = dyld::findImageContainingAddress(symbol); + ImageLoader* image = dyld::findImageContainingSymbol(symbol); if ( image != NULL ) result = image->getExportedSymbolName(NSSymbolToSymbol(symbol)); return result; @@ -586,8 +684,10 @@ void* NSAddressOfSymbol(NSSymbol symbol) { if ( dyld::gLogAPIs ) dyld::log("%s(%p)\n", __func__, (void *)symbol); + if ( symbol == NULL ) + return NULL; void* result = NULL; - ImageLoader* image = dyld::findImageContainingAddress(symbol); + ImageLoader* image = dyld::findImageContainingSymbol(symbol); if ( image != NULL ) result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); return result; @@ -598,42 +698,20 @@ NSModule NSModuleForSymbol(NSSymbol symbol) if ( dyld::gLogAPIs ) dyld::log("%s(%p)\n", __func__, (void *)symbol); NSModule result = NULL; - ImageLoader* image = dyld::findImageContainingAddress(symbol); + ImageLoader* image = dyld::findImageContainingSymbol(symbol); if ( image != NULL ) result = ImageLoaderToNSModule(image); return result; } -intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - ImageLoader* image = dyld::getIndexedImage(image_index); - if ( image != NULL ) - return image->getSlide(); - else - return 0; -} - -const char* _dyld_get_image_name(uint32_t image_index) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%u)\n", __func__, image_index); - ImageLoader* image = dyld::getIndexedImage(image_index); - if ( image != NULL ) - return image->getLogicalPath(); - else - return NULL; -} - bool _dyld_all_twolevel_modules_prebound(void) { if ( dyld::gLogAPIs ) dyld::log("%s()\n", __func__); - return FALSE; // fixme + return FALSE; } void _dyld_bind_objc_module(const void *objc_module) @@ -690,13 +768,14 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName dyld::LoadContext context; context.useSearchPaths = false; + context.useFallbackPaths = false; context.useLdLibraryPath = false; context.implicitRPath = false; context.matchByInstallName = false; context.dontLoad = false; context.mustBeBundle = true; context.mustBeDylib = false; - context.findDLL = false; + context.canBePIE = false; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path context.rpath = NULL; // support not yet implemented @@ -769,7 +848,7 @@ bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) // and we should delete it bool linkedImage = dyld::validImage(objectFileImage->image); if ( ! linkedImage ) { - delete objectFileImage->image; + ImageLoader::deleteImage(objectFileImage->image); objectFileImage->image = NULL; } } @@ -785,7 +864,16 @@ bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) // if object was created from a memory, release that memory // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands over ownership of the memory to dyld if ( objectFileImage->imageBaseAddress != NULL ) { - vm_deallocate(mach_task_self(), (vm_address_t)objectFileImage->imageBaseAddress, objectFileImage->imageLength); + bool freed = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 6) ) { + size_t sz = (*dyld::gLibSystemHelpers->malloc_size)(objectFileImage->imageBaseAddress); + if ( sz != 0 ) { + (*dyld::gLibSystemHelpers->free)((void*)(objectFileImage->imageBaseAddress)); + freed = true; + } + } + if ( ! freed ) + vm_deallocate(mach_task_self(), (vm_address_t)objectFileImage->imageBaseAddress, objectFileImage->imageLength); } // free ofi object @@ -832,7 +920,7 @@ const char * NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFile dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedImportedSymbol(ordinal); if ( tentative_definition != NULL ) { - ImageLoader::ReferenceFlags flags = objectFileImage->image->geImportedSymbolInfo(sym); + ImageLoader::ReferenceFlags flags = objectFileImage->image->getImportedSymbolInfo(sym); if ( (flags & ImageLoader::kTentativeDefinition) != 0 ) *tentative_definition = true; else @@ -863,34 +951,10 @@ bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const { if ( dyld::gLogAPIs ) dyld::log("%s(%p,%s)\n", __func__, objectFileImage, symbolName); - const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, NULL, true, NULL); + const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, true, NULL); return ( sym != NULL ); } -/* - * Given an imageOffset into an ObjectFileImage, returns - * the segment/section name and offset into that section of - * that imageOffset. Returns FALSE if the imageOffset is not - * in any section. You can used the resulting sectionOffset to - * index into the data returned by NSGetSectionDataInObjectFileImage. - * - * First appeared in Mac OS X 10.3 - * - * SPI: currently only used by ZeroLink to detect +load methods - */ -bool -NSFindSectionAndOffsetInObjectFileImage(NSObjectFileImage objectFileImage, - unsigned long imageOffset, - const char** segmentName, /* can be NULL */ - const char** sectionName, /* can be NULL */ - unsigned long* sectionOffset) /* can be NULL */ -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, objectFileImage); - - return objectFileImage->image->findSection(((char*)(objectFileImage->image->machHeader()))+imageOffset, segmentName, sectionName, sectionOffset); -} - NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) @@ -904,24 +968,12 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, // each link causes the bundle to be copied to a new address if ( objectFileImage->image->isLinked() ) { // already linked, so clone a new one and link it -#if 0 - dyld::warn("%s(0x%08X, \"%s\", 0x%08X) called more than once for 0x%08X\n", - __func__, objectFileImage, moduleName, options, objectFileImage); -#endif objectFileImage->image = dyld::cloneImage(objectFileImage->image); } - - // if this ofi was made with NSCreateObjectFileImageFromFile() then physical path is already set - // if this ofi was create with NSCreateObjectFileImageFromMemory() then the phyiscal path should be set if supplied - if ( (options & NSLINKMODULE_OPTION_TRAILING_PHYS_NAME) != 0 ) { - if ( objectFileImage->imageBaseAddress != NULL ) { - const char* physEnd = &moduleName[strlen(moduleName)+1]; - objectFileImage->image->setPath(physEnd); - } - } - - // set moduleName as the name anyone calling _dyld_get_image_name() will see - objectFileImage->image->setLogicalPath(moduleName); + + // for memory based images, set moduleName as the name anyone calling _dyld_get_image_name() will see + if ( objectFileImage->image->getPath() == NULL ) + objectFileImage->image->setPath(moduleName); // support private bundles if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) @@ -933,13 +985,13 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, // load libraries, rebase, bind, to make this image usable dyld::link(objectFileImage->image, forceLazysBound, ImageLoader::RPathChain(NULL,NULL)); + // bump reference count to keep this bundle from being garbage collected + objectFileImage->image->incrementDlopenReferenceCount(); + // run initializers unless magic flag says not to if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) dyld::runInitializers(objectFileImage->image); - // bump reference count to keep this bundle from being garbage collected - objectFileImage->image->incrementDlopenReferenceCount(); - return ImageLoaderToNSModule(objectFileImage->image); } catch (const char* msg) { @@ -965,15 +1017,9 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s ImageLoader* image = NULL; dyld::clearErrorMessage(); try { - const char* imageName = moduleName; - if ( (options & NSLINKMODULE_OPTION_TRAILING_PHYS_NAME) != 0 ) - imageName = &moduleName[strlen(moduleName)+1]; - + const char* imageName = moduleName; image = dyld::loadFromMemory((const uint8_t*)object_addr, object_size, imageName); - if ( (options & NSLINKMODULE_OPTION_TRAILING_PHYS_NAME) != 0 ) - image->setLogicalPath(moduleName); - if ( image != NULL ) { // support private bundles if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) @@ -998,7 +1044,7 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s // if image was created for this bundle, destroy it if ( image != NULL ) { dyld::removeImage(image); - delete image; + ImageLoader::deleteImage(image); } image = NULL; free((void*)msg); @@ -1014,7 +1060,7 @@ NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) ImageLoader* image = NSModuleToImageLoader(module); if ( image == NULL ) return NULL; - return SymbolToNSSymbol(image->findExportedSymbol(symbolName, NULL, false, NULL)); + return SymbolToNSSymbol(image->findExportedSymbol(symbolName, false, NULL)); } const char* NSNameOfModule(NSModule module) @@ -1063,7 +1109,7 @@ bool NSUnLinkModule(NSModule module, uint32_t options) found = true; } if ( !found ) - delete image; + ImageLoader::deleteImage(image); return true; } @@ -1078,43 +1124,7 @@ static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEd // no support for multiple or linkedit handlers } -const struct mach_header * _dyld_get_image_header_containing_address(const void* address) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, address); - ImageLoader* image = dyld::findImageContainingAddress(address); - if ( image != NULL ) - return image->machHeader(); - return NULL; -} - - -void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)func); - dyld::registerAddCallback(func); -} -void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p)\n", __func__, (void *)func); - dyld::registerRemoveCallback(func); -} - - -// called by crt before main -static void _dyld_make_delayed_module_initializer_calls() -{ - if ( dyld::gLogAPIs ) - dyld::log("%s()\n", __func__); - -#if SUPPORT_OLD_CRT_INITIALIZATION - if ( dyld::gRunInitializersOldWay ) - dyld::initializeMainExecutable(); -#endif -} void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString) @@ -1126,6 +1136,8 @@ void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileNam *errorString = dyld::getErrorMessage(); } + + static void _dyld_register_binding_handler(void * (*bindingHandler)(const char *, const char *, void *), ImageLoader::BindingOptions bindingOptions) { if ( dyld::gLogAPIs ) @@ -1134,6 +1146,8 @@ static void _dyld_register_binding_handler(void * (*bindingHandler)(const char * dyld::gLinkContext.bindingOptions = bindingOptions; } +#endif //DEPRECATED_APIS_SUPPORTED + // Call by fork() in libSystem after the kernel trap is done on the child side static void _dyld_fork_child() @@ -1149,6 +1163,13 @@ static void _dyld_fork_child() // extern mach_port_t mach_task_self_; mach_task_self_ = task_self_trap(); + + // If dyld is sending load/unload notices to CoreSymbolication, the shared memory + // page is not copied on fork. + // NULL the CoreSymbolication shared memory pointer to prevent a crash. + dyld_all_image_infos.coreSymbolicationShmPage = NULL; + // for safety, make sure child starts with clean systemOrderFlag + dyld_all_image_infos.systemOrderFlag = 0; } @@ -1175,6 +1196,7 @@ void _dyld_moninit(MonitorProc proc) dyld::forEachImageDo(&monInitCallback, (void*)proc); } +#if DEPRECATED_APIS_SUPPORTED // returns true if prebinding was used in main executable bool _dyld_launched_prebound() { @@ -1205,7 +1227,7 @@ static bool NSMakePrivateModulePublic(NSModule module) return false; } - +#endif // DEPRECATED_APIS_SUPPORTED bool lookupDyldFunction(const char* name, uintptr_t* address) { @@ -1221,59 +1243,12 @@ bool lookupDyldFunction(const char* name, uintptr_t* address) return false; } -static bool readOnlyBootVolume() -{ - struct statfs sfs; - if ( statfs("/", &sfs) == 0 ) { - return ( sfs.f_flags & MNT_RDONLY ); - } - // if statfs() fails, something is wrong, so don't rebuild dyld cache - return true; -} - - static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) { dyld::gLibSystemHelpers = helpers; -#if DYLD_SHARED_CACHE_SUPPORT - // notify dyld server if something is wrong with the shared cache - //dyld::log("helpers->version=%lu, gSharedCacheDontNotify=%d, gSharedCacheNotFound=%d, gSharedCacheNeedsUpdating=%d, readOnlyBootVolume()=%d\n", - // helpers->version, dyld::gSharedCacheDontNotify, dyld::gSharedCacheNotFound, dyld::gSharedCacheNeedsUpdating, readOnlyBootVolume()); - if ( (helpers != NULL) && (helpers->version >= 3) && !dyld::gSharedCacheDontNotify ) { - if ( dyld::gSharedCacheNotFound || dyld::gSharedCacheNeedsUpdating ) { - if ( ! readOnlyBootVolume() ) { - if ( dyld::gSharedCacheNotFound ) - (*helpers->dyld_shared_cache_missing)(); - else if ( dyld::imMemorySharedCacheHeader() == NULL ) { - // since shared cache is not mapped and not missing, it must be - // corrupt. Don't need to test contents, just - // ping launchd to start update_dyld_shared_cache - // rdar://problem/5694507 - (*helpers->dyld_shared_cache_out_of_date)(); - } - else if ( dyld::gSharedCacheNeedsUpdating ) { - // To reduce the storm of messages to update_dyld_shared_cache - // don't message if the cache file is missing (which means the cache is in - // the process of being regenerated) or if the contents of the cache file - // don't match what is already in memory (which means the cache has - // already be regenerated). - int fd = dyld::openSharedCacheFile(); - if ( fd != -1 ) { - uint8_t onDiskCache[4096]; - if ( ::read(fd, onDiskCache, 4096) == 4096 ) { - if ( ::memcmp(onDiskCache, dyld::imMemorySharedCacheHeader(), 4096) == 0 ) { - // ping launchd to start update_dyld_shared_cache - (*helpers->dyld_shared_cache_out_of_date)(); - } - } - ::close(fd); - } - } - } - } - } -#endif + // let gdb know it is safe to run code in inferior that might call malloc() + dyld_all_image_infos.libSystemInitialized = true; } @@ -1305,6 +1280,13 @@ bool dlopen_preflight(const char* path) dlerrorClear(); +#if DYLD_SHARED_CACHE_SUPPORT + // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + // if requested path is to something in the dyld shared cache, always succeed + if ( dyld::inSharedCache(path) ) + return true; +#endif + bool result = false; std::vector rpathsFromCallerImage; try { @@ -1319,15 +1301,18 @@ bool dlopen_preflight(const char* path) } ImageLoader* image = NULL; + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); dyld::LoadContext context; context.useSearchPaths = true; - context.useLdLibraryPath= (strchr(path, '/') == NULL); // a leafname implies should search - context.implicitRPath = (path[0] != '/'); // a non-absolute path implies try rpath searching + context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching context.matchByInstallName = true; context.dontLoad = false; context.mustBeBundle = false; context.mustBeDylib = false; - context.findDLL = false; + context.canBePIE = true; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path context.rpath = &callersRPaths; // rpaths from caller and main executable @@ -1389,15 +1374,18 @@ void* dlopen(const char* path, int mode) dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); } + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); dyld::LoadContext context; context.useSearchPaths = true; - context.useLdLibraryPath= (strchr(path, '/') == NULL); // a leafname implies should search - context.implicitRPath = (path[0] != '/'); // a non-absolute path implies try rpath searching + context.useFallbackPaths= leafName; // a partial path means no fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching context.matchByInstallName = true; context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); context.mustBeBundle = false; context.mustBeDylib = false; - context.findDLL = ( (mode & RTLD_DLL) != 0 ); + context.canBePIE = true; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path context.rpath = &callersRPaths; // rpaths from caller and main executable @@ -1469,6 +1457,11 @@ void* dlopen(const char* path, int mode) free((void*)str); } + // when context.dontLoad is set, load() returns NULL instead of throwing an exception + if ( (mode & RTLD_NOLOAD) && (result == NULL) ) { + dlerrorSet("image not already loaded"); + } + if ( lockHeld ) dyld::gLibSystemHelpers->releaseGlobalDyldLock(); return result; @@ -1514,30 +1507,24 @@ int dladdr(const void* address, Dl_info* info) ImageLoader* image = dyld::findImageContainingAddress(address); if ( image != NULL ) { - info->dli_fname = image->getLogicalPath(); - info->dli_fbase = (void*)image->machHeader(); - // find closest exported symbol in the image - const uint32_t exportCount = image->getExportedSymbolCount(); - const ImageLoader::Symbol* bestSym = NULL; - const void* bestAddr = 0; - for(uint32_t i=0; i < exportCount; ++i) { - const ImageLoader::Symbol* sym = image->getIndexedExportedSymbol(i); - const void* symAddr = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); - if ( (symAddr <= address) && (bestAddr < symAddr) ) { - bestSym = sym; - bestAddr = symAddr; - } + info->dli_fname = image->getPath(); + info->dli_fbase = (void*)image->machHeader(); + if ( address == info->dli_fbase ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + return 1; // success } - if ( bestSym != NULL ) { - info->dli_sname = image->getExportedSymbolName(bestSym); + // find closest symbol in the image + info->dli_sname = image->findClosestSymbol(address, (const void**)&info->dli_saddr); + if ( info->dli_sname != NULL ) { if ( info->dli_sname[0] == '_' ) info->dli_sname = info->dli_sname +1; // strip off leading underscore - info->dli_saddr = (void*)bestAddr; - } - else { - info->dli_sname = NULL; - info->dli_saddr = NULL; + //dyld::log("dladdr(%p) => %p %s\n", address, info->dli_saddr, info->dli_sname); + return 1; // success } + info->dli_sname = NULL; + info->dli_saddr = NULL; return 1; // success } return 0; // failure @@ -1590,7 +1577,7 @@ void* dlsym(void* handle, const char* symbolName) // magic "search only main executable" handle if ( handle == RTLD_MAIN_ONLY ) { image = dyld::mainExecutable(); - sym = image->findExportedSymbol(underscoredName, NULL, true, &image); // search RTLD_FIRST way + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way if ( sym != NULL ) { return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } @@ -1630,7 +1617,7 @@ void* dlsym(void* handle, const char* symbolName) image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits if ( dyld::validImage(image) ) { if ( (((uintptr_t)handle) & 1) != 0 ) - sym = image->findExportedSymbol(underscoredName, NULL, true, &image); // search RTLD_FIRST way + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way else sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against @@ -1649,64 +1636,6 @@ void* dlsym(void* handle, const char* symbolName) -void* dlord(void* handle, uint32_t ordinal) -{ - if ( dyld::gLogAPIs ) - dyld::log("%s(%p, %u)\n", __func__, handle, ordinal); - - dlerrorClear(); - - - // magic "search all" handle - if ( handle == RTLD_DEFAULT ) { - const char* str = dyld::mkstringf("dlord(RTLD_DEFAULT, %u): not supported", ordinal); - dlerrorSet(str); - free((void*)str); - return NULL; - } - // magic "search only main executable" handle - if ( handle == RTLD_MAIN_ONLY ) { - const char* str = dyld::mkstringf("dlord(RTLD_MAIN_ONLY, %u): not supported", ordinal); - dlerrorSet(str); - free((void*)str); - return NULL; - } - // magic "search what I would see" handle - if ( handle == RTLD_NEXT ) { - const char* str = dyld::mkstringf("dlord(RTLD_NEXT, %u): not supported", ordinal); - dlerrorSet(str); - free((void*)str); - return NULL; - } - // magic "search me, then what I would see" handle - if ( handle == RTLD_SELF ) { - const char* str = dyld::mkstringf("dlord(RTLD_SELF, %u): not supported", ordinal); - dlerrorSet(str); - free((void*)str); - return NULL; - } - - // real handle - const ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits - if ( dyld::validImage(image) ) { - const ImageLoader::Symbol* sym; - sym = image->getIndexedExportedSymbol(ordinal-1); // map ordinals to mach-o zero based index - - if ( sym != NULL ) { - return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); - } - const char* str = dyld::mkstringf("dlord(%p, %u): ordinal not found", handle, ordinal); - dlerrorSet(str); - free((void*)str); - } - else { - dlerrorSet("invalid handle passed to dlord()"); - } - return NULL; -} - - - @@ -1714,11 +1643,25 @@ void* dlord(void* handle, uint32_t ordinal) -static const struct dyld_all_image_infos* _dyld_get_all_image_infos() +const struct dyld_all_image_infos* _dyld_get_all_image_infos() { return &dyld_all_image_infos; } +#if !__arm__ +static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + //if ( dyld::gLogAPIs ) + // dyld::log("%s(%p, %p)\n", __func__, addr, info); + + ImageLoader* image = dyld::findImageContainingAddress(addr); + if ( image != NULL ) { + image->getUnwindInfo(info); + return true; + } + return false; +} +#endif void dyld_register_image_state_change_handler(dyld_image_states state, bool batch, @@ -1733,9 +1676,15 @@ void dyld_register_image_state_change_handler(dyld_image_states state, bool batc } +const char* dyld_image_path_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); - - - + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->getPath(); + return NULL; +} diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index fbc172f..e14a6ef 100644 --- a/src/dyldAPIsInLibSystem.cpp +++ b/src/dyldAPIsInLibSystem.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,7 +24,10 @@ #include #include +#include + #include +#include #include "mach-o/dyld.h" #include "mach-o/dyld_priv.h" @@ -33,6 +36,14 @@ extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); +#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__) + +// deprecated APIs are still availble on Mac OS X, but not on iPhone OS +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define DEPRECATED_APIS_SUPPORTED 0 +#else + #define DEPRECATED_APIS_SUPPORTED 1 +#endif /* * names_match() takes an install_name from an LC_LOAD_DYLIB command and a @@ -87,6 +98,8 @@ const char* libraryName) return(FALSE); } +#if DEPRECATED_APIS_SUPPORTED + void NSInstallLinkEditErrorHandlers( const NSLinkEditErrorHandlers* handlers) { @@ -294,6 +307,7 @@ uint32_t options) _dyld_func_lookup("__dyld_NSAddImage", (void**)&p); return(p(image_name, options)); } +#endif // DEPRECATED_APIS_SUPPORTED /* * This routine returns the current version of the named shared library the @@ -385,6 +399,7 @@ const char* libraryName) return(-1); } +#if DEPRECATED_APIS_SUPPORTED /* * NSCreateObjectFileImageFromFile() creates an NSObjectFileImage for the * specified file name if the file is a correct Mach-O file that can be loaded @@ -477,31 +492,6 @@ uint32_t options) } -/* - * NSFindSectionAndOffsetInObjectFileImage() takes the specified imageOffset - * into the specified ObjectFileImage and returns the segment/section name and - * offset into that section of that imageOffset. Returns FALSE if the - * imageOffset is not in any section. You can used the resulting sectionOffset - * to index into the data returned by NSGetSectionDataInObjectFileImage. - * - * SPI: currently only used by ZeroLink to detect +load methods - */ -bool -NSFindSectionAndOffsetInObjectFileImage( -NSObjectFileImage objectFileImage, -unsigned long imageOffset, -const char** segmentName, /* can be NULL */ -const char** sectionName, /* can be NULL */ -unsigned long* sectionOffset) /* can be NULL */ -{ - DYLD_LOCK_THIS_BLOCK; - static bool (*p)(NSObjectFileImage, unsigned long, const char**, const char**, unsigned long*) = NULL; - - if(p == NULL) - _dyld_func_lookup("__dyld_NSFindSectionAndOffsetInObjectFileImage", (void**)&p); - - return p(objectFileImage, imageOffset, segmentName, sectionName, sectionOffset); -} /* @@ -620,24 +610,6 @@ unsigned long *size) /* can be NULL */ return p(objectFileImage, segmentName, sectionName, size); } -/* - * NSHasModInitObjectFileImage() returns TRUE if the NSObjectFileImage has any - * module initialization sections and FALSE it it does not. - * - * SPI: currently only used by ZeroLink to detect C++ initializers - */ -bool -NSHasModInitObjectFileImage( -NSObjectFileImage objectFileImage) -{ - DYLD_LOCK_THIS_BLOCK; - static bool (*p)(NSObjectFileImage) = NULL; - - if(p == NULL) - _dyld_func_lookup("__dyld_NSHasModInitObjectFileImage", (void**)&p); - - return p(objectFileImage); -} void NSLinkEditError( @@ -683,6 +655,9 @@ uint32_t options) } #endif + +#endif // DEPRECATED_APIS_SUPPORTED + /* *_NSGetExecutablePath copies the path of the executable into the buffer and * returns 0 if the path was successfully copied in the provided buffer. If the @@ -705,6 +680,7 @@ uint32_t *bufsize) return(p(buf, bufsize)); } +#if DEPRECATED_APIS_SUPPORTED void _dyld_lookup_and_bind( const char* symbol_name, @@ -775,6 +751,7 @@ const void* address) _dyld_func_lookup("__dyld_bind_fully_image_containing_address", (void**)&p); return p(address); } +#endif // DEPRECATED_APIS_SUPPORTED /* @@ -903,12 +880,14 @@ _dyld_bind_objc_module(const void* objc_module) } #endif +#if DEPRECATED_APIS_SUPPORTED bool _dyld_present(void) { // this function exists for compatiblity only return true; } +#endif uint32_t _dyld_image_count(void) @@ -954,6 +933,18 @@ _dyld_get_image_name(uint32_t image_index) return(p(image_index)); } +// SPI in Mac OS X 10.6 +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static intptr_t (*p)(const struct mach_header*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_slide", (void**)&p); + return(p(mh)); +} + + bool _dyld_image_containing_address(const void* address) { @@ -988,6 +979,7 @@ void (*monaddition)(char *lowpc, char *highpc)) p(monaddition); } +#if DEPRECATED_APIS_SUPPORTED bool _dyld_launched_prebound(void) { DYLD_LOCK_THIS_BLOCK; @@ -1007,6 +999,7 @@ bool _dyld_all_twolevel_modules_prebound(void) _dyld_func_lookup("__dyld_all_twolevel_modules_prebound", (void**)&p); return(p()); } +#endif // DEPRECATED_APIS_SUPPORTED #include @@ -1016,7 +1009,6 @@ bool _dyld_all_twolevel_modules_prebound(void) #include #include #include "dyldLibSystemInterface.h" -#include "dyld_shared_cache_user.h" // pthread key used to access per-thread dlerror message @@ -1061,54 +1053,30 @@ static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) } - -static bool get_update_dyld_shared_cache_bootstrap_port(mach_port_t& mp) -{ - static bool found = false; - static mach_port_t port; - if ( found ) { - mp = port; - return true; - } - if ( bootstrap_look_up(bootstrap_port, "com.apple.dyld", &port) == KERN_SUCCESS ) { - found = true; - mp = port; - return true; - } - return false; -} - -#if __ppc__ - #define CUR_ARCH CPU_TYPE_POWERPC -#elif __ppc64__ - #define CUR_ARCH CPU_TYPE_POWERPC64 -#elif __i386__ - #define CUR_ARCH CPU_TYPE_I386 -#elif __x86_64__ - #define CUR_ARCH CPU_TYPE_X86_64 -#endif - +#if DYLD_SHARED_CACHE_SUPPORT static void shared_cache_missing() { - mach_port_t mp; - if ( get_update_dyld_shared_cache_bootstrap_port(mp) ) - dyld_shared_cache_missing(mp, CUR_ARCH, 0); + // leave until dyld's that might call this are rare } static void shared_cache_out_of_date() { - mach_port_t mp; - if ( get_update_dyld_shared_cache_bootstrap_port(mp) ) - dyld_shared_cache_out_of_date(mp, CUR_ARCH, 0); + // leave until dyld's that might call this are rare } +#endif // DYLD_SHARED_CACHE_SUPPORT - -// that table passed to dyld containing thread helpers -static dyld::LibSystemHelpers sHelpers = { 4, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, +// the table passed to dyld containing thread helpers +static dyld::LibSystemHelpers sHelpers = { 6, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, + #if DYLD_SHARED_CACHE_SUPPORT &shared_cache_missing, &shared_cache_out_of_date, - NULL, NULL }; + #else + NULL, NULL, + #endif + NULL, NULL, + &pthread_key_create, &pthread_setspecific, + &malloc_size }; // @@ -1208,6 +1176,52 @@ void dyld_register_image_state_change_handler(dyld_image_states state, } +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static struct dyld_all_image_infos* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p); + return p(); +} + +#if !__arm__ +__attribute__((visibility("hidden"))) +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, dyld_unwind_sections*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + return p(addr, info); +} +#endif + + +#if __i386__ || __x86_64__ +__attribute__((visibility("hidden"))) +void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, long) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p); + return p(loadercache, lazyinfo); +} +#endif + + +const char* dyld_image_path_containing_address(const void* addr) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)(const void*) = NULL; + if(p == NULL) + _dyld_func_lookup("__dyld_image_path_containing_address", (void**)&p); + return p(addr); +} diff --git a/src/dyldExceptions.c b/src/dyldExceptions.c index 133e978..0cf1a18 100644 --- a/src/dyldExceptions.c +++ b/src/dyldExceptions.c @@ -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-2008 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,6 +26,130 @@ #include #include #include +#include +#include +#include +#include + +#include "mach-o/dyld_priv.h" +#include "dyldLibSystemInterface.h" + +extern void _ZN4dyld3logEPKcz(const char*, ...); +extern struct LibSystemHelpers* _ZN4dyld17gLibSystemHelpersE; + + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_header mach_header_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 + #define getsectdatafromheader getsectdatafromheader_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_header mach_header + #define macho_segment_command segment_command + #define macho_section section +#endif + + +#if __i386__ || __x86_64 || __ppc__ + +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) +{ + // record location of unwind sections in dyld itself + sDyldInfo.mh = mh; + 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, "__TEXT") == 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, "__eh_frame") == 0 ) { + sDyldInfo.dwarf_section = (void*)(sect[j].addr + slide); + sDyldInfo.dwarf_section_length = sect[j].size; + } + else if ( strcmp(sect[j].sectname, "__unwind_info") == 0 ) { + sDyldInfo.compact_unwind_section = (void*)(sect[j].addr + slide); + sDyldInfo.compact_unwind_section_length = sect[j].size; + } + } + sDyldTextEnd = (void*)(seg->vmaddr + seg->vmsize + slide); + } + } + cmd = (struct load_command*)( (char*)cmd + cmd->cmdsize ); + } +} + +// called by libuwind code to find unwind information in dyld +bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) +{ + if ( ((void*)sDyldInfo.mh < addr) && (addr < sDyldTextEnd) ) { + *info = sDyldInfo; + return true; + } + else { + return false; + } +} + + + +// 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 + + + + + +#else /* __i386__ || __x86_64 || __ppc__ */ + + + + + // // BEGIN copy of code from libgcc.a source file unwind-dw2-fde-darwin.c @@ -158,18 +282,6 @@ void _keymgr_set_per_thread_data(unsigned int key, void *keydata) dyld_abort(); } -#if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 - #define macho_header mach_header_64 - #define macho_segment_command segment_command_64 - #define macho_section section_64 - #define getsectdatafromheader getsectdatafromheader_64 -#else - #define LC_SEGMENT_COMMAND LC_SEGMENT - #define macho_header mach_header - #define macho_segment_command segment_command - #define macho_section section -#endif // 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) @@ -209,4 +321,7 @@ const void* getsectdatafromheader(struct mach_header* mh, const char* segname, c } #endif +#endif + + diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index 61b23b3..a112d18 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -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-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -45,13 +45,11 @@ #if __LP64__ - #define macho_header mach_header_64 #define LC_SEGMENT_COMMAND LC_SEGMENT_64 #define macho_segment_command segment_command_64 #define macho_section section_64 #define RELOC_SIZE 3 #else - #define macho_header mach_header #define LC_SEGMENT_COMMAND LC_SEGMENT #define macho_segment_command segment_command #define macho_section section @@ -219,13 +217,19 @@ static void segmentProtectDyld(const struct macho_header* mh, intptr_t slide) // // re-map the main executable to a new random address // -static const struct mach_header* randomizeExecutableLoadAddress(const struct mach_header* orgMH, uintptr_t* appsSlide) +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; @@ -267,6 +271,8 @@ static const struct mach_header* randomizeExecutableLoadAddress(const struct mac // 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 @@ -287,11 +293,13 @@ static const struct mach_header* randomizeExecutableLoadAddress(const struct mac 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 exectable\n"); + dyld::warn("could not relocate position independent executable\n"); return orgMH; } } @@ -300,7 +308,7 @@ static const struct mach_header* randomizeExecutableLoadAddress(const struct mac // run with newly mapped executable *appsSlide = newBaseAddress - lowestAddressUsed; - return (const struct mach_header*)newBaseAddress; + return (const struct macho_header*)newBaseAddress; } // can't get new range, so don't slide to random address @@ -327,7 +335,7 @@ 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 mach_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) { // _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); @@ -339,11 +347,7 @@ uintptr_t start(const struct mach_header* appsMachHeader, int argc, const char* } uintptr_t appsSlide = 0; - - // set pthread keys to dyld range - __pthread_tsd_first = 1; - _pthread_keys_init(); - + // enable C++ exceptions to work inside dyld dyld_exceptions_init(dyldsMachHeader, slide); @@ -366,7 +370,7 @@ uintptr_t start(const struct mach_header* appsMachHeader, int argc, const char* // if main executable was linked -pie, then randomize its load address if ( appsMachHeader->flags & MH_PIE ) - appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, &appsSlide); + appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, envp, &appsSlide); // now that we are done bootstrapping dyld, call dyld's main return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple); diff --git a/src/dyldLibSystemGlue.c b/src/dyldLibSystemGlue.c new file mode 100644 index 0000000..2628673 --- /dev/null +++ b/src/dyldLibSystemGlue.c @@ -0,0 +1,49 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * 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 + +// +// 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. + +struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); }; + +static struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL }; + + +__attribute__((weak, 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 c110616..94062fc 100644 --- a/src/dyldLibSystemInterface.h +++ b/src/dyldLibSystemInterface.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@ * @@ -28,8 +28,9 @@ #include - +#if __cplusplus namespace dyld { +#endif // // This file contains the synchronization utilities used by dyld to be thread safe. // This struct is implemented in in libSystem (where pthreads is available) @@ -51,9 +52,15 @@ namespace dyld { // addded in version 4 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*); + // added in version 6 + size_t (*malloc_size)(const void *ptr); }; +#if __cplusplus }; - +#endif diff --git a/src/dyldLock.cpp b/src/dyldLock.cpp index b94551b..dad1390 100644 --- a/src/dyldLock.cpp +++ b/src/dyldLock.cpp @@ -30,6 +30,9 @@ static pthread_mutex_t sGlobalMutex; +// Need a way to determine if a gdb call to dlopen() would block +int __attribute__((visibility("hidden"))) _dyld_global_lock_held = 0; + // // This initializer can go away once the following is available: @@ -46,21 +49,23 @@ void dyldGlobalLockInitialize() LockHelper::LockHelper() { - pthread_mutex_lock(&sGlobalMutex); + dyldGlobalLockAcquire(); } LockHelper::~LockHelper() { - pthread_mutex_unlock(&sGlobalMutex); + dyldGlobalLockRelease(); } void dyldGlobalLockAcquire() { pthread_mutex_lock(&sGlobalMutex); + _dyld_global_lock_held = 1; } void dyldGlobalLockRelease() { + _dyld_global_lock_held = 0; pthread_mutex_unlock(&sGlobalMutex); } diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index 4a5dcad..fe828ee 100644 --- a/src/dyldNew.cpp +++ b/src/dyldNew.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,6 +26,7 @@ #include #include #include +#include extern "C" void* __dso_handle; @@ -39,13 +40,24 @@ extern "C" void* __dso_handle; #if __LP64__ // room for about ~1000 initial dylibs - #define DYLD_INITIAL_POOL_SIZE 400*1024 + #define DYLD_POOL_CHUNK_SIZE 200*1024 #else // room for about ~900 initial dylibs - #define DYLD_INITIAL_POOL_SIZE 200*1024 + #define DYLD_POOL_CHUNK_SIZE 150*1024 #endif -static uint8_t dyldPool[DYLD_INITIAL_POOL_SIZE]; -static uint8_t* curPoolPtr = dyldPool; + +struct dyld_static_pool { + dyld_static_pool* previousPool; + uint8_t* current; + uint8_t* end; + uint8_t pool[1]; +}; + +// allocate initial pool independently of pool header to take less space on disk +static uint8_t initialPoolContent[DYLD_POOL_CHUNK_SIZE] __attribute__((__aligned__(16))); +static dyld_static_pool initialPool = { NULL, initialPoolContent, &initialPoolContent[DYLD_POOL_CHUNK_SIZE] }; +static dyld_static_pool* currentPool = &initialPool; + void* malloc(size_t size) { @@ -56,13 +68,29 @@ void* malloc(size_t size) } else { size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align - uint8_t* result = curPoolPtr; - if ( (curPoolPtr + size) > &dyldPool[DYLD_INITIAL_POOL_SIZE] ) { - dyld::log("initial dyld memory pool exhausted\n"); - _exit(1); + uint8_t* result = currentPool->current; + currentPool->current += size; + if ( currentPool->current > currentPool->end ) { + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, DYLD_POOL_CHUNK_SIZE, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + dyld::log("out of address space for dyld memory pool\n"); + exit(1); + } + dyld_static_pool* newPool = (dyld_static_pool*)addr; + newPool->previousPool = NULL; + newPool->current = newPool->pool; + newPool->end = (uint8_t*)(addr + DYLD_POOL_CHUNK_SIZE); + newPool->previousPool = currentPool; + currentPool = newPool; + if ( (currentPool->current + size) > currentPool->end ) { + dyld::log("dyld memory pool exhausted: size=%lu\n", size); + exit(1); + } + result = currentPool->current; + currentPool->current += size; } - curPoolPtr += size; - //dyld::log("%p = malloc(%lu) from pool, total = %d\n", result, size, curPoolPtr-dyldPool); + //dyld::log("%p = malloc(%3lu) from pool %p, free space = %lu\n", result, size, currentPool, (long)(currentPool->end - currentPool->current)); return result; } } @@ -71,13 +99,22 @@ void* malloc(size_t size) void free(void* ptr) { // ignore any pointer within dyld (i.e. stuff from pool or static strings) - if ( (dyld::gLibSystemHelpers != NULL) && ((ptr < &__dso_handle) || (ptr >= &dyldPool[DYLD_INITIAL_POOL_SIZE])) ) { + if ( (dyld::gLibSystemHelpers != NULL) && ((ptr < &__dso_handle) || (ptr >= &initialPoolContent[DYLD_POOL_CHUNK_SIZE])) ) { + // ignore stuff in any dynamically alloated dyld pools + for (dyld_static_pool* p = initialPool.previousPool; p != NULL; p = p->previousPool) { + if ( (p->pool < ptr) && (ptr < p->end) ) { + // do nothing, pool entries can't be reclaimed + //dyld::log("free(%p) from dynamic pool\n", ptr); + return; + } + } + //dyld::log("free(%p) from libSystem\n", ptr); return dyld::gLibSystemHelpers->free(ptr); } else { // do nothing, pool entries can't be reclaimed - //dyld::log("free(%p) from pool\n", ptr); + //dyld::log("free(%p) from static pool\n", ptr); } } @@ -85,8 +122,8 @@ void free(void* ptr) void* calloc(size_t count, size_t size) { if ( dyld::gLibSystemHelpers != NULL ) { - void* result = dyld::gLibSystemHelpers->malloc(size); - bzero(result, size); + void* result = dyld::gLibSystemHelpers->malloc(size*count); + bzero(result, size*count); return result; } else { diff --git a/src/dyldStartup.s b/src/dyldStartup.s index 1bf7931..e1214f8 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -63,9 +63,21 @@ * Where arg[i] and env[i] point into the STRING AREA */ - .globl __dyld_start + // Hack to make _offset_to_dyld_all_image_infos work + // Without this local symbol, assembler will error out about in subtraction expression + // The real _dyld_all_image_infos (non-weak) _dyld_all_image_infos is defined in dyld_gdb.o + // and the linker with throw this one away and use the real one instead. + .section __DATA,__datacoal_nt,coalesced + .globl _dyld_all_image_infos + .weak_definition _dyld_all_image_infos +_dyld_all_image_infos: .long 0 + + + + .globl __dyld_start + #ifdef __i386__ .data __dyld_start_static_picbase: @@ -84,7 +96,17 @@ _stub_binding_helper: .globl _dyld_func_lookup _dyld_func_lookup: jmp __Z18lookupDyldFunctionPKcPm + nop + nop + nop +_offset_to_dyld_all_image_infos: + .long _dyld_all_image_infos - . + 0x1010 + .long 0 + # space for future stable entry points + .space 16 + + .text .align 4, 0x90 .globl __dyld_start @@ -106,7 +128,7 @@ L__dyld_start_picbase: pushl %ebx # param2 = argc movl 4(%ebp),%ebx pushl %ebx # param1 = mh - call __ZN13dyldbootstrap5startEPK11mach_headeriPPKcl + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl # clean up stack and jump to result movl %ebp,%esp # restore the unaligned stack pointer @@ -123,6 +145,7 @@ L_end: #endif /* __i386__ */ + #if __x86_64__ .data .align 3 @@ -141,6 +164,15 @@ _stub_binding_helper: .globl _dyld_func_lookup _dyld_func_lookup: jmp __Z18lookupDyldFunctionPKcPm + nop + nop + nop +_offset_to_dyld_all_image_infos: + .long _dyld_all_image_infos - . + 0x1010 + .long 0 + # space for future stable entry points + .space 16 + .text .align 2,0x90 @@ -157,7 +189,7 @@ __dyld_start: movq __dyld_start_static(%rip), %r8 leaq __dyld_start(%rip), %rcx subq %r8, %rcx # param4 = slide into %rcx - call __ZN13dyldbootstrap5startEPK11mach_headeriPPKcl + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl # clean up stack and jump to result movq %rbp,%rsp # restore the unaligned stack pointer @@ -196,7 +228,12 @@ _stub_binding_helper: .globl _dyld_func_lookup _dyld_func_lookup: b __Z18lookupDyldFunctionPKcPm - + nop +_offset_to_dyld_all_image_infos: + .long _dyld_all_image_infos - . + 0x1010 + .long 0 + # space for future stable entry points + .space 16 .text @@ -219,7 +256,7 @@ 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 __ZN13dyldbootstrap5startEPK11mach_headeriPPKcl + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl ; clean up stack and jump to result mtctr r3 ; Put entry point in count register @@ -234,6 +271,81 @@ dyld_stub_binding_helper: L_end: #endif /* __ppc__ */ +#if __arm__ + .data + .align 2 +__dyld_start_static_picbase: + .long L__dyld_start_picbase + + .text + .align 2 + .globl _stub_binding_helper +_stub_binding_helper: + b _stub_binding_helper_interface + nop + + .globl _dyld_func_lookup +_dyld_func_lookup: + b _branch_to_lookupDyldFunction + nop + +_offset_to_dyld_all_image_infos: + .long _dyld_all_image_infos - . + 0x1010 + .long 0 + # space for future stable entry points + .space 16 + + + .text + .align 2 +__dyld_start: + // call dyldbootstrap::start(app_mh, argc, argv, slide) + + ldr r3, L__dyld_start_picbase_ptr +L__dyld_start_picbase: + sub r0, pc, #8 // load actual PC + 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 + + mov r8, sp // save stack pointer + bic sp, sp, #7 // force 8-byte alignment + + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl + + // 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 + + + .text + .align 2 +_branch_to_lookupDyldFunction: + // arm has no "bx label" instruction, so need this island in case lookupDyldFunction() is in thumb + ldr ip, L2 +L1: ldr pc, [pc, ip] +L2: .long _lookupDyldFunction_ptr-8-L1 + + .data + .align 2 +_lookupDyldFunction_ptr: + .long __Z18lookupDyldFunctionPKcPm + + + .text + .globl dyld_stub_binding_helper +dyld_stub_binding_helper: + trap + +L_end: +#endif /* __arm__ */ /* * dyld calls this function to terminate a process. @@ -244,10 +356,12 @@ L_end: .align 2 .globl _dyld_fatal_error _dyld_fatal_error: -#if __ppc__ || __ppc64__ +#if __ppc__ || __ppc64__ || __arm__ trap + nop #elif __x86_64__ || __i386__ int3 + nop #else #error unknown architecture #endif diff --git a/src/dyld_gdb.cpp b/src/dyld_gdb.cpp index 456f048..7d79aad 100644 --- a/src/dyld_gdb.cpp +++ b/src/dyld_gdb.cpp @@ -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@ * @@ -32,149 +32,16 @@ #include "mach-o/dyld_gdb.h" #include "mach-o/dyld_images.h" +#include "ImageLoader.h" -#define OLD_GDB_DYLD_INTERFACE __ppc__ || __i386__ - -// old gdb interface to dyld only supported on 32-bit ppc and i386 -#if OLD_GDB_DYLD_INTERFACE - -unsigned int gdb_dyld_version = 2; - - -/* - * gdb_dyld_state_changed() is a dummy routine called by dyld after images get - * added or removed/ Gdb is expected to set a break point at - * gdb_dyld_state_changed() then re-read dyld internal data as specified in - * the header file dyld_gdb.h - */ -void gdb_dyld_state_changed() -{ - // do nothing -} - -#define NLIBRARY_IMAGES 200 -#define NOBJECT_IMAGES 1 - - -struct image { - const char* physical_name; // physical image name (file name) - uint32_t vmaddr_slide; // the slide from the staticly linked address - const mach_header* mh; // address of the mach header of the image - uint32_t valid; // TRUE if this is struct is valid - const char* name; // image name for reporting errors -}; - - -struct library_images { - struct image images[NLIBRARY_IMAGES]; - uint32_t nimages; - struct library_images* next_images; -}; -struct object_images { - struct image images[NOBJECT_IMAGES]; - uint32_t nimages; - struct library_images* next_images; -}; - -unsigned int gdb_nobject_images = NOBJECT_IMAGES; -unsigned int gdb_object_image_size = sizeof(image); -unsigned int gdb_nlibrary_images = NLIBRARY_IMAGES; -unsigned int gdb_library_image_size = sizeof(image); - -extern "C" { -object_images object_images;// = { {}, 0 , NULL }; -library_images library_images;// = { {}, 0 , NULL }; -void send_event(const struct dyld_event* event); -} - - -enum dyld_event_type { - DYLD_IMAGE_ADDED = 0, - DYLD_IMAGE_REMOVED = 5 -}; - -struct dyld_event { - enum dyld_event_type type; - const struct mach_header* header; - uintptr_t slide; -}; - - -// gdb only notices changes bundles/dylibs loaded at runtime -// if the "send_event()" function in dyld is called... -void send_event(const struct dyld_event* event); -void (*send_event_ptr)(const struct dyld_event* event) = &send_event; - -void addImageForgdb(const mach_header* mh, uintptr_t slide, const char* physicalPath, const char* logicalPath) -{ - struct library_images* li = &library_images; - while ( li->nimages >= NLIBRARY_IMAGES ) { - if ( li->next_images == NULL ) { - struct library_images* li2 = new struct library_images(); - li2->nimages = 0; - li2->next_images = NULL; - li->next_images = li2; - li = li2; - } - else { - li = li->next_images; - } - } - image* info = &li->images[li->nimages++]; - info->physical_name = physicalPath; - info->vmaddr_slide = slide; - info->mh = mh; - info->valid = 1; - info->name = logicalPath; - - // ping gdb about change - dyld_event event; - event.type = DYLD_IMAGE_ADDED; - event.header = mh; - event.slide = slide; - - // we have to indirect through a function pointer to keep gcc-3.5 from inlining away the function call - // rdar://problem/3830560 - (*send_event_ptr)(&event); -} - -// move this to after use, otherwise gcc will see it has an empty implementation and -// optimize away the call site -void send_event(const struct dyld_event* event) -{ - // This function exists to let gdb set a break point - // and catch libraries being added... -} - - -void removeImageForgdb(const mach_header* mh) -{ - for (struct library_images* li = &library_images; li != NULL; li = li->next_images) { - for( uint32_t n=0; n < li->nimages; ++n) { - struct image* image = &li->images[n]; - if ( image->mh == mh ) { - image->physical_name = NULL; - image->vmaddr_slide = 0; - image->mh = 0; - image->valid = 0; - image->name = NULL; - return; - } - } - } -} - -#endif static std::vector sImageInfos; - - 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 if ( sImageInfos.size() == 0 ) - sImageInfos.reserve(200); + sImageInfos.reserve(INITIAL_IMAGE_COUNT); // set infoArray to NULL to denote it is in-use dyld_all_image_infos.infoArray = NULL; @@ -227,10 +94,22 @@ static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, co // dyld::log("dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath); } +void setAlImageInfosHalt(const char* message, uintptr_t flags) +{ + dyld_all_image_infos.errorMessage = message; + dyld_all_image_infos.terminationFlags = flags; +} -struct dyld_all_image_infos dyld_all_image_infos = { 1, 0, NULL, &gdb_image_notifier, false }; +extern void* __dso_handle; +#define STR(s) # s +#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 }; struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + diff --git a/src/dyld_stub_binder.s b/src/dyld_stub_binder.s new file mode 100644 index 0000000..6d7f182 --- /dev/null +++ b/src/dyld_stub_binder.s @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#ifdef __i386__ + +#define MH_PARAM_OUT 0 +#define LP_PARAM_OUT 4 +#define XMMM0_SAVE 16 /* 16-byte align */ +#define XMMM1_SAVE 32 +#define XMMM2_SAVE 48 +#define XMMM3_SAVE 64 +#define EAX_SAVE 84 +#define ECX_SAVE 88 +#define EDX_SAVE 92 +#define LP_LOCAL 96 +#define MH_LOCAL 100 +#define STACK_SIZE 100 /* must be 4 mod 16 so that stack winds up 16-byte aliged */ +#define LP_OLD_BP_SAVE 104 + +/* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .text + .align 4,0x90 + .globl dyld_stub_binder + .globl _misaligned_stack_error +dyld_stub_binder: + subl $STACK_SIZE,%esp # makes stack 16-byte aligned + movl %eax,EAX_SAVE(%esp) + movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter + movl %eax,LP_LOCAL(%esp) + movl %ebp,LP_OLD_BP_SAVE(%esp) # store epb back chain + movl %esp,%ebp # set epb to be this frame + add $LP_OLD_BP_SAVE,%ebp + movl %ecx,ECX_SAVE(%esp) + movl %edx,EDX_SAVE(%esp) + .align 0,0x90 +_misaligned_stack_error_: + movdqa %xmm0,XMMM0_SAVE(%esp) + movdqa %xmm1,XMMM1_SAVE(%esp) + movdqa %xmm2,XMMM2_SAVE(%esp) + movdqa %xmm3,XMMM3_SAVE(%esp) +dyld_stub_binder_: + movl MH_LOCAL(%esp),%eax # call dyld::fastBindLazySymbol(loadercache, lazyinfo) + movl %eax,MH_PARAM_OUT(%esp) + movl LP_LOCAL(%esp),%eax + movl %eax,LP_PARAM_OUT(%esp) + call __Z21_dyld_fast_stub_entryPvl + movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%esp),%xmm1 + movdqa XMMM2_SAVE(%esp),%xmm2 + movdqa XMMM3_SAVE(%esp),%xmm3 + movl ECX_SAVE(%esp),%ecx + movl EDX_SAVE(%esp),%edx + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eax + addl $STACK_SIZE+4,%esp # cut back stack + xchg %ebp, (%esp) # restore ebp and set target to top of stack + ret # jump to target + + +#endif /* __i386__ */ + + +#if __x86_64__ + +#define MH_PARAM_BP 8 +#define LP_PARAM_BP 16 + +#define RDI_SAVE 0 +#define RSI_SAVE 8 +#define RDX_SAVE 16 +#define RCX_SAVE 24 +#define R8_SAVE 32 +#define R9_SAVE 40 +#define RAX_SAVE 48 +#define XMMM0_SAVE 64 /* 16-byte align */ +#define XMMM1_SAVE 80 +#define XMMM2_SAVE 96 +#define XMMM3_SAVE 112 +#define XMMM4_SAVE 128 +#define XMMM5_SAVE 144 +#define XMMM6_SAVE 160 +#define XMMM7_SAVE 176 +#define STACK_SIZE 192 /* (XMMM7_SAVE+16) must be 16 byte aligned too */ + + + /* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .align 2,0x90 + .globl dyld_stub_binder +dyld_stub_binder: + pushq %rbp + movq %rsp,%rbp + subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed + movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE(%rsp) + movq %rdx,RDX_SAVE(%rsp) + movq %rcx,RCX_SAVE(%rsp) + movq %r8,R8_SAVE(%rsp) + movq %r9,R9_SAVE(%rsp) + movq %rax,RAX_SAVE(%rsp) + movdqa %xmm0,XMMM0_SAVE(%rsp) + movdqa %xmm1,XMMM1_SAVE(%rsp) + movdqa %xmm2,XMMM2_SAVE(%rsp) + movdqa %xmm3,XMMM3_SAVE(%rsp) + movdqa %xmm4,XMMM4_SAVE(%rsp) + movdqa %xmm5,XMMM5_SAVE(%rsp) + movdqa %xmm6,XMMM6_SAVE(%rsp) + movdqa %xmm7,XMMM7_SAVE(%rsp) + movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) + movq LP_PARAM_BP(%rbp),%rsi + call __Z21_dyld_fast_stub_entryPvl + movq %rax,%r11 # save target + movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%rsp),%xmm1 + movdqa XMMM2_SAVE(%rsp),%xmm2 + movdqa XMMM3_SAVE(%rsp),%xmm3 + movdqa XMMM4_SAVE(%rsp),%xmm4 + movdqa XMMM5_SAVE(%rsp),%xmm5 + movdqa XMMM6_SAVE(%rsp),%xmm6 + movdqa XMMM7_SAVE(%rsp),%xmm7 + movq RDI_SAVE(%rsp),%rdi + movq RSI_SAVE(%rsp),%rsi + movq RDX_SAVE(%rsp),%rdx + movq RCX_SAVE(%rsp),%rcx + movq R8_SAVE(%rsp),%r8 + movq R9_SAVE(%rsp),%r9 + movq RAX_SAVE(%rsp),%rax + addq $STACK_SIZE,%rsp + popq %rbp + addq $16,%rsp # remove meta-parameters + jmp *%r11 # jmp to target + +#endif + + + diff --git a/src/glue.c b/src/glue.c index cce0963..399ad24 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,36 +27,53 @@ #include #include #include +#include +#include + +// from _simple.h in libc +typedef struct _SIMPLE* _SIMPLE_STRING; +extern void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap); +extern void _simple_dprintf(int __fd, const char *__fmt, ...); +extern _SIMPLE_STRING _simple_salloc(void); +extern int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap); +extern void _simple_sfree(_SIMPLE_STRING __b); +extern char * _simple_string(_SIMPLE_STRING __b); + +// dyld::log(const char* format, ...) +extern void _ZN4dyld3logEPKcz(const char*, ...); + +// dyld::halt(const char* msg); +extern void _ZN4dyld4haltEPKc(const char* msg) __attribute__((noreturn)); + - // abort called by C++ unwinding code void abort() -{ - _exit(1); +{ + _ZN4dyld4haltEPKc("dyld calling abort()\n"); } // std::terminate called by C++ unwinding code void _ZSt9terminatev() { - _exit(1); + _ZN4dyld4haltEPKc("dyld std::terminate()\n"); } // std::unexpected called by C++ unwinding code void _ZSt10unexpectedv() { - _exit(1); + _ZN4dyld4haltEPKc("dyld std::unexpected()\n"); } // __cxxabiv1::__terminate(void (*)()) called to terminate process void _ZN10__cxxabiv111__terminateEPFvvE() { - _exit(1); + _ZN4dyld4haltEPKc("dyld std::__terminate()\n"); } // __cxxabiv1::__unexpected(void (*)()) called to terminate process void _ZN10__cxxabiv112__unexpectedEPFvvE() { - _exit(1); + _ZN4dyld4haltEPKc("dyld std::__unexpected()\n"); } // __cxxabiv1::__terminate_handler @@ -65,7 +82,52 @@ void* _ZN10__cxxabiv119__terminate_handlerE = &_ZSt9terminatev; // __cxxabiv1::__unexpected_handler void* _ZN10__cxxabiv120__unexpected_handlerE = &_ZSt10unexpectedv; +// libc uses assert() +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) +{ + if (func == NULL) + _ZN4dyld3logEPKcz("Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line); + else + _ZN4dyld3logEPKcz("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + abort(); +} + + +// called by libuwind code before aborting +size_t fwrite(const void* ptr, size_t size, size_t nitme, FILE* stream) +{ + return fprintf(stream, "%s", (char*)ptr); +} + +// called by libuwind code before aborting +int fprintf(FILE* file, const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); + return 0; +} +// called by LIBC_ABORT +void abort_report_np(const char* format, ...) +{ + va_list list; + const char *str; + _SIMPLE_STRING s = _simple_salloc(); + if ( s != NULL ) { + va_start(list, format); + _simple_vsprintf(s, format, list); + va_end(list); + str = _simple_string(s); + } + else { + // _simple_salloc failed, but at least format may have useful info by itself + str = format; + } + _ZN4dyld4haltEPKc(str); + // _ZN4dyld4haltEPKc doesn't return, so we can't call _simple_sfree +} // real cthread_set_errno_self() has error handling that pulls in @@ -87,3 +149,23 @@ struct tm* localtime(const time_t* t) } +// +// 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) +{ +#if __LP64__ + __stack_chk_guard = ((long)arc4random() << 32) | arc4random(); +#else + __stack_chk_guard = arc4random(); +#endif +} +extern void _ZN4dyld4haltEPKc(const char*); +void __stack_chk_fail() +{ + _ZN4dyld4haltEPKc("stack buffer overrun"); +} + + diff --git a/src/strip.exp b/src/strip.exp deleted file mode 100644 index 8a2f778..0000000 --- a/src/strip.exp +++ /dev/null @@ -1,4 +0,0 @@ - -# local symbols to suppress -*PE* -*Win* diff --git a/src/stub_binding_helper.s b/src/stub_binding_helper.s index 331a5cd..aec491f 100644 --- a/src/stub_binding_helper.s +++ b/src/stub_binding_helper.s @@ -245,6 +245,40 @@ _stub_binding_helper_interface: #endif /* __ppc__ */ +#if __arm__ +/* + * This is the interface for the stub_binding_helper for ARM: + * The caller has pushed the address of the a lazy pointer to be filled in with + * the value for the defined symbol and pushed the address of the the mach + * header this pointer comes from. + * + * sp+4 address of lazy pointer + * sp+0 address of mach header + * + * After the symbol has been resolved and the pointer filled in this is to pop + * these arguments off the stack and jump to the address of the defined symbol. + */ + + .text + .align 2 + .globl _stub_binding_helper_interface +_stub_binding_helper_interface: + 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 of mach header to 1st parameter + ldr r1, [sp, #28] // move address of lazy pointer to 2nd parameter + + // call dyld::bindLazySymbol(mh, lazy_symbol_pointer_address) + bl __ZN4dyld14bindLazySymbolEPK11mach_headerPm + 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/unit-tests/bin/make-recursive.pl b/unit-tests/bin/make-recursive.pl index c980711..a441350 100755 --- a/unit-tests/bin/make-recursive.pl +++ b/unit-tests/bin/make-recursive.pl @@ -91,6 +91,7 @@ sub find_callback open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + $ENV{UNIT_TEST_NAME} = $reldir; my $exit = system(@$cmd); close(STDOUT) || die("$!"); diff --git a/unit-tests/bin/pass-iff-exit-zero.pl b/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 0000000..07854b5 --- /dev/null +++ b/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 != system(@ARGV)) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 243fc2d..f94abba 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -6,15 +6,17 @@ SHELL = /bin/sh ARCH ?= $(shell arch) # set default to be all -VALID_ARCHS ?= "ppc ppc64 i386 x86_64" +VALID_ARCHS ?= "ppc i386 x86_64" -CC = gcc-4.0 -arch ${ARCH} +CC = gcc-4.2 -arch ${ARCH} CCFLAGS = -Wall -std=c99 -CXX = g++-4.0 -arch ${ARCH} +CXX = g++-4.2 -arch ${ARCH} CXXFLAGS = -Wall RM = rm RMFLAGS = -rf SAFE_RUN = ${TESTROOT}/bin/fail-if-non-zero.pl +PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl + diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index 0cfeb45..45f846f 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -32,15 +32,18 @@ fi # if Intel, then also run all test cases under emulation if [ "`sysctl -n hw.machine`" = "i386" ] 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 + 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 # if 64-bit capable Intel, then also run all test cases for 64-bits if [ `sysctl -n hw.optional.x86_64` = "1" ] diff --git a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile index 49c689a..2a47c2b 100644 --- a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile +++ b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile @@ -23,8 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all - ./main +all-check: all check all: main @@ -37,5 +36,8 @@ libfoo.dylib : foo.c libbar.dylib libbar.dylib : bar.c ${CC} -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib -install_name /usr/local/hide/libbar.dylib +check: + ./main + clean: ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib diff --git a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile index 7d4b9f1..72b6a43 100644 --- a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile +++ b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/NSAddImage-leafname/Makefile b/unit-tests/test-cases/NSAddImage-leafname/Makefile index f46b18e..c31f202 100644 --- a/unit-tests/test-cases/NSAddImage-leafname/Makefile +++ b/unit-tests/test-cases/NSAddImage-leafname/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_FALLBACK_LIBRARY_PATH="hide" && ./main all: main hide/libzzz.dylib diff --git a/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile b/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile new file mode 100644 index 0000000..1590fdc --- /dev/null +++ b/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile @@ -0,0 +1,19 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wno-deprecated-declarations + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c b/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c new file mode 100644 index 0000000..9ac5211 --- /dev/null +++ b/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008 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 + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + NSAddressOfSymbol(NULL); + + PASS("NSAddressOfSymbol-NULL"); + return 0; +} diff --git a/unit-tests/test-cases/addend/Makefile b/unit-tests/test-cases/addend/Makefile new file mode 100644 index 0000000..db2bdbf --- /dev/null +++ b/unit-tests/test-cases/addend/Makefile @@ -0,0 +1,23 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/addend/foo.c b/unit-tests/test-cases/addend/foo.c new file mode 100644 index 0000000..efdd301 --- /dev/null +++ b/unit-tests/test-cases/addend/foo.c @@ -0,0 +1,7 @@ + + +const char a = 10; +const char b = 11; +const char c = 12; +const char d = 13; +const char e = 14; diff --git a/unit-tests/test-cases/addend/main.c b/unit-tests/test-cases/addend/main.c new file mode 100644 index 0000000..fd8099b --- /dev/null +++ b/unit-tests/test-cases/addend/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern const char a; +extern const char b; +extern const char c; +extern const char d; +extern const char e; + + +const char* pc = &c; +const char* pd_2 = &d - 2; +const char* pb2 = &b + 2; + + +int main() +{ + if (*pc != 12 ) { + FAIL("addend: *pc != 12"); + return 0; + } + + if (*pd_2 != 11 ) { + FAIL("addend: *pd_2 != 11"); + return 0; + } + + if (*pb2 != 13 ) { + FAIL("addend: *pb2 != 13"); + return 0; + } + + PASS("addend"); +} diff --git a/unit-tests/test-cases/all_image_infos/Makefile b/unit-tests/test-cases/all_image_infos/Makefile new file mode 100644 index 0000000..b625c20 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/unit-tests/test-cases/all_image_infos/foo.c b/unit-tests/test-cases/all_image_infos/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/unit-tests/test-cases/all_image_infos/main.c b/unit-tests/test-cases/all_image_infos/main.c new file mode 100644 index 0000000..30a8427 --- /dev/null +++ b/unit-tests/test-cases/all_image_infos/main.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // EXIT_SUCCESS +#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() +{ + 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* infos = getImageInfos(); + if ( infos->version != 7 ) { + FAIL("all_image_infos: dyld_all_image_infos is not version 7"); + exit(0); + } + + if ( infos->infoArrayCount < 2 ) { + FAIL("all_image_infos: dyld_all_image_infos.infoArrayCount is < 2"); + exit(0); + } + + if ( infos->infoArray[0].imageLoadAddress != &__dso_handle ) { + FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong"); + exit(0); + } + + if ( getImageInfosFromKernel() != infos ) { + FAIL("all_image_infos: task_info and dyld disagree"); + exit(0); + } + + 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 new file mode 100644 index 0000000..80ee137 --- /dev/null +++ b/unit-tests/test-cases/always-libSystem/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + export DYLD_SHARED_REGION=avoid && ./main + +all: main + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.5 + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/always-libSystem/main.c b/unit-tests/test-cases/always-libSystem/main.c new file mode 100644 index 0000000..5626783 --- /dev/null +++ b/unit-tests/test-cases/always-libSystem/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// app crashes when libSystem cannot be found +// + +int main() +{ + // see if libSystem is in list of images + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, "/libSystem.") != NULL ) { + PASS("always-libSystem"); + return 0; + } + } + + FAIL("always-libSystem"); + return 0; +} + diff --git a/unit-tests/test-cases/big-jump-table/Makefile b/unit-tests/test-cases/big-jump-table/Makefile index 0fe015b..d968239 100644 --- a/unit-tests/test-cases/big-jump-table/Makefile +++ b/unit-tests/test-cases/big-jump-table/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main @@ -32,7 +34,7 @@ all: main ### The point of this test is to check an edge case for i386 architecture ### with "fast stubs". We want to verify that a fast stub that ends ### near the page boundary for the __IMPORT segment does not cause an -### accidental read beyou the __IMPORT segment +### accidental read beyond the __IMPORT segment ### rdar://problem/4653725 ### @@ -43,13 +45,13 @@ libtest1.dylib: pointers.c funcs.c libfoo.dylib ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=1 -o libtest1.dylib libfoo.dylib libtest2.dylib: pointers.c funcs.c libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=2 -o libtest2.dylib libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=2 -o libtest2.dylib libfoo.dylib libtest3.dylib: pointers.c funcs.c libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=3 -o libtest3.dylib libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=3 -o libtest3.dylib libfoo.dylib libtest4.dylib: pointers.c funcs.c libfoo.dylib - ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=4 -o libtest4.dylib libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=4 -o libtest4.dylib libfoo.dylib libfoo.dylib: foo.c ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib diff --git a/unit-tests/test-cases/big-jump-table/funcs.c b/unit-tests/test-cases/big-jump-table/funcs.c index 16c40a2..64cd71a 100644 --- a/unit-tests/test-cases/big-jump-table/funcs.c +++ b/unit-tests/test-cases/big-jump-table/funcs.c @@ -20,11 +20,13 @@ * * @APPLE_LICENSE_HEADER_END@ */ - +#include +#include #include "foo.h" static void __attribute__((constructor)) myinit() { +// uint64_t t1 = mach_absolute_time(); foo002(); foo003(); foo004(); @@ -849,5 +851,9 @@ static void __attribute__((constructor)) myinit() #if CASE <= 1 foo817(); #endif + +// uint64_t t2 = mach_absolute_time(); +// fprintf(stderr, "total time = %lld\n", t2-t1); + } diff --git a/unit-tests/test-cases/big-stack/Makefile b/unit-tests/test-cases/big-stack/Makefile index cc85b85..b6f9489 100644 --- a/unit-tests/test-cases/big-stack/Makefile +++ b/unit-tests/test-cases/big-stack/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,11 +23,28 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +# rosetta does not support very large stack sizes +STACK_SIZE = 0x83000000 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + STACK_SIZE = 0x02100000 + endif +endif + + +ifeq "armv6" "$(ARCH)" + STACK_SIZE = 0x20000000 +endif + + +all-check: all check + +check: ${TESTROOT}/bin/exit-zero-pass.pl "big stack" "big stack failed" ./main all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-w -Wl,-stack_size -Wl,0x83000000 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-w -Wl,-stack_size -Wl,${STACK_SIZE} -DSTACK_SIZE=${STACK_SIZE} clean: ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/big-stack/main.c b/unit-tests/test-cases/big-stack/main.c index dc1339a..003881f 100644 --- a/unit-tests/test-cases/big-stack/main.c +++ b/unit-tests/test-cases/big-stack/main.c @@ -31,6 +31,7 @@ // This builds an executable that needs > 2GB of stack // + char* keepAlive; // to keep compiler from optimizing away stack variable // keep recursing until desired stack size achieved @@ -38,7 +39,10 @@ void foo(unsigned long long stackSize, char* stackStart) { char buffer[32*1024*1024]; keepAlive = buffer; - if ( (stackStart - buffer) > stackSize ) + // only recursive if there is enough room for next buffer + intptr_t freeStackSpace = (buffer - sizeof(buffer)) - (stackStart - stackSize); + //fprintf(stderr, "&buffer=%p, stackStart=%p, freeStackSpace=0x%lx\n", buffer, stackStart, freeStackSpace); + if ( freeStackSpace < sizeof(buffer) ) return; else foo(stackSize, stackStart); @@ -69,7 +73,7 @@ main() foo(0x02000000, &start); else #endif - foo(0x81000000, &start); // 2.1 GB stack + foo(STACK_SIZE, &start); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/bundle-basic/Makefile b/unit-tests/test-cases/bundle-basic/Makefile index d2c407c..c05f64e 100644 --- a/unit-tests/test-cases/bundle-basic/Makefile +++ b/unit-tests/test-cases/bundle-basic/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-dont-gc/Makefile b/unit-tests/test-cases/bundle-dont-gc/Makefile index 6728b7a..5f4f05f 100644 --- a/unit-tests/test-cases/bundle-dont-gc/Makefile +++ b/unit-tests/test-cases/bundle-dont-gc/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/bundle-memory-load-bad/Makefile b/unit-tests/test-cases/bundle-memory-load-bad/Makefile index 4b3865d..a6b0db5 100644 --- a/unit-tests/test-cases/bundle-memory-load-bad/Makefile +++ b/unit-tests/test-cases/bundle-memory-load-bad/Makefile @@ -31,7 +31,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-memory-load-fat/Makefile b/unit-tests/test-cases/bundle-memory-load-fat/Makefile index 7c57314..687eea6 100644 --- a/unit-tests/test-cases/bundle-memory-load-fat/Makefile +++ b/unit-tests/test-cases/bundle-memory-load-fat/Makefile @@ -25,7 +25,9 @@ include ${TESTROOT}/include/common.makefile FATFLAGS = `lipo -detailed_info /usr/lib/libSystem.B.dylib | grep architecture | sed -e 's/architecture/-arch/'` -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-memory-load-malloc/Makefile b/unit-tests/test-cases/bundle-memory-load-malloc/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-malloc/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c b/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c @@ -0,0 +1,32 @@ +/* + * 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 + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 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 new file mode 100644 index 0000000..d00fbde --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-malloc/main.c @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ + 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 1; + } + + void* loadAddress = malloc((stat_buf.st_size+4095) & (-4096)); + if ( loadAddress == NULL ) { + FAIL("malloc failed"); + return 1; + } + + if ( pread(fd, loadAddress, stat_buf.st_size, 0) != stat_buf.st_size ) { + FAIL("pread() failed"); + return 1; + } + + //void* loadAddress2 = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + //fprintf(stderr, "loadAddress=%p\n", loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + //fprintf(stderr, "loadAddress2=%p\n", loadAddress2); + //fprintf(stderr, "malloc_size(loadAddress2) => 0x%08X\n", malloc_size(loadAddress2)); + + + //free(loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + if ( malloc_size(loadAddress) != 0 ) { + FAIL("malloc_size(loadAddress) => 0x%08X", malloc_size(loadAddress)); + FAIL("malloc still thinks it owns this block"); + return 1; + } + + + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) + + PASS("bundle-memory-load-malloc"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-memory-load/Makefile b/unit-tests/test-cases/bundle-memory-load/Makefile index a44aeca..0a4e679 100644 --- a/unit-tests/test-cases/bundle-memory-load/Makefile +++ b/unit-tests/test-cases/bundle-memory-load/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-multi-link/Makefile b/unit-tests/test-cases/bundle-multi-link/Makefile index c044f7f..02b7dca 100644 --- a/unit-tests/test-cases/bundle-multi-link/Makefile +++ b/unit-tests/test-cases/bundle-multi-link/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-multi-load/Makefile b/unit-tests/test-cases/bundle-multi-load/Makefile index c044f7f..02b7dca 100644 --- a/unit-tests/test-cases/bundle-multi-load/Makefile +++ b/unit-tests/test-cases/bundle-multi-load/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-name-ownership/Makefile b/unit-tests/test-cases/bundle-name-ownership/Makefile index f2d2d0a..d5b047f 100644 --- a/unit-tests/test-cases/bundle-name-ownership/Makefile +++ b/unit-tests/test-cases/bundle-name-ownership/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-private/Makefile b/unit-tests/test-cases/bundle-private/Makefile index 29d5d41..72ca403 100644 --- a/unit-tests/test-cases/bundle-private/Makefile +++ b/unit-tests/test-cases/bundle-private/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-reload/Makefile b/unit-tests/test-cases/bundle-reload/Makefile index 97eb0d5..723c2c6 100644 --- a/unit-tests/test-cases/bundle-reload/Makefile +++ b/unit-tests/test-cases/bundle-reload/Makefile @@ -29,7 +29,9 @@ else CXX_VERSION = ${CXX} endif -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-unlinkable/Makefile b/unit-tests/test-cases/bundle-unlinkable/Makefile index bb9c30d..b872e6c 100644 --- a/unit-tests/test-cases/bundle-unlinkable/Makefile +++ b/unit-tests/test-cases/bundle-unlinkable/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile index f2d2d0a..d5b047f 100644 --- a/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile +++ b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-v-dylib/Makefile b/unit-tests/test-cases/bundle-v-dylib/Makefile index 952c3a5..e706883 100644 --- a/unit-tests/test-cases/bundle-v-dylib/Makefile +++ b/unit-tests/test-cases/bundle-v-dylib/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/bundle-weak/Makefile b/unit-tests/test-cases/bundle-weak/Makefile index 3cf265b..7cdc170 100644 --- a/unit-tests/test-cases/bundle-weak/Makefile +++ b/unit-tests/test-cases/bundle-weak/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/bundle-weak/main.c b/unit-tests/test-cases/bundle-weak/main.c index 2de9a58..814c406 100644 --- a/unit-tests/test-cases/bundle-weak/main.c +++ b/unit-tests/test-cases/bundle-weak/main.c @@ -33,7 +33,7 @@ int main() { void* handle = dlopen("test.bundle", RTLD_LAZY); if ( handle == NULL ) { - FAIL("dlopen(\test.bundle\") failed"); + FAIL("dlopen(\"test.bundle\") failed: %s", dlerror()); return 0; } diff --git a/unit-tests/test-cases/crt-apple/Makefile b/unit-tests/test-cases/crt-apple/Makefile index 0905421..893e25b 100644 --- a/unit-tests/test-cases/crt-apple/Makefile +++ b/unit-tests/test-cases/crt-apple/Makefile @@ -27,7 +27,9 @@ include ${TESTROOT}/include/common.makefile # verify that apple[0] parameter is correct by comparing to argv[1] # -run: all +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 diff --git a/unit-tests/test-cases/crt-argv-NULL/Makefile b/unit-tests/test-cases/crt-argv-NULL/Makefile index 9df7f9b..9d8dd59 100644 --- a/unit-tests/test-cases/crt-argv-NULL/Makefile +++ b/unit-tests/test-cases/crt-argv-NULL/Makefile @@ -27,7 +27,9 @@ include ${TESTROOT}/include/common.makefile # verifies that crt glue can handle argv[0] = NULL # -run: all +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 diff --git a/unit-tests/test-cases/crt-custom/Makefile b/unit-tests/test-cases/crt-custom/Makefile index d07ac32..491d480 100644 --- a/unit-tests/test-cases/crt-custom/Makefile +++ b/unit-tests/test-cases/crt-custom/Makefile @@ -28,7 +28,9 @@ include ${TESTROOT}/include/common.makefile # have the entry point called before initializers are run # -run: all +all-check: all check + +check: ./main-10.4 ./main-10.5 diff --git a/unit-tests/test-cases/crt-libSystem/Makefile b/unit-tests/test-cases/crt-libSystem/Makefile index 8d8b832..6f30df5 100644 --- a/unit-tests/test-cases/crt-libSystem/Makefile +++ b/unit-tests/test-cases/crt-libSystem/Makefile @@ -28,13 +28,17 @@ include ${TESTROOT}/include/common.makefile # the mechanism for 10.4 and 10.5 is different # -run: all +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.4.stripped main-10.5.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 @@ -48,7 +52,13 @@ main-10.5: main.c 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-10.6.stripped: main-10.6 + strip main-10.6 -o main-10.6.stripped + clean: - ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.6 main-10.4.stripped main-10.5.stripped main-10.6.stripped diff --git a/unit-tests/test-cases/crt-result/Makefile b/unit-tests/test-cases/crt-result/Makefile index 8e7cbf3..0e4bd7b 100644 --- a/unit-tests/test-cases/crt-result/Makefile +++ b/unit-tests/test-cases/crt-result/Makefile @@ -28,7 +28,9 @@ include ${TESTROOT}/include/common.makefile # for both crt1.0 and crt1.10.5.o # -run: all +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 diff --git a/unit-tests/test-cases/cxa_finalize/Makefile b/unit-tests/test-cases/cxa_finalize/Makefile new file mode 100644 index 0000000..44b4c0c --- /dev/null +++ b/unit-tests/test-cases/cxa_finalize/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/cxa_finalize/foo.cxx b/unit-tests/test-cases/cxa_finalize/foo.cxx new file mode 100644 index 0000000..0399be1 --- /dev/null +++ b/unit-tests/test-cases/cxa_finalize/foo.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +class A +{ +public: + A() { f = 10; } + ~A() { f = 0; } + int get() { return f; } +private: + int f; +}; + +A a; + + +int test() +{ + return a.get(); +} diff --git a/unit-tests/test-cases/cxa_finalize/main.c b/unit-tests/test-cases/cxa_finalize/main.c new file mode 100644 index 0000000..d1b62d5 --- /dev/null +++ b/unit-tests/test-cases/cxa_finalize/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + return EXIT_SUCCESS; + } + + dlclose(handle); + + PASS("cxa_finalize"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/deadlock/Makefile b/unit-tests/test-cases/deadlock/Makefile index c21045d..c01b46d 100644 --- a/unit-tests/test-cases/deadlock/Makefile +++ b/unit-tests/test-cases/deadlock/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dladdr-stripped/Makefile b/unit-tests/test-cases/dladdr-stripped/Makefile new file mode 100644 index 0000000..a3ac2a4 --- /dev/null +++ b/unit-tests/test-cases/dladdr-stripped/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + strip main + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dladdr-stripped/main.c b/unit-tests/test-cases/dladdr-stripped/main.c new file mode 100644 index 0000000..4f8901a --- /dev/null +++ b/unit-tests/test-cases/dladdr-stripped/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// verify dladdr() returns NULL for a symbol name in a fully stripped +/// main executable (and not _mh_execute_header+nnn). +/// + +int main() +{ + Dl_info info; + if ( dladdr(&main, &info) == 0 ) { + FAIL("dladdr(&main, xx) failed"); + exit(0); + } + + if ( info.dli_sname != NULL ){ + FAIL("dladdr() returned: \"%s\" instead of NULL", info.dli_sname); + exit(0); + } + + PASS("dladdr-stripped"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dladdr/Makefile b/unit-tests/test-cases/dladdr/Makefile index fcb934d..3a61220 100644 --- a/unit-tests/test-cases/dladdr/Makefile +++ b/unit-tests/test-cases/dladdr/Makefile @@ -23,12 +23,15 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: - ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.5 + clean: - ${RM} ${RMFLAGS} *~ main + ${RM} ${RMFLAGS} *~ main main.dSYM diff --git a/unit-tests/test-cases/dladdr/main.c b/unit-tests/test-cases/dladdr/main.c index a57e708..ab88475 100644 --- a/unit-tests/test-cases/dladdr/main.c +++ b/unit-tests/test-cases/dladdr/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,7 +40,7 @@ static int foo() return 3; } -int bar2() +__attribute__((visibility("hidden"))) int hide() { return 4; } @@ -72,7 +72,7 @@ static void verifybar() } } -// checks local symbol (should resolve to previoius global symbol bar) +// checks local symbol static void verifyfoo() { Dl_info info; @@ -80,21 +80,38 @@ static void verifyfoo() FAIL("dladdr(&foo, xx) failed"); exit(0); } - if ( strcmp(info.dli_sname, "bar") != 0 ) { - if ( strcmp(info.dli_sname, "_bar") == 0 ) { - XFAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); - } - else { - FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); - exit(0); - } + if ( strcmp(info.dli_sname, "foo") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname); + exit(0); } - if ( info.dli_saddr != &bar) { - FAIL("dladdr()->dli_saddr is not &bar"); + if ( info.dli_saddr != &foo) { + FAIL("dladdr()->dli_saddr is not &foo"); exit(0); } - if ( info.dli_fbase != _dyld_get_image_header_containing_address(&bar) ) { - FAIL("dladdr()->dli_fbase is not image that contains &bar"); + if ( info.dli_fbase != _dyld_get_image_header_containing_address(&foo) ) { + FAIL("dladdr()->dli_fbase is not image that contains &foo"); + exit(0); + } +} + +// checks hidden symbol +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&hide, &info) == 0 ) { + FAIL("dladdr(&hide, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "hide") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &hide) { + FAIL("dladdr()->dli_saddr is not &hide"); + exit(0); + } + if ( info.dli_fbase != _dyld_get_image_header_containing_address(&hide) ) { + FAIL("dladdr()->dli_fbase is not image that contains &hide"); exit(0); } } @@ -103,6 +120,7 @@ static void verifyfoo() int main() { verifybar(); + verifyhide(); verifyfoo(); diff --git a/unit-tests/test-cases/dlclose-basic/Makefile b/unit-tests/test-cases/dlclose-basic/Makefile index 73451f0..8aeb014 100644 --- a/unit-tests/test-cases/dlclose-basic/Makefile +++ b/unit-tests/test-cases/dlclose-basic/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlclose-bundle-unload/Makefile b/unit-tests/test-cases/dlclose-bundle-unload/Makefile index 84fc5b4..c3d1b06 100644 --- a/unit-tests/test-cases/dlclose-bundle-unload/Makefile +++ b/unit-tests/test-cases/dlclose-bundle-unload/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle diff --git a/unit-tests/test-cases/dlclose-dylib-unload/Makefile b/unit-tests/test-cases/dlclose-dylib-unload/Makefile index 2597b53..bba1b65 100644 --- a/unit-tests/test-cases/dlclose-dylib-unload/Makefile +++ b/unit-tests/test-cases/dlclose-dylib-unload/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main libfoo.dylib diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile index 4f19e3f..0c9b579 100644 --- a/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile @@ -27,7 +27,9 @@ include ${TESTROOT}/include/common.makefile # Leopard (9a499): dyld crash with recursive calls to dlclose() -run: all +all-check: all check + +check: ./main all: main libfoo.dylib diff --git a/unit-tests/test-cases/dlclose-unload-c++/Makefile b/unit-tests/test-cases/dlclose-unload-c++/Makefile index 8cce77e..35530b6 100644 --- a/unit-tests/test-cases/dlclose-unload-c++/Makefile +++ b/unit-tests/test-cases/dlclose-unload-c++/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main libfoo.dylib libbar.dylib diff --git a/unit-tests/test-cases/dlclose-unmap/Makefile b/unit-tests/test-cases/dlclose-unmap/Makefile new file mode 100644 index 0000000..5f24149 --- /dev/null +++ b/unit-tests/test-cases/dlclose-unmap/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlclose-unmap/foo.c b/unit-tests/test-cases/dlclose-unmap/foo.c new file mode 100644 index 0000000..8826cba --- /dev/null +++ b/unit-tests/test-cases/dlclose-unmap/foo.c @@ -0,0 +1,2 @@ + +void foo() {} \ No newline at end of file diff --git a/unit-tests/test-cases/dlclose-unmap/main.c b/unit-tests/test-cases/dlclose-unmap/main.c new file mode 100644 index 0000000..a2c3cbf --- /dev/null +++ b/unit-tests/test-cases/dlclose-unmap/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that dlclose() actually unmmaps the image +/// + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\" RTLD_LAZY) failed but it should have worked: %s", path, msg); + exit(0); + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + // now try to create a page where foo() was + vm_address_t addr = ((uintptr_t)sym) & (-4096); + kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_FIXED); + if ( r != KERN_SUCCESS ) { + FAIL("dlclose-unmap: could not allocate page where SO was previously mapped", result); + exit(0); + } +} + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlclose-unmap"); + return 0; +} diff --git a/unit-tests/test-cases/dlerror-clear/Makefile b/unit-tests/test-cases/dlerror-clear/Makefile index 7337131..4871023 100644 --- a/unit-tests/test-cases/dlerror-clear/Makefile +++ b/unit-tests/test-cases/dlerror-clear/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlerror/Makefile b/unit-tests/test-cases/dlerror/Makefile index 7337131..4871023 100644 --- a/unit-tests/test-cases/dlerror/Makefile +++ b/unit-tests/test-cases/dlerror/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..30d9778 --- /dev/null +++ b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,31 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# Test that DYLD_FALLBACK_LIBRARY_PATH does not apply to dlopen() of a full path +# DYLD_FALLBACK_LIBRARY_PATH man page misleading +# + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH="${PWD}/hide" && ./main + +all: main hide/libfoo.dylib + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/hide/libfoo.dylib" + + + +clean: + ${RM} -rf *~ main hide + diff --git a/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..33c0685 --- /dev/null +++ b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 0; +} diff --git a/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..d2ca85e --- /dev/null +++ b/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008 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 +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("/junk/path/libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + FAIL("dlopen-DYLD_FALLBACK_LIBRARY_PATH unexpected found dylib"); + else + PASS("dlopen-DYLD_FALLBACK_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile index b20f99c..9ea3667 100644 --- a/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile +++ b/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile @@ -23,9 +23,13 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all - ./main "`pwd`/libfoo.dylib" - export DYLD_LIBRARY_PATH="`pwd`/alt" && ./main "`pwd`/libfoo.dylib" +PWD = $(shell pwd) + +all-check: all check + +check: + ./main "${PWD}/libfoo.dylib" + export DYLD_LIBRARY_PATH="${PWD}/alt" && ./main "${PWD}/libfoo.dylib" all: main alt/libfoo.dylib @@ -35,10 +39,10 @@ main : main.c libfoo.dylib alt/libfoo.dylib : foo.c mkdir -p alt - ${CC} ${CCFLAGS} -dynamiclib foo.c -o "`pwd`/alt/libfoo.dylib" -DALT + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt/libfoo.dylib" -DALT libfoo.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib foo.c -o "`pwd`/libfoo.dylib" + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" diff --git a/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile index 6a3e391..dddef71 100644 --- a/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile +++ b/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # tests combinations of dlopen() and LD_LIBRARY_PATH @@ -37,13 +38,15 @@ include ${TESTROOT}/include/common.makefile # 6) fullpath and LD_LIBRARY_PATH set to alt # -run: all +all-check: all check + +check: cd alt1 && ../main "libfoo.dylib" 1 "leafname found in cwd" ./main "./alt1/libfoo.dylib" 1 "relative path" - ./main "`pwd`/alt2/libfoo.dylib" 2 "fullpath" - export LD_LIBRARY_PATH="`pwd`/alt1" && ./main "libfoo.dylib" 1 "leafname and LD_LIBRARY_PATH overrides cwd" - export LD_LIBRARY_PATH="`pwd`/alt1" && cd alt3 && ../main "libfoo.dylib" 1 "leafname and alt LD_LIBRARY_PATH" - export LD_LIBRARY_PATH="`pwd`/alt1" && ./main "`pwd`/alt2/libfoo.dylib" 2 "fullpath and LD_LIBRARY_PATH" + ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "libfoo.dylib" 1 "leafname and LD_LIBRARY_PATH overrides cwd" + export LD_LIBRARY_PATH="${PWD}/alt1" && cd alt3 && ../main "libfoo.dylib" 1 "leafname and alt LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath and LD_LIBRARY_PATH" all: main alt1/libfoo.dylib alt2/libfoo.dylib alt3 @@ -53,14 +56,14 @@ main : main.c libfoo.dylib alt1/libfoo.dylib : foo.c mkdir -p alt1 - ${CC} ${CCFLAGS} -dynamiclib foo.c -o "`pwd`/alt1/libfoo.dylib" -DVALUE=1 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt1/libfoo.dylib" -DVALUE=1 alt2/libfoo.dylib : foo.c mkdir -p alt2 - ${CC} ${CCFLAGS} -dynamiclib foo.c -o "`pwd`/alt2/libfoo.dylib" -DVALUE=2 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt2/libfoo.dylib" -DVALUE=2 libfoo.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib foo.c -o "`pwd`/libfoo.dylib" -DVALUE=0 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" -DVALUE=0 alt3 : mkdir -p alt3 diff --git a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile index aee8633..6d7266a 100644 --- a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile +++ b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile @@ -25,7 +25,9 @@ include ${TESTROOT}/include/common.makefile PWD = `pwd` -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile index 170c483..c1e199b 100644 --- a/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile @@ -25,7 +25,9 @@ include ${TESTROOT}/include/common.makefile PWD = `pwd` -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile b/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile index 260c1e0..e6a5e5a 100644 --- a/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main foo.bundle bar.bundle ./main foo.dylib bar.bundle diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile index d7b6978..823d5de 100644 --- a/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile new file mode 100644 index 0000000..48bf4c4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile @@ -0,0 +1,31 @@ + + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifes that RTLD_LOCAL suppresses weak symbol coalescing +# + + + +all-check: all check + +check: + ./main + +all: main libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c new file mode 100644 index 0000000..b4ae377 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c @@ -0,0 +1,5 @@ +int __attribute__((weak)) A[] = { 1, 2, 3, 4 }; + + +int* getA() { return A; } + diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c new file mode 100644 index 0000000..c8ea49b --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c @@ -0,0 +1,5 @@ + + +int __attribute__((weak)) A[] = { 5, 6, 7, 8 }; + +int* getA() { return A; } diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c new file mode 100644 index 0000000..0340138 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// this strong A should not override weak +// As in loaded code because of RTLD_LOCAL +int A[] = { 10, 11, 12, 13 }; + +typedef int* (*getproc)(void); + +int main(int argc, const char* argv[]) +{ + // open first object + void* fooHandle = dlopen("libfoo.dylib", RTLD_LOCAL); + if ( fooHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // open second object + void* barHandle = dlopen("libbar.dylib", RTLD_LOCAL); + if ( barHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libbar.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get functions + getproc fooproc = (getproc)dlsym(fooHandle, "getA"); + if ( fooproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + getproc barproc = (getproc)dlsym(barHandle, "getA"); + if ( barproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get values + int* fooA = (*fooproc)(); + int* barA = (*barproc)(); + + if ( fooA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == A"); + else if ( barA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: barA == A"); + else if ( fooA == barA ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == barA"); + else + PASS("dlopen-RTLD_LOCAL-weak"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile b/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile index 260c1e0..e6a5e5a 100644 --- a/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main foo.bundle bar.bundle ./main foo.dylib bar.bundle diff --git a/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile b/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile index 776d676..800a40c 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle test.dylib diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile index 3cbb8e5..b6a476e 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile @@ -34,7 +34,9 @@ include ${TESTROOT}/include/common.makefile ### -run: all +all-check: all check + +check: export DYLD_FALLBACK_LIBRARY_PATH=hide && ./main /foo/bar/libfoo.dylib all: main diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile index 3bb5738..75479e4 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile index 92a0b79..eb6db50 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile @@ -23,13 +23,16 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) ### ### Test that RTLD_NOLOAD finds existing image ### even when symlinks are used to obscure it ### -run: all +all-check: all check + +check: ./main libfoosym.dylib ./main2 libbar.dylib @@ -43,7 +46,7 @@ libfoosym.dylib : libfoo.dylib ln -sf libfoo.dylib libfoosym.dylib libfoo.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib foo.c -o `pwd`/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o ${PWD}/libfoo.dylib main2 : main.c libbarsym.dylib ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbarsym.dylib -o main2 @@ -52,7 +55,7 @@ libbarsym.dylib : libbar.dylib ln -sf libbar.dylib libbarsym.dylib libbar.dylib : bar.c - ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name `pwd`/libbarsym.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name ${PWD}/libbarsym.dylib clean: ${RM} ${RMFLAGS} *~ main main2 libfoo.dylib libfoosym.dylib libbar.dylib libbarsym.dylib diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile index 624f4f1..f634e7b 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main libfoo.dylib diff --git a/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile index 0d2ffe2..3b639ea 100644 --- a/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile +++ b/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle foo.dylib foo_foo2.dylib diff --git a/unit-tests/test-cases/dlopen-basic/Makefile b/unit-tests/test-cases/dlopen-basic/Makefile index 776d676..800a40c 100644 --- a/unit-tests/test-cases/dlopen-basic/Makefile +++ b/unit-tests/test-cases/dlopen-basic/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle test.dylib diff --git a/unit-tests/test-cases/dlopen-dyld-locking/Makefile b/unit-tests/test-cases/dlopen-dyld-locking/Makefile index 9902a73..dcf5be4 100644 --- a/unit-tests/test-cases/dlopen-dyld-locking/Makefile +++ b/unit-tests/test-cases/dlopen-dyld-locking/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main libbar.dylib diff --git a/unit-tests/test-cases/dlopen-dyld-locking/base.c b/unit-tests/test-cases/dlopen-dyld-locking/base.c index 4f842dc..6947d31 100644 --- a/unit-tests/test-cases/dlopen-dyld-locking/base.c +++ b/unit-tests/test-cases/dlopen-dyld-locking/base.c @@ -41,6 +41,7 @@ static void __attribute__((constructor)) myinit() void waitForState(int value) { + //fprintf(stderr, "waitForState(%d), currently %d\n", value, sValue); pthread_mutex_lock(&sBarrierMutex); while ( sValue < value ) { struct timeval tvNow; @@ -49,7 +50,7 @@ void waitForState(int value) TIMEVAL_TO_TIMESPEC(&tvNow, &tsTimeout); tsTimeout.tv_sec += 2; // fail if block for 2 seconds if ( pthread_cond_timedwait(&sBarrierFree, &sBarrierMutex, &tsTimeout) == ETIMEDOUT ) { - FAIL("dlsym-dyld-locking"); + FAIL("dlopen-dyld-locking: lock timed out"); exit(0); } } @@ -60,6 +61,7 @@ void waitForState(int value) void setState(int value) { pthread_mutex_lock(&sBarrierMutex); + //fprintf(stderr, "setState(%d)\n", value); sValue = value; pthread_cond_broadcast(&sBarrierFree); pthread_mutex_unlock(&sBarrierMutex); diff --git a/unit-tests/test-cases/dlopen-dyld-locking/main.c b/unit-tests/test-cases/dlopen-dyld-locking/main.c index 7952613..3c68a9d 100644 --- a/unit-tests/test-cases/dlopen-dyld-locking/main.c +++ b/unit-tests/test-cases/dlopen-dyld-locking/main.c @@ -61,7 +61,7 @@ int main() exit(0); } - PASS("dlsym-dyld-locking"); + PASS("dlopen-dyld-locking"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dlopen-error/Makefile b/unit-tests/test-cases/dlopen-error/Makefile new file mode 100644 index 0000000..797e0c4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-error/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2008 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${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-error/foo.c b/unit-tests/test-cases/dlopen-error/foo.c new file mode 100644 index 0000000..0fd6c1e --- /dev/null +++ b/unit-tests/test-cases/dlopen-error/foo.c @@ -0,0 +1,2 @@ + +void foo() {} diff --git a/unit-tests/test-cases/dlopen-error/main.c b/unit-tests/test-cases/dlopen-error/main.c new file mode 100644 index 0000000..481db0c --- /dev/null +++ b/unit-tests/test-cases/dlopen-error/main.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strstr() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // test error message of a dylib that does not exist + void* handle = dlopen("libdoesnotexist.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libdoesnotexist.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "image not found") == NULL ) { + FAIL("dlopen-error: expected 'image not found' in dlerror() string"); + return 0; + } + + // test error message of a dylib that is not readabble + handle = dlopen("libnoread.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libnoread.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "failed with errno=13") == NULL ) { + FAIL("dlopen-error: expected 'failed with errno=13' in dlerror() string"); + return 0; + } + + + PASS("dlopen-error"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-executable/Makefile b/unit-tests/test-cases/dlopen-executable/Makefile new file mode 100644 index 0000000..a3afc96 --- /dev/null +++ b/unit-tests/test-cases/dlopen-executable/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.exe foo.pie + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.exe : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.exe + +foo.pie : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.pie -Wl,-pie + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.exe foo.pie + diff --git a/unit-tests/test-cases/dlopen-executable/foo.c b/unit-tests/test-cases/dlopen-executable/foo.c new file mode 100644 index 0000000..d639011 --- /dev/null +++ b/unit-tests/test-cases/dlopen-executable/foo.c @@ -0,0 +1,10 @@ +int foo() +{ + return 10; +} + +int main() +{ + return 0; +} + diff --git a/unit-tests/test-cases/dlopen-executable/main.c b/unit-tests/test-cases/dlopen-executable/main.c new file mode 100644 index 0000000..a9a6f21 --- /dev/null +++ b/unit-tests/test-cases/dlopen-executable/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*fooproc)(void); + +int main() +{ + // dlopen of regular executable should fail + void* handle = dlopen("./foo.exe", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.exe\") did not fail"); + return EXIT_SUCCESS; + } + + // dlopen of pie should work + handle = dlopen("./foo.pie", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.pie\") failed with: %s", dlerror()); + return EXIT_SUCCESS; + } + + fooproc pfoo = (fooproc)dlsym(handle, "foo"); + if ( pfoo == NULL ) { + FAIL("dlopen-executable: dlsym(handle, \"foo\") failed"); + return EXIT_SUCCESS; + } + + if ( (*pfoo)() != 10 ) { + FAIL("dlopen-executable: foo() != 10"); + return EXIT_SUCCESS; + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlopen-executable: dlclose(handle) returned %d", result); + return EXIT_SUCCESS; + } + + PASS("dlopen-executable"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile b/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile index cc201a6..fec0176 100644 --- a/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main foo.bundle diff --git a/unit-tests/test-cases/dlopen-in-initializer/Makefile b/unit-tests/test-cases/dlopen-in-initializer/Makefile index d3dd9c2..8623188 100644 --- a/unit-tests/test-cases/dlopen-in-initializer/Makefile +++ b/unit-tests/test-cases/dlopen-in-initializer/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main || echo "FAIL dlopen-in-initializer" all: main test.bundle test.dylib diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile b/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile new file mode 100644 index 0000000..b24fa5f --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile @@ -0,0 +1,62 @@ +all-check: all check + +check:## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### ADOBE: Premiere Pro crashes on quit +### +### libfoo depends on libfoo1 and libfoo2. main dlopens(libfoo). +### libfoo1 has an initializer that calls dlopen(libbar). +### libbar depends on libfoo2 +### + +all-check: all check + +check: + ./main + +all: main + +main : main.cxx libfoo.dylib + ${CXX} ${CCXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +libfoo.dylib : foo.c libfoo1.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libfoo1.dylib libfoo2.dylib + +libfoo1.dylib : foo1.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o libfoo1.dylib + +libfoo2.dylib : foo2.c + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o libfoo2.dylib + +libbar.dylib : bar.c libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib libfoo1.dylib libfoo2.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c b/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c new file mode 100644 index 0000000..edbdbc4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c @@ -0,0 +1,31 @@ +/* + * 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 + + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c new file mode 100644 index 0000000..c24996b --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen to verify that initializer lock can be held recursively + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo1() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c new file mode 100644 index 0000000..5dd0a2a --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c @@ -0,0 +1,37 @@ +/* + * 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 + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + +} + + +int foo2() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx b/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx new file mode 100644 index 0000000..cb916f2 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx @@ -0,0 +1,82 @@ +/* + * 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 +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + +static std::set sCurrentImages; + +static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + //fprintf(stderr, "mh=%p\n", mh); + if ( sCurrentImages.count(mh) != 0 ) { + FAIL("notified twice about %p", mh); + exit(0); + } + sCurrentImages.insert(mh); +} + + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-notify"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile b/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile index 723ff97..8144bce 100644 --- a/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main || echo "FAIL dlopen-init-dlopen-up" all: main diff --git a/unit-tests/test-cases/dlopen-init-dlopen/Makefile b/unit-tests/test-cases/dlopen-init-dlopen/Makefile index 4a35562..67d3a63 100644 --- a/unit-tests/test-cases/dlopen-init-dlopen/Makefile +++ b/unit-tests/test-cases/dlopen-init-dlopen/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main || echo "FAIL dlopen-init-dlopen" all: main diff --git a/unit-tests/test-cases/dlopen-init-up/Makefile b/unit-tests/test-cases/dlopen-init-up/Makefile index 3991ab4..104e51b 100644 --- a/unit-tests/test-cases/dlopen-init-up/Makefile +++ b/unit-tests/test-cases/dlopen-init-up/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main || echo "FAIL dlopen-init-dlopen-up" all: main diff --git a/unit-tests/test-cases/dlopen-initializer/Makefile b/unit-tests/test-cases/dlopen-initializer/Makefile index e307053..a4bc8a5 100644 --- a/unit-tests/test-cases/dlopen-initializer/Makefile +++ b/unit-tests/test-cases/dlopen-initializer/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test1.dylib test2.dylib diff --git a/unit-tests/test-cases/dlopen-leak-threaded/Makefile b/unit-tests/test-cases/dlopen-leak-threaded/Makefile new file mode 100644 index 0000000..1afad69 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak-threaded/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/dlopen-leak-threaded/main.c b/unit-tests/test-cases/dlopen-leak-threaded/main.c new file mode 100644 index 0000000..c7fc150 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak-threaded/main.c @@ -0,0 +1,84 @@ +/* + * 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 +#include +#include +#include +#include +#include + + +#include "test.h" + + +/// +/// Test that the dyld copy of the unwinder keeps each thread's +/// exception object seperate. +/// + +static void* work(void* arg) +{ + // will fail and cause exception to be thrown inside dyld + dlopen((char*)arg, RTLD_LAZY); + return NULL; +} + + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, "/frazzle/bar") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, "/frazzle/foo") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, "/frazzle/dazzle") != 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); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + // 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-leak/Makefile b/unit-tests/test-cases/dlopen-leak/Makefile new file mode 100644 index 0000000..b3a47a3 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak/Makefile @@ -0,0 +1,68 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/unit-tests/test-cases/dlopen-leak/bar.c b/unit-tests/test-cases/dlopen-leak/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/unit-tests/test-cases/dlopen-leak/foo.c b/unit-tests/test-cases/dlopen-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen-leak/main.c b/unit-tests/test-cases/dlopen-leak/main.c new file mode 100644 index 0000000..511a140 --- /dev/null +++ b/unit-tests/test-cases/dlopen-leak/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#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); + dlopen("libnotthere.dylib", RTLD_LAZY); + } + + // 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-local-and-global/Makefile b/unit-tests/test-cases/dlopen-local-and-global/Makefile index 90f8872..dae1997 100644 --- a/unit-tests/test-cases/dlopen-local-and-global/Makefile +++ b/unit-tests/test-cases/dlopen-local-and-global/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main-local-first ./main-global-first diff --git a/unit-tests/test-cases/dlopen-multi/Makefile b/unit-tests/test-cases/dlopen-multi/Makefile index 776d676..800a40c 100644 --- a/unit-tests/test-cases/dlopen-multi/Makefile +++ b/unit-tests/test-cases/dlopen-multi/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle test.dylib diff --git a/unit-tests/test-cases/dlopen-notify-bind/Makefile b/unit-tests/test-cases/dlopen-notify-bind/Makefile new file mode 100644 index 0000000..5844be2 --- /dev/null +++ b/unit-tests/test-cases/dlopen-notify-bind/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### CFSTRs cause crashes in Leopard +### +### main registers for notification and then dlopens(libfoo). +### In the notification callback, main calls NSLookupSymbolInImage(BIND) +### which double bound libfoo +### + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.4 + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-notify-bind/foo.c b/unit-tests/test-cases/dlopen-notify-bind/foo.c new file mode 100644 index 0000000..8dab6be --- /dev/null +++ b/unit-tests/test-cases/dlopen-notify-bind/foo.c @@ -0,0 +1,32 @@ +/* + * 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 + + +void* externalRlocToMalloc = &malloc; + +void* foo() +{ + return externalRlocToMalloc; +} diff --git a/unit-tests/test-cases/dlopen-notify-bind/main.c b/unit-tests/test-cases/dlopen-notify-bind/main.c new file mode 100644 index 0000000..d99436a --- /dev/null +++ b/unit-tests/test-cases/dlopen-notify-bind/main.c @@ -0,0 +1,68 @@ +/* + * 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 +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +typedef void* (*fooProc)(); + + +static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + //fprintf(stderr, "mh=%p\n", mh); + NSLookupSymbolInImage(mh, "_bar", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); +} + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + exit(0); + } + + fooProc fooPtr = (fooProc)dlsym(handle, "foo"); + if ( fooPtr == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + void* foosMalloc = (*fooPtr)(); + //fprintf(stderr, "foo says &malloc=%p\n", foosMalloc); + //fprintf(stderr, "&malloc=%p\n", &malloc); + + dlclose(handle); + + if ( foosMalloc == &malloc ) + PASS("dlopen-notify-bind"); + else + FAIL("dlopen-notify-bind libfoo.dylib double bound"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-zero/Makefile b/unit-tests/test-cases/dlopen-zero/Makefile index 92ea1b0..b8a5145 100644 --- a/unit-tests/test-cases/dlopen-zero/Makefile +++ b/unit-tests/test-cases/dlopen-zero/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlopen-zero/main.c b/unit-tests/test-cases/dlopen-zero/main.c index 7240521..81977a4 100644 --- a/unit-tests/test-cases/dlopen-zero/main.c +++ b/unit-tests/test-cases/dlopen-zero/main.c @@ -49,6 +49,7 @@ int main() FAIL("dlsym(handle, \"foo\") failed"); exit(1); } + if ( sym != &foo ) { FAIL("dlsym(handle, \"foo\") returned wrong address"); exit(1); diff --git a/unit-tests/test-cases/dlopen_preflight-basic/Makefile b/unit-tests/test-cases/dlopen_preflight-basic/Makefile index 4b1d3e7..285779f 100644 --- a/unit-tests/test-cases/dlopen_preflight-basic/Makefile +++ b/unit-tests/test-cases/dlopen_preflight-basic/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main ./main batch diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile new file mode 100644 index 0000000..9562ac5 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen_preflight() if a non-batch image state handler +# denies the load +# Leaked fSegmentsArray and image segments during failed dlopen_preflight +# + + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak-image-deny-single" "dlopen_preflight-leak-image-deny-single" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +libfoo.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c new file mode 100644 index 0000000..a7ffa59 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +static bool doCheck = false; + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( doCheck ) + return "can't load"; + return NULL; +} + + +int main() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, batchMappedHandler); + doCheck = true; + + for (int i=0; i < 10; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // 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-leak/Makefile b/unit-tests/test-cases/dlopen_preflight-leak/Makefile new file mode 100644 index 0000000..eff03d8 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak" "dlopen_preflight-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak" "dlopen_preflight-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/unit-tests/test-cases/dlopen_preflight-leak/bar.c b/unit-tests/test-cases/dlopen_preflight-leak/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/unit-tests/test-cases/dlopen_preflight-leak/foo.c b/unit-tests/test-cases/dlopen_preflight-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen_preflight-leak/main.c b/unit-tests/test-cases/dlopen_preflight-leak/main.c new file mode 100644 index 0000000..612e141 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-leak/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // 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/Makefile b/unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile new file mode 100644 index 0000000..8a5e93a --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-shared-cache/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libbar.dylib + ${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 + diff --git a/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c b/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-shared-cache/bar.c @@ -0,0 +1 @@ + diff --git a/unit-tests/test-cases/dlopen_preflight-shared-cache/main.c b/unit-tests/test-cases/dlopen_preflight-shared-cache/main.c new file mode 100644 index 0000000..6c69905 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-shared-cache/main.c @@ -0,0 +1,64 @@ +/* + * 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 // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#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()); + 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); + } + } + + + PASS("dlopen_preflight-shared-cache"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile b/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile index d029efe..d1f6bf0 100644 --- a/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile +++ b/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle test.dylib diff --git a/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile new file mode 100644 index 0000000..9e2b6da --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile @@ -0,0 +1,22 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/text-relocs/foo.s b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c similarity index 94% rename from unit-tests/test-cases/text-relocs/foo.s rename to unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c index 7f8821e..a82c67d 100644 --- a/unit-tests/test-cases/text-relocs/foo.s +++ b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c @@ -22,9 +22,12 @@ */ - .text - .globl _foo -_foo: - .long _bar - - +int foo() +{ + return 10; +} + +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c new file mode 100644 index 0000000..65aacee --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008 Apple, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 0; +} + + +int main() +{ + // make sure we get the foo() in main and not the one in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "foo") != &foo ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"foo\") returned wrong value"); + } + + // make sure don't find bar() in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "bar") != NULL ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"bar\") returned non-NULL"); + } + + + PASS("dlsym-RTLD_MAIN_ONLY"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile index 980ce61..8be6211 100644 --- a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile @@ -34,7 +34,9 @@ endif -run: all +all-check: all check + +check: ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ${PROG} all: main diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile b/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile index af0dec0..4a148d8 100644 --- a/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile b/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile index af0dec0..4a148d8 100644 --- a/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile +++ b/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlsym-error/Makefile b/unit-tests/test-cases/dlsym-error/Makefile index 92ea1b0..b8a5145 100644 --- a/unit-tests/test-cases/dlsym-error/Makefile +++ b/unit-tests/test-cases/dlsym-error/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/dlsym-indirect/Makefile b/unit-tests/test-cases/dlsym-indirect/Makefile index b363103..f806baa 100644 --- a/unit-tests/test-cases/dlsym-indirect/Makefile +++ b/unit-tests/test-cases/dlsym-indirect/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main foo.dylib ./main foo.bundle diff --git a/unit-tests/test-cases/dtrace-static-probes/Makefile b/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..3c40c6f --- /dev/null +++ b/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that dtrace probes fire +# + +all-check: all check + +check: + if [ `sudo dtrace -q -n 'Foo$$target:::count { printf("%u\n", arg0); } ' -c ./main | wc -w` == 5 ]; \ + then \ + echo "PASS dtrace-static-probes"; \ + else \ + echo "FAIL dtrace-static-probes"; \ + fi; \ + + + +all: main + + +main: main.c + dtrace -h -s foo.d + ${CC} ${CCFLAGS} main.c -o main -dead_strip + + +clean: + rm -rf main foo.h + diff --git a/unit-tests/test-cases/dtrace-static-probes/foo.d b/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 0000000..930965b --- /dev/null +++ b/unit-tests/test-cases/dtrace-static-probes/foo.d @@ -0,0 +1,7 @@ + +provider Foo { + probe count(int); +}; + + +#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/unit-tests/test-cases/dtrace-static-probes/main.c b/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 0000000..851bb33 --- /dev/null +++ b/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,13 @@ + +#include + +#include "foo.h" + + +int main() { + for (int i=0; i < 5; ++i) { + FOO_COUNT(i); + } + + return 0; +} diff --git a/unit-tests/test-cases/dyld-func-lookup/Makefile b/unit-tests/test-cases/dyld-func-lookup/Makefile index 56ebee5..00ad156 100644 --- a/unit-tests/test-cases/dyld-func-lookup/Makefile +++ b/unit-tests/test-cases/dyld-func-lookup/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,20 +23,22 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main test.bundle test.dylib main : main.c foo.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c -mmacosx-version-min=10.5 test.bundle : foo.c - ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle -mmacosx-version-min=10.5 test.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib -mmacosx-version-min=10.5 diff --git a/unit-tests/test-cases/dyld-launched-prebound/Makefile b/unit-tests/test-cases/dyld-launched-prebound/Makefile index 7233096..61f202b 100644 --- a/unit-tests/test-cases/dyld-launched-prebound/Makefile +++ b/unit-tests/test-cases/dyld-launched-prebound/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ${TESTROOT}/bin/exit-zero-pass.pl "_dyld_launched_prebound() was implemented" "_dyld_launched_prebound() was not implemented" ./main all: diff --git a/unit-tests/test-cases/dyld-slide/Makefile b/unit-tests/test-cases/dyld-slide/Makefile index 98fd14d..d17bbaa 100644 --- a/unit-tests/test-cases/dyld-slide/Makefile +++ b/unit-tests/test-cases/dyld-slide/Makefile @@ -23,11 +23,27 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +STACK_BASE = 0x8fe01000 + +ifeq "armv6" "$(ARCH)" + STACK_BASE = 0x2fe01000 +endif +ifeq "ppc64" "$(ARCH)" + STACK_BASE = 0x7fff5fc00000 +endif +ifeq "x86_64" "$(ARCH)" + STACK_BASE = 0x7fff5fc00000 +endif + + + +all-check: all check + +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 -pagezero_size 0x40000000 + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -Wl,-stack_addr,${STACK_BASE} -Wl,-stack_size,0x00100000 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 957dfef..2bd67ba 100644 --- a/unit-tests/test-cases/dyld-slide/main.c +++ b/unit-tests/test-cases/dyld-slide/main.c @@ -28,10 +28,13 @@ // // This builds an executable that is just big enough to force dyld to slide a bit // +#if __arm__ + #define ARRAY_SIZE 66582528 +#else + #define ARRAY_SIZE 335400000 +#endif -#define ARRAY_SIZE 335400000 - -int bigarray1[ARRAY_SIZE]; +//int bigarray1[ARRAY_SIZE]; int main() diff --git a/unit-tests/test-cases/dynamic_cast-basic/Makefile b/unit-tests/test-cases/dynamic_cast-basic/Makefile new file mode 100644 index 0000000..79e8be6 --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/Makefile @@ -0,0 +1,24 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.cxx librealmain.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx librealmain.dylib + +librealmain.dylib: realmain.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.cxx + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/dynamic_cast-basic/foo.cxx b/unit-tests/test-cases/dynamic_cast-basic/foo.cxx new file mode 100644 index 0000000..5c824bb --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/foo.cxx @@ -0,0 +1,15 @@ + +#include + +#include "foo.h" + +test* maketestsub() +{ + return new testsub(); +} + +bool istestsub(test* t) +{ + return (dynamic_cast(t) != NULL); +} + diff --git a/unit-tests/test-cases/dynamic_cast-basic/foo.h b/unit-tests/test-cases/dynamic_cast-basic/foo.h new file mode 100644 index 0000000..940e356 --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/foo.h @@ -0,0 +1,32 @@ + + +class test +{ +public: + virtual void aa() {} + + int f; +}; + +class testsub : public test +{ +public: + testsub() : g(0) {} + virtual void aa() {} + + int g; +}; + + +class testsubother : public test +{ +public: + testsubother() : h(0) {} + virtual void aa() {} + + int h; +}; + +extern test* maketestsub(); + +extern bool istestsub(test* t); \ No newline at end of file diff --git a/unit-tests/test-cases/dynamic_cast-basic/main.cxx b/unit-tests/test-cases/dynamic_cast-basic/main.cxx new file mode 100644 index 0000000..a25c448 --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/main.cxx @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx b/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx new file mode 100644 index 0000000..e951768 --- /dev/null +++ b/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "foo.h" + + +void realmain() +{ + test* t1 = maketestsub(); + test* t2 = new testsub(); + test* t3 = new testsubother(); + testsub* t1a = dynamic_cast(t1); + testsubother* t3a = dynamic_cast(t3); + if ( (t1a == NULL) || (t3a == NULL) || !istestsub(t2) ) + FAIL("dynamic_cast-basic"); + else + PASS("dynamic_cast-basic"); +} + + diff --git a/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..ab33f8c --- /dev/null +++ b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/hide && ./main hide/libz.dylib + ./main /usr/lib/libz + export DYLD_FALLBACK_LIBRARY_PATH="" && ${TESTROOT}/bin/exit-non-zero-pass.pl "env-DYLD_FALLBACK_LIBRARY_PATH" "env-DYLD_FALLBACK_LIBRARY_PATH with empty env" ./main2 + + +all: + mkdir -p hide + ${CC} compress.c -dynamiclib -o hide/libz.dylib -install_name /other/libz.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libz.dylib + ${CC} -I${TESTROOT}/include main.c -DSHOULD_FAIL=1 -o main2 hide/libz.dylib + + +clean: + ${RM} -rf *~ main main2 hide diff --git a/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c new file mode 100644 index 0000000..b45d613 --- /dev/null +++ b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c @@ -0,0 +1,4 @@ +int compress() +{ + return 0; +} diff --git a/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..3534032 --- /dev/null +++ b/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "test.h" + +extern void compress(); + +int main(int argc, const char* argv[]) +{ +#if !SHOULD_FAIL + Dl_info info; + if ( dladdr(&compress, &info) ) { + //fprintf(stderr, "_compress found in %s\n", info.dli_fname); + if ( strstr(info.dli_fname, argv[1]) != 0 ) { + PASS("env-DYLD_FALLBACK_LIBRARY_PATH"); + exit(0); + } + } + FAIL("env-DYLD_FALLBACK_LIBRARY_PATH"); +#endif + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/executable-image-index/Makefile b/unit-tests/test-cases/executable-image-index/Makefile index ae7321e..b85494d 100644 --- a/unit-tests/test-cases/executable-image-index/Makefile +++ b/unit-tests/test-cases/executable-image-index/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: #export DYLD_INSERT_LIBRARIES=libfoo.dylib && ./main ./main diff --git a/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile b/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile index 8b4d218..1992633 100644 --- a/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile +++ b/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile @@ -23,21 +23,31 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -all : main - export DYLD_FALLBACK_LIBRARY_PATH=$$PWD && ./main +PWD = $(shell pwd) + + +all-check: all check + + +all: main main : main.c libfoo.dylib libbar.dylib ${CC} -I${TESTROOT}/include main.c libfoo.dylib libbar.dylib -o main libfoo.dylib : foo.c - ${CC} -I${TESTROOT}/include foo.c -DFOO=1 -dynamiclib -o $$PWD/libfoo.dylib + ${CC} -I${TESTROOT}/include foo.c -DFOO=1 -dynamiclib -install_name ${PWD}/libfoo.dylib -o libfoo.dylib libbar.dylib : bar.c other/libfoo.dylib - ${CC} -I${TESTROOT}/include bar.c -dynamiclib other/libfoo.dylib -o $$PWD/libbar.dylib - + ${CC} -I${TESTROOT}/include bar.c -dynamiclib other/libfoo.dylib -install_name ${PWD}/libbar.dylib -o libbar.dylib + other/libfoo.dylib : foo.c - mkdir -p $$PWD/other - ${CC} -I${TESTROOT}/include foo.c -DFOO=2 -dynamiclib -o $$PWD/other/libfoo.dylib + mkdir -p ${PWD}/other + ${CC} -I${TESTROOT}/include foo.c -DFOO=2 -dynamiclib -install_name ${PWD}/other/libfoo.dylib -o other/libfoo.dylib + + + +check: + export DYLD_FALLBACK_LIBRARY_PATH=${PWD} && ./main clean: diff --git a/unit-tests/test-cases/fallback-with-suid/Makefile b/unit-tests/test-cases/fallback-with-suid/Makefile index 6fd0158..af3f2b9 100644 --- a/unit-tests/test-cases/fallback-with-suid/Makefile +++ b/unit-tests/test-cases/fallback-with-suid/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ${TESTROOT}/bin/exit-non-zero-pass.pl "fallback-with-suid" "fallback-with-suid" ./main-suid all: main-suid diff --git a/unit-tests/test-cases/flat-data/Makefile b/unit-tests/test-cases/flat-data/Makefile index 5f54e4b..392ffaa 100644 --- a/unit-tests/test-cases/flat-data/Makefile +++ b/unit-tests/test-cases/flat-data/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/flat-insert/Makefile b/unit-tests/test-cases/flat-insert/Makefile index ff17213..9ce9b26 100644 --- a/unit-tests/test-cases/flat-insert/Makefile +++ b/unit-tests/test-cases/flat-insert/Makefile @@ -26,7 +26,9 @@ include ${TESTROOT}/include/common.makefile ### verify inserted libraries override with flat namespace -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libfoo.dylib" && ./main all: diff --git a/unit-tests/test-cases/flat-insert/main.c b/unit-tests/test-cases/flat-insert/main.c index 160955c..14cb600 100644 --- a/unit-tests/test-cases/flat-insert/main.c +++ b/unit-tests/test-cases/flat-insert/main.c @@ -37,7 +37,7 @@ int main(int argc, const char* argv[]) } // inserted library has another copy of foo() that should -// overridde this one and return 42 +// override this one and return 42 int foo() { return 0; diff --git a/unit-tests/test-cases/flat-prebound/Makefile b/unit-tests/test-cases/flat-prebound/Makefile index 8727921..caf93e9 100644 --- a/unit-tests/test-cases/flat-prebound/Makefile +++ b/unit-tests/test-cases/flat-prebound/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main @@ -33,11 +35,11 @@ main : main.c libfoo.dylib libfoo.dylib : foo.c libbar.dylib - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 + ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 libbar.dylib : bar.c - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 + ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 diff --git a/unit-tests/test-cases/flat-private-extern/Makefile b/unit-tests/test-cases/flat-private-extern/Makefile index c68be00..fe7dbc1 100644 --- a/unit-tests/test-cases/flat-private-extern/Makefile +++ b/unit-tests/test-cases/flat-private-extern/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/framework-fallback/Makefile b/unit-tests/test-cases/framework-fallback/Makefile index 44cfad1..f29fb62 100644 --- a/unit-tests/test-cases/framework-fallback/Makefile +++ b/unit-tests/test-cases/framework-fallback/Makefile @@ -23,10 +23,12 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main -all : main +all: main main: main.c ${CC} ${CCFLAGS} -Wno-deprecated-declarations main.c -o main -I${TESTROOT}/include diff --git a/unit-tests/test-cases/framework-fallback/main.c b/unit-tests/test-cases/framework-fallback/main.c index 13f8146..904aeb3 100644 --- a/unit-tests/test-cases/framework-fallback/main.c +++ b/unit-tests/test-cases/framework-fallback/main.c @@ -39,12 +39,12 @@ main(int argc, const char* argv[]) { const struct mach_header *image; - image = NSAddImage("Carbon.framework/Carbon", + image = NSAddImage("AppKit.framework/AppKit", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_WITH_SEARCHING); if ( image != NULL ) - PASS("Carbon loaded"); + PASS("AppKit loaded"); else - FAIL("Could not load Carbon"); + FAIL("Could not load AppKit"); return 0; } diff --git a/unit-tests/test-cases/ignore-bad-files/Makefile b/unit-tests/test-cases/ignore-bad-files/Makefile index b5c0a4c..b181007 100644 --- a/unit-tests/test-cases/ignore-bad-files/Makefile +++ b/unit-tests/test-cases/ignore-bad-files/Makefile @@ -30,7 +30,9 @@ include ${TESTROOT}/include/common.makefile # -run: all +all-check: all check + +check: export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir && ${TESTROOT}/bin/exit-non-zero-pass.pl "ignore-bad-files intended failure" "ignore-bad-files intended failure" ./main export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir:locations/real && ${TESTROOT}/bin/exit-zero-pass.pl "ignore-bad-files intended success" "ignore-bad-files intended success" ./main diff --git a/unit-tests/test-cases/image-remove-notification/Makefile b/unit-tests/test-cases/image-remove-notification/Makefile index a1ad36d..0e1b901 100644 --- a/unit-tests/test-cases/image-remove-notification/Makefile +++ b/unit-tests/test-cases/image-remove-notification/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main foo.bundle diff --git a/unit-tests/test-cases/image-slide/Makefile b/unit-tests/test-cases/image-slide/Makefile new file mode 100644 index 0000000..9c791ea --- /dev/null +++ b/unit-tests/test-cases/image-slide/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/unit-tests/test-cases/image-slide/foo.c b/unit-tests/test-cases/image-slide/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/unit-tests/test-cases/image-slide/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/unit-tests/test-cases/image-slide/main.c b/unit-tests/test-cases/image-slide/main.c new file mode 100644 index 0000000..f4fb318 --- /dev/null +++ b/unit-tests/test-cases/image-slide/main.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +extern void foo(); + +intptr_t findSlide(const struct mach_header* mh) +{ + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + if ( mh == _dyld_get_image_header(i) ) { + return _dyld_get_image_vmaddr_slide(i); + } + } + return -1; +} + + +int +main() +{ + // find mach_header for libfoo.dylib + const struct mach_header* mh; + Dl_info info; + if ( dladdr(&foo, &info) ) { + mh = info.dli_fbase; + } + else { + FAIL("dladdr() could not find foo()"); + exit(0); + } + + + intptr_t slide1 = _dyld_get_image_slide(mh); + intptr_t slide2 = findSlide(mh); + if ( slide1 == slide2 ) + PASS("image-slide"); + else + FAIL("image-slide: 0x%lX != 0x%lX", slide1, slide2); + + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/image-state-change/Makefile b/unit-tests/test-cases/image-state-change/Makefile index b287606..d4e91c8 100644 --- a/unit-tests/test-cases/image-state-change/Makefile +++ b/unit-tests/test-cases/image-state-change/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main foo.bundle diff --git a/unit-tests/test-cases/image-state-change/main.c b/unit-tests/test-cases/image-state-change/main.c index 0a6673d..6ee6a62 100644 --- a/unit-tests/test-cases/image-state-change/main.c +++ b/unit-tests/test-cases/image-state-change/main.c @@ -34,7 +34,7 @@ static int singleMappedCount = 0; static int batchMappedCount = 0; - +static int singleUnMappedCount = 0; static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) @@ -64,6 +64,21 @@ static const char* singleMappedHandler(enum dyld_image_states state, uint32_t in return NULL; } +static const char* singleUnmappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleUnmappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_terminated ) { + FAIL("image-state-change: singleUnmappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleUnmappedHandler given %d images", infoCount); + exit(0); + } + ++singleUnMappedCount; + return NULL; +} + static void loadAndUnLoad() { void* handle = dlopen("foo.bundle", RTLD_LAZY); @@ -85,6 +100,7 @@ int main(int argc, const char* argv[]) // tell dyld we want to know when images are mapped dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + dyld_register_image_state_change_handler(dyld_image_state_terminated, false, singleUnmappedHandler); // with batch mode we get notified of existing images, but not with single mode, so re-sync counts batchMappedCount=0; @@ -92,7 +108,7 @@ int main(int argc, const char* argv[]) loadAndUnLoad(); - if ( singleMappedCount == batchMappedCount ) + if ( (singleMappedCount == batchMappedCount) && (singleMappedCount == singleUnMappedCount) ) PASS("image-state-change"); else FAIL("image-state-change: batch count=%d, single count=%d", batchMappedCount, singleMappedCount); diff --git a/unit-tests/test-cases/image-state-deny-OFI/Makefile b/unit-tests/test-cases/image-state-deny-OFI/Makefile index 5ad9359..a89cbf9 100644 --- a/unit-tests/test-cases/image-state-deny-OFI/Makefile +++ b/unit-tests/test-cases/image-state-deny-OFI/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main ./main batch diff --git a/unit-tests/test-cases/image-state-deny-dlclose/Makefile b/unit-tests/test-cases/image-state-deny-dlclose/Makefile index ed88495..a67d741 100644 --- a/unit-tests/test-cases/image-state-deny-dlclose/Makefile +++ b/unit-tests/test-cases/image-state-deny-dlclose/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/image-state-deny/Makefile b/unit-tests/test-cases/image-state-deny/Makefile index 4bff299..04973bc 100644 --- a/unit-tests/test-cases/image-state-deny/Makefile +++ b/unit-tests/test-cases/image-state-deny/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main ./main batch diff --git a/unit-tests/test-cases/image-state-dependents-initialized/Makefile b/unit-tests/test-cases/image-state-dependents-initialized/Makefile index 91b2ef1..86f01d8 100644 --- a/unit-tests/test-cases/image-state-dependents-initialized/Makefile +++ b/unit-tests/test-cases/image-state-dependents-initialized/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/image-suffix/Makefile b/unit-tests/test-cases/image-suffix/Makefile index 4317204..87e7bc8 100644 --- a/unit-tests/test-cases/image-suffix/Makefile +++ b/unit-tests/test-cases/image-suffix/Makefile @@ -23,41 +23,46 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run : check1 check2 check3 check4 check5 check6 check7 check8 +PWD = $(shell pwd) -all : main1 main2 main3 main4 main5 main6 main7 main8 + +all-check: all check + +check: check1 check2 check3 check4 check5 check6 check7 check8 + +all: main1 main2 main3 main4 main5 main6 main7 main8 main : main.c libfoo.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main libfoo.dylib libfoo.dylib : foo.c - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/libfoo.dylib + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $(PWD)/libfoo.dylib libfoo_debug.dylib : foo.c - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o $$PWD/libfoo_debug.dylib -install_name $$PWD/libfoo.dylib + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib hide/libfoo.dylib : foo.c - mkdir -p $$PWD/hide - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/hide/libfoo.dylib -install_name $$PWD/libfoo.dylib + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo.dylib -install_name $(PWD)/libfoo.dylib hide/libfoo_debug.dylib : foo.c - mkdir -p $$PWD/hide - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/hide/libfoo_debug.dylib -install_name $$PWD/libfoo.dylib + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib # bar_debug has bar_debug as install name libbar.dylib : foo.c - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/libbar.dylib + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o libbar.dylib libbar_debug.dylib : foo.c - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o $$PWD/libbar_debug.dylib + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libbar_debug.dylib hide/libbar.dylib : foo.c - mkdir -p $$PWD/hide - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/hide/libbar.dylib -install_name $$PWD/libbar.dylib + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar.dylib -install_name $(PWD)/libbar.dylib hide/libbar_debug.dylib : foo.c - mkdir -p $$PWD/hide - ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $$PWD/hide/libbar_debug.dylib -install_name $$PWD/libbar_debug.dylib + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar_debug.dylib -install_name $(PWD)/libbar_debug.dylib clean: rm -rf libfoo.dylib libfoo_debug.dylib hide libbar.dylib libbar_debug.dylib main1 main2 main3 main4 main5 main6 main7 main8 @@ -70,9 +75,9 @@ clean: main1 : main.c libfoo.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main1 libfoo.dylib -check1: main1 +check1: DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 libfoo.dylib - DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 $$PWD/libfoo.dylib + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 $(PWD)/libfoo.dylib # @@ -81,11 +86,11 @@ check1: main1 main2 : main.c libfoo_debug.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main2 libfoo_debug.dylib -check2: main2 +check2: echo "pwd-1 is ${PWD}" - echo "pwd-2 is $$PWD" + echo "pwd-2 is $(PWD)" pwd - ./main2 $$PWD/libfoo.dylib + ./main2 $(PWD)/libfoo.dylib # @@ -94,8 +99,8 @@ check2: main2 main3 : main.c libfoo.dylib ${CC} -Wno-deprecated-declarations -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main3 libfoo.dylib -check3: main3 - DYLD_LIBRARY_PATH=$$PWD/hide && export DYLD_LIBRARY_PATH && ./main3 $$PWD/libfoo.dylib +check3: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main3 $(PWD)/libfoo.dylib # @@ -105,8 +110,8 @@ check3: main3 main4 : main.c libfoo.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main4 libfoo.dylib -check4: main4 - DYLD_LIBRARY_PATH=$$PWD/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main4 $$PWD/libfoo.dylib +check4: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main4 $(PWD)/libfoo.dylib # @@ -116,8 +121,8 @@ check4: main4 main5 : main.c libbar.dylib libbar_debug.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main5 libbar.dylib -check5: main5 - DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main5 $$PWD/libbar.dylib +check5: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main5 $(PWD)/libbar.dylib # @@ -127,8 +132,8 @@ check5: main5 main6 : main.c libbar_debug.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main6 libbar_debug.dylib -check6: main6 - DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main6 $$PWD/libbar.dylib +check6: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main6 $(PWD)/libbar.dylib # @@ -137,8 +142,8 @@ check6: main6 main7 : main.c libbar.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main7 libbar.dylib -check7: main7 - DYLD_LIBRARY_PATH=$$PWD/hide && export DYLD_LIBRARY_PATH && ./main7 $$PWD/libbar.dylib +check7: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main7 $(PWD)/libbar.dylib # @@ -148,5 +153,5 @@ check7: main7 main8 : main.c libbar.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main8 libbar.dylib -check8: main8 - DYLD_LIBRARY_PATH=$$PWD/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main8 $$PWD/libbar.dylib +check8: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main8 $(PWD)/libbar.dylib diff --git a/unit-tests/test-cases/image_path_containing_address/Makefile b/unit-tests/test-cases/image_path_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/unit-tests/test-cases/image_path_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/image_path_containing_address/foo.c b/unit-tests/test-cases/image_path_containing_address/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/image_path_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/image_path_containing_address/main.c b/unit-tests/test-cases/image_path_containing_address/main.c new file mode 100644 index 0000000..2025b1d --- /dev/null +++ b/unit-tests/test-cases/image_path_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2008 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 +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_path_containing_address() return same path +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const char* path = dyld_image_path_containing_address(addr); + if ( path != info.dli_fname ) { + FAIL("dladdr's dli_fname != dyld_image_path_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_path_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("image_path_containing_address"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/init-libSystem-first/Makefile b/unit-tests/test-cases/init-libSystem-first/Makefile new file mode 100644 index 0000000..67424c7 --- /dev/null +++ b/unit-tests/test-cases/init-libSystem-first/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -nostdlib -ldylib1.o -flat_namespace -undefined suppress -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/init-libSystem-first/foo.c b/unit-tests/test-cases/init-libSystem-first/foo.c new file mode 100644 index 0000000..1affc43 --- /dev/null +++ b/unit-tests/test-cases/init-libSystem-first/foo.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +extern char*** _NSGetArgv(void); + +static bool libSystemAlreadyInited = false; + + + +// +static __attribute__((constructor)) void myInit() +{ + libSystemAlreadyInited = ( _NSGetArgv() != NULL ); +} + + + +bool foo() +{ + return libSystemAlreadyInited; +} diff --git a/unit-tests/test-cases/init-libSystem-first/main.c b/unit-tests/test-cases/init-libSystem-first/main.c new file mode 100644 index 0000000..28a4db3 --- /dev/null +++ b/unit-tests/test-cases/init-libSystem-first/main.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("init-libSystem-first"); + else + FAIL("init-libSystem-first"); + + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/init-order/Makefile b/unit-tests/test-cases/init-order/Makefile index dfa47dd..6525dc6 100644 --- a/unit-tests/test-cases/init-order/Makefile +++ b/unit-tests/test-cases/init-order/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/initializer-args/Makefile b/unit-tests/test-cases/initializer-args/Makefile index 1afdb74..3f4e4d0 100644 --- a/unit-tests/test-cases/initializer-args/Makefile +++ b/unit-tests/test-cases/initializer-args/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main arg1 arg2 all: diff --git a/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile b/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile new file mode 100644 index 0000000..be551ef --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that weak symbols in inserted libraries work +# + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main + +all: main libinsert.dylib + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libinsert.dylib: insert.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c b/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c new file mode 100644 index 0000000..9bfcca7 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-weak-symbols/insert.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 + + +void __attribute__((weak)) foo() +{ + +} + + +static __attribute__((constructor)) void init() +{ + // have intializer call weak function + foo(); +} + diff --git a/unit-tests/test-cases/insert-libraries-weak-symbols/main.c b/unit-tests/test-cases/insert-libraries-weak-symbols/main.c new file mode 100644 index 0000000..8593697 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-weak-symbols/main.c @@ -0,0 +1,42 @@ +/* + * 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 "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// since both main and insert.dylib have a weak _foo, +// dyld will coalesce. +void __attribute__((weak)) foo() +{ + +} + + +int main() +{ + PASS("insert-libraries-weak-symbols"); + return EXIT_SUCCESS; +} + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/Makefile b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile index f4ae30a..fc9269f 100644 --- a/unit-tests/test-cases/insert-libraries-with-initializer/Makefile +++ b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile @@ -29,7 +29,9 @@ include ${TESTROOT}/include/common.makefile # -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main all: main libinsert.dylib diff --git a/unit-tests/test-cases/insert-libraries-with-suid/Makefile b/unit-tests/test-cases/insert-libraries-with-suid/Makefile index 7e6ad2a..1380ba9 100644 --- a/unit-tests/test-cases/insert-libraries-with-suid/Makefile +++ b/unit-tests/test-cases/insert-libraries-with-suid/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="/usr/lib/libldap.dylib:/usr/lib/libpcap.dylib" && ./main all: main diff --git a/unit-tests/test-cases/interpose-basic-prebound/Makefile b/unit-tests/test-cases/interpose-basic-prebound/Makefile index 5c35a89..c98a29b 100644 --- a/unit-tests/test-cases/interpose-basic-prebound/Makefile +++ b/unit-tests/test-cases/interpose-basic-prebound/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main all: main libmystrdup.dylib diff --git a/unit-tests/test-cases/interpose-basic/Makefile b/unit-tests/test-cases/interpose-basic/Makefile index 0bd0312..1ea5385 100644 --- a/unit-tests/test-cases/interpose-basic/Makefile +++ b/unit-tests/test-cases/interpose-basic/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main all: main libmystrdup.dylib diff --git a/unit-tests/test-cases/interpose-chained/Makefile b/unit-tests/test-cases/interpose-chained/Makefile index dd89247..2920a5c 100644 --- a/unit-tests/test-cases/interpose-chained/Makefile +++ b/unit-tests/test-cases/interpose-chained/Makefile @@ -36,7 +36,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib:libfoo3.dylib" && ./main all: main libfoo1.dylib libfoo2.dylib libfoo3.dylib diff --git a/unit-tests/test-cases/interpose-dlsym/Makefile b/unit-tests/test-cases/interpose-dlsym/Makefile index 9b9ae73..33e6b2a 100644 --- a/unit-tests/test-cases/interpose-dlsym/Makefile +++ b/unit-tests/test-cases/interpose-dlsym/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libmyfree.dylib" && ./main all: main libmyfree.dylib diff --git a/unit-tests/test-cases/interpose-shared-cache/Makefile b/unit-tests/test-cases/interpose-shared-cache/Makefile new file mode 100644 index 0000000..c1cb7dd --- /dev/null +++ b/unit-tests/test-cases/interpose-shared-cache/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main + +all: main libmymalloc.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libmymalloc.dylib : mymalloc.c + ${CC} ${CCFLAGS} -dynamiclib mymalloc.c -o libmymalloc.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmymalloc.dylib + diff --git a/unit-tests/test-cases/interpose-shared-cache/main.c b/unit-tests/test-cases/interpose-shared-cache/main.c new file mode 100644 index 0000000..192b3b6 --- /dev/null +++ b/unit-tests/test-cases/interpose-shared-cache/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main() +{ + const char* x = strdup("123"); + + if ( (strcmp(&x[-16], "hello") == 0) ) + PASS("interpose-basic-shared-cache"); + else + FAIL("interpose-basic-shared-cache"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/interpose-shared-cache/mymalloc.c b/unit-tests/test-cases/interpose-shared-cache/mymalloc.c new file mode 100644 index 0000000..9afe7f1 --- /dev/null +++ b/unit-tests/test-cases/interpose-shared-cache/mymalloc.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +// return blocks that have preceeding 16-bytes filled with "hello" +void* my_malloc(size_t size) +{ + char* x = malloc(size+16); + strcpy(x, "hello"); + return &x[16]; +} + +DYLD_INTERPOSE(my_malloc, malloc) diff --git a/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile b/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile index 04d3fb6..e21b784 100644 --- a/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile +++ b/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main libfoo.dylib diff --git a/unit-tests/test-cases/jump-table-race/Makefile b/unit-tests/test-cases/jump-table-race/Makefile index abb4839..273748c 100644 --- a/unit-tests/test-cases/jump-table-race/Makefile +++ b/unit-tests/test-cases/jump-table-race/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/lazy-binding-reg-params/Makefile b/unit-tests/test-cases/lazy-binding-reg-params/Makefile index 558fc57..73ce203 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/Makefile +++ b/unit-tests/test-cases/lazy-binding-reg-params/Makefile @@ -45,7 +45,9 @@ endif -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/lazy-binding-reg-params/foo.c b/unit-tests/test-cases/lazy-binding-reg-params/foo.c index 7270f66..e2683b3 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/foo.c +++ b/unit-tests/test-cases/lazy-binding-reg-params/foo.c @@ -77,6 +77,9 @@ bool dofloattest(double p1, double p2, double p3, double p4, double p5, double p #endif + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + static bool comparevFloat(vFloat p1, vFloat p2) { return (memcmp(&p1, &p2, 16) == 0); @@ -103,6 +106,8 @@ bool dovectortest(vFloat p1, vFloat p2, vFloat p3, vFloat p4, vFloat p5) return true; } +#endif + diff --git a/unit-tests/test-cases/lazy-binding-reg-params/foo.h b/unit-tests/test-cases/lazy-binding-reg-params/foo.h index 007f11d..8b76407 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/foo.h +++ b/unit-tests/test-cases/lazy-binding-reg-params/foo.h @@ -12,11 +12,14 @@ extern bool dofloattest(double,double,double,double,double,double,double,double, #endif -#if __i386__ || __x86_64__ - typedef float vFloat __attribute__ ((__vector_size__ (16))); -#elif __ppc__ || __ppc64__ - typedef __vector float vFloat; -#endif +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + + #if __i386__ || __x86_64__ + typedef float vFloat __attribute__ ((__vector_size__ (16))); + #elif __ppc__ || __ppc64__ + typedef __vector float vFloat; + #endif -extern bool dovectortest(vFloat, vFloat, vFloat, vFloat, vFloat); + extern bool dovectortest(vFloat, vFloat, vFloat, vFloat, vFloat); +#endif \ No newline at end of file diff --git a/unit-tests/test-cases/lazy-binding-reg-params/main.c b/unit-tests/test-cases/lazy-binding-reg-params/main.c index 44c98b2..fc66bd1 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/main.c +++ b/unit-tests/test-cases/lazy-binding-reg-params/main.c @@ -48,13 +48,14 @@ static bool floattest() { #if __ppc__ || __ppc64__ return dofloattest(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0); -#elif __i386__ || __x86_64__ +#elif __i386__ || __x86_64__ || __arm__ return true; #else - #error unknown architecture + #error unknown arch #endif } +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ static bool vectorSupport() { #if __ppc__ @@ -68,7 +69,6 @@ static bool vectorSupport() #endif } - static bool vectortest() { vFloat p1 = { 1.1, 1.2, 1.3, 1.4 }; @@ -78,6 +78,7 @@ static bool vectortest() vFloat p5 = { 5.1, 5.2, 5.3, 5.4 }; return dovectortest(p1, p2, p3, p4, p5); } +#endif int main() { @@ -91,11 +92,13 @@ int main() return EXIT_SUCCESS; } +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ if ( vectorSupport() && ! vectortest() ) { FAIL("lazy-binding-reg-params vector parameters"); return EXIT_SUCCESS; } - +#endif + PASS("lazy-binding-reg-params"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/lazy-dylib-init-order/Makefile b/unit-tests/test-cases/lazy-dylib-init-order/Makefile new file mode 100644 index 0000000..f3e0ed5 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-init-order/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main > actual.out + ${PASS_IFF} diff expected.out actual.out + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + + +clean: + rm -f libfoo.dylib main actual.out diff --git a/unit-tests/test-cases/lazy-dylib-init-order/expected.out b/unit-tests/test-cases/lazy-dylib-init-order/expected.out new file mode 100644 index 0000000..3302190 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-init-order/expected.out @@ -0,0 +1,6 @@ +main_init +main +foo_init +foo +foo_term +main_term diff --git a/unit-tests/test-cases/lazy-dylib-init-order/foo.c b/unit-tests/test-cases/lazy-dylib-init-order/foo.c new file mode 100644 index 0000000..1047dce --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-init-order/foo.c @@ -0,0 +1,21 @@ + +#include + +int foo() +{ + printf("foo\n"); + return 1; +} +int bar() { return 1; } + + +static __attribute__((constructor)) void foo_init() +{ + printf("foo_init\n"); +} + +static __attribute__((destructor)) void foo_term() +{ + printf("foo_term\n"); +} + diff --git a/unit-tests/test-cases/lazy-dylib-init-order/main.c b/unit-tests/test-cases/lazy-dylib-init-order/main.c new file mode 100644 index 0000000..06b33ce --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-init-order/main.c @@ -0,0 +1,25 @@ +#include +#include + + +extern int foo(); +extern int bar(); + +static __attribute__((constructor)) void main_init() +{ + printf("main_init\n"); +} + +static __attribute__((destructor)) void main_term() +{ + printf("main_term\n"); +} + + +int main() +{ + printf("main\n"); + foo(); + return 0; +} + diff --git a/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile b/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile new file mode 100644 index 0000000..4cddd76 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib that can't be found errors out cleanly +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib.full main main2 diff --git a/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c b/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c new file mode 100644 index 0000000..f41dbba --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 1; +} diff --git a/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c b/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c new file mode 100644 index 0000000..9ff0032 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should not exist + +int main() +{ +#if LAZY_HANDLER + // sanity check that foo was not found + // dyld_lazy_dylib_proxy returns 7 when function cannot be found + if ( foo() != 7 ) { + FAIL("lazy-dylib-missing-dylib: dyld_lazy_dylib_proxy() not used for foo"); + return 0; + } +#else + // sanity check that foo was not found + // lazy loading return 0 when function cannot be found + if ( foo() != 0 ) { + FAIL("lazy-dylib-missing-dylib: foo found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-dylib"); + return 0; +} + diff --git a/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile b/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile new file mode 100644 index 0000000..cd631ce --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name libfoo.dylib + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib libfoo.dylib.full main main2 diff --git a/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c b/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c new file mode 100644 index 0000000..2c56b39 --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c @@ -0,0 +1,15 @@ + + +int foo() +{ + return 1; +} + +#if BAR +int bar() +{ + return 1; +} +#endif + + diff --git a/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c b/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c new file mode 100644 index 0000000..934e48a --- /dev/null +++ b/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should exist +extern int bar(); // should not exist + +int main() +{ + // sanity check that foo was found + if ( foo() != 1 ) { + FAIL("lazy-dylib-missing-symbol: foo not found"); + return 0; + } + +#if LAZY_HANDLER + // sanity check that bar was not found + // lazy loading return 7 when function cannot be found + if ( bar() != 7 ) { + FAIL("lazy-dylib-missing-symbol: dyld_lazy_dylib_proxy() not used for bar"); + return 0; + } +#else + // sanity check that bar was not found + // lazy loading return 0 when function cannot be found + if ( bar() != 0 ) { + FAIL("lazy-dylib-missing-symbol: bar found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-symbol"); + return 0; +} + diff --git a/unit-tests/test-cases/lazy-pointer-binding/Makefile b/unit-tests/test-cases/lazy-pointer-binding/Makefile index 9a7f756..6d42f96 100644 --- a/unit-tests/test-cases/lazy-pointer-binding/Makefile +++ b/unit-tests/test-cases/lazy-pointer-binding/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/lib-name-overload/Makefile b/unit-tests/test-cases/lib-name-overload/Makefile index 52c2e50..c5a211f 100644 --- a/unit-tests/test-cases/lib-name-overload/Makefile +++ b/unit-tests/test-cases/lib-name-overload/Makefile @@ -35,24 +35,25 @@ include ${TESTROOT}/include/common.makefile PWD = `pwd` +all-check: all check -run : all +check: # verify it runs as-is ./main # verify dyld doesn't hang on the circularity DYLD_LIBRARY_PATH=$(PWD) && export DYLD_LIBRARY_PATH && ${TESTROOT}/bin/exit-zero-pass.pl "lib-name-overload" "lib-name-overload" ./main -all : main +all: main other/libfoo.dylib : foo2.c mkdir -p other - gcc foo2.c -dynamiclib -o $(PWD)/other/libfoo.dylib + ${CC} foo2.c -dynamiclib -o $(PWD)/other/libfoo.dylib libfoo.dylib : foo.c other/libfoo.dylib - gcc foo.c -dynamiclib $(PWD)/other/libfoo.dylib -sub_library libfoo -o $(PWD)/libfoo.dylib + ${CC} foo.c -dynamiclib $(PWD)/other/libfoo.dylib -sub_library libfoo -o $(PWD)/libfoo.dylib main : main.c libfoo.dylib - gcc main.c -I${TESTROOT}/include -o main libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib clean: diff --git a/unit-tests/test-cases/loader_path-dup/Makefile b/unit-tests/test-cases/loader_path-dup/Makefile index a89a431..61d36e4 100644 --- a/unit-tests/test-cases/loader_path-dup/Makefile +++ b/unit-tests/test-cases/loader_path-dup/Makefile @@ -23,6 +23,8 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) + ### ### This test case is to verify that two different dylibs with the same name ### and each loaded via the same @loader_path does not confuse dyld @@ -32,14 +34,16 @@ include ${TESTROOT}/include/common.makefile ## Note, until ld understands @loader_path we have to do a funky make -run: all +all-check: all check + +check: ./main all: main foo/libfoo.dylib : foo.c foo/libbase.dylib mkdir -p foo - ${CC} foo.c foo/libbase.dylib -dynamiclib -o "`pwd`/foo/libfoo.dylib" + ${CC} foo.c foo/libbase.dylib -dynamiclib -o "${PWD}/foo/libfoo.dylib" foo/libbase.dylib : base.c mkdir -p foo @@ -48,7 +52,7 @@ foo/libbase.dylib : base.c bar/libbar.dylib : bar.c bar/libbase.dylib mkdir -p bar - ${CC} bar.c bar/libbase.dylib -dynamiclib -o "`pwd`/bar/libbar.dylib" + ${CC} bar.c bar/libbase.dylib -dynamiclib -o "${PWD}/bar/libbar.dylib" bar/libbase.dylib : base.c mkdir -p bar diff --git a/unit-tests/test-cases/loader_path/Makefile b/unit-tests/test-cases/loader_path/Makefile index 694cd9d..7e646d1 100644 --- a/unit-tests/test-cases/loader_path/Makefile +++ b/unit-tests/test-cases/loader_path/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile b/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile index 4cfeb68..217074c 100644 --- a/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile +++ b/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_FALLBACK_FRAMEWORK_PATH="dir" && ${TESTROOT}/bin/exit-zero-pass.pl "pass message" "fail message" ./main all: main dir/Foo.framework/Versions/Current/Foo diff --git a/unit-tests/test-cases/non-lazy-slide/Makefile b/unit-tests/test-cases/non-lazy-slide/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/unit-tests/test-cases/non-lazy-slide/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/non-lazy-slide/bar.c b/unit-tests/test-cases/non-lazy-slide/bar.c new file mode 100644 index 0000000..fa8deff --- /dev/null +++ b/unit-tests/test-cases/non-lazy-slide/bar.c @@ -0,0 +1,3 @@ + + +int bar = 10; diff --git a/unit-tests/test-cases/non-lazy-slide/foo.c b/unit-tests/test-cases/non-lazy-slide/foo.c new file mode 100644 index 0000000..1b2c9f7 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-slide/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/unit-tests/test-cases/non-lazy-slide/main.c b/unit-tests/test-cases/non-lazy-slide/main.c new file mode 100644 index 0000000..a73a1db --- /dev/null +++ b/unit-tests/test-cases/non-lazy-slide/main.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib is built from multiple source files +// It internally uses non-lazy pointer to compute +// the result for foo() + + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-slide"); + else + FAIL("non-lazy-slide"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/non-lazy-weak/Makefile b/unit-tests/test-cases/non-lazy-weak/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/unit-tests/test-cases/non-lazy-weak/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/non-lazy-weak/bar.c b/unit-tests/test-cases/non-lazy-weak/bar.c new file mode 100644 index 0000000..91a216d --- /dev/null +++ b/unit-tests/test-cases/non-lazy-weak/bar.c @@ -0,0 +1,3 @@ + + +__attribute__((weak)) int bar = 10; diff --git a/unit-tests/test-cases/non-lazy-weak/foo.c b/unit-tests/test-cases/non-lazy-weak/foo.c new file mode 100644 index 0000000..1b2c9f7 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-weak/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/unit-tests/test-cases/non-lazy-weak/main.c b/unit-tests/test-cases/non-lazy-weak/main.c new file mode 100644 index 0000000..16a78b7 --- /dev/null +++ b/unit-tests/test-cases/non-lazy-weak/main.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak global variable that foo() returns + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-weak"); + else + FAIL("non-lazy-weak"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/non-weak-library/Makefile b/unit-tests/test-cases/non-weak-library/Makefile index 2c49d4f..540909d 100644 --- a/unit-tests/test-cases/non-weak-library/Makefile +++ b/unit-tests/test-cases/non-weak-library/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,16 +23,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: cd absent/ && ../${TESTROOT}/bin/exit-non-zero-pass.pl "non-weak-library" "non-weak-library" ./main cd present/ && ./main all: foo.c main.c ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c mkdir -p absent/ - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_ABSENT -o absent/main main.c + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_ABSENT -o absent/main main.c mkdir -p present/ - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_PRESENT -o present/main main.c + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_PRESENT -o present/main main.c mv libfoo.dylib present/ clean: diff --git a/unit-tests/test-cases/operator-new-dylib/Makefile b/unit-tests/test-cases/operator-new-dylib/Makefile new file mode 100644 index 0000000..fbaac19 --- /dev/null +++ b/unit-tests/test-cases/operator-new-dylib/Makefile @@ -0,0 +1,33 @@ + + + + +# +# test that if the main executable overrides operator new +# that a dylib using operator new is redirected to it. +# +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + + +main: main.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/operator-new-dylib/foo.cxx b/unit-tests/test-cases/operator-new-dylib/foo.cxx new file mode 100644 index 0000000..f91f88c --- /dev/null +++ b/unit-tests/test-cases/operator-new-dylib/foo.cxx @@ -0,0 +1,13 @@ + +#include +#include + + + + + +char* foo() +{ + return new char[24]; +} + diff --git a/unit-tests/test-cases/operator-new-dylib/main.cxx b/unit-tests/test-cases/operator-new-dylib/main.cxx new file mode 100644 index 0000000..25be30d --- /dev/null +++ b/unit-tests/test-cases/operator-new-dylib/main.cxx @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + + +extern char* foo(); + +static char all[] = "hello"; + +void* operator new[](std::size_t) throw (std::bad_alloc) +{ + return all; +} + + +int main() +{ + char* newArray = new char[24]; + char* fromFoo = foo(); + + if ( fromFoo == newArray ) + PASS("operator-new-dylib"); + else + FAIL("operator-new-dylib"); + return EXIT_SUCCESS; +} + diff --git a/unit-tests/test-cases/operator-new/Makefile b/unit-tests/test-cases/operator-new/Makefile index fb7fe84..2fbed98 100644 --- a/unit-tests/test-cases/operator-new/Makefile +++ b/unit-tests/test-cases/operator-new/Makefile @@ -24,7 +24,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/operator-new/main.cxx b/unit-tests/test-cases/operator-new/main.cxx index ead5c1e..a0c25ce 100644 --- a/unit-tests/test-cases/operator-new/main.cxx +++ b/unit-tests/test-cases/operator-new/main.cxx @@ -52,6 +52,3 @@ int main() return EXIT_SUCCESS; } - -// add this so WEAK_DEFINES is set, so dyld searchs this image -int __attribute__((weak)) junk = 2; diff --git a/unit-tests/test-cases/partial-library-load/Makefile b/unit-tests/test-cases/partial-library-load/Makefile index 37a84a4..83ef9bd 100644 --- a/unit-tests/test-cases/partial-library-load/Makefile +++ b/unit-tests/test-cases/partial-library-load/Makefile @@ -32,7 +32,9 @@ include ${TESTROOT}/include/common.makefile ### -run: all +all-check: all check + +check: export DYLD_IMAGE_SUFFIX="_missing" && ${TESTROOT}/bin/exit-zero-pass.pl "partial-library-load" "partial-library-load" ./main all: main test.bundle diff --git a/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile b/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile new file mode 100644 index 0000000..f3fd51e --- /dev/null +++ b/unit-tests/test-cases/pie-DYLD_NO_PIE/Makefile @@ -0,0 +1,34 @@ + +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-DYLD_NO_PIE/main.c b/unit-tests/test-cases/pie-DYLD_NO_PIE/main.c new file mode 100644 index 0000000..3a6571d --- /dev/null +++ b/unit-tests/test-cases/pie-DYLD_NO_PIE/main.c @@ -0,0 +1,33 @@ +/* + * 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 +#include + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/unit-tests/test-cases/pie-basic/Makefile b/unit-tests/test-cases/pie-basic/Makefile index 2a2f680..0c6c455 100644 --- a/unit-tests/test-cases/pie-basic/Makefile +++ b/unit-tests/test-cases/pie-basic/Makefile @@ -25,8 +25,9 @@ include ${TESTROOT}/include/common.makefile # run a PIE four times and verify its load address was different every time +all-check: all check -run: main +check: ./main > main.out ./main >> main.out ./main >> main.out @@ -38,6 +39,7 @@ run: main echo "FAIL pie-basic"; \ fi; \ +all: main main : main.c ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 diff --git a/unit-tests/test-cases/pie-big/Makefile b/unit-tests/test-cases/pie-big/Makefile index 6d4d95b..b295994 100644 --- a/unit-tests/test-cases/pie-big/Makefile +++ b/unit-tests/test-cases/pie-big/Makefile @@ -25,8 +25,9 @@ include ${TESTROOT}/include/common.makefile # run a PIE four times and verify its load address was different every time +all-check: all check -run: main +check: ./main > main.out ./main >> main.out ./main >> main.out @@ -38,6 +39,7 @@ run: main echo "XFAIL pie-basic"; \ fi; \ +all: main main : main.c ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 diff --git a/unit-tests/test-cases/pie-custom-stack/Makefile b/unit-tests/test-cases/pie-custom-stack/Makefile index 48ff01e..7c68dbc 100644 --- a/unit-tests/test-cases/pie-custom-stack/Makefile +++ b/unit-tests/test-cases/pie-custom-stack/Makefile @@ -25,8 +25,9 @@ include ${TESTROOT}/include/common.makefile # run a PIE four times and verify its load address was different every time +all-check: all check -run: main +check: ./main > main.out ./main >> main.out ./main >> main.out @@ -38,6 +39,7 @@ run: main echo "FAIL pie-custom-stack"; \ fi; \ +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 diff --git a/unit-tests/test-cases/pie-dylib/Makefile b/unit-tests/test-cases/pie-dylib/Makefile new file mode 100644 index 0000000..3b709dd --- /dev/null +++ b/unit-tests/test-cases/pie-dylib/Makefile @@ -0,0 +1,41 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# If pie, ignore preferred load address +# +# run a PIE four times and verify libfoo.dylib load address was different every time +# + +FOO_ADDRESS = 0x10000000 + +ifeq "x86_64" "$(ARCH)" + FOO_ADDRESS = 0x300000000 +endif + + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-dylib"; \ + else \ + echo "FAIL pie-dylib"; \ + fi; \ + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -seg1addr ${FOO_ADDRESS} + +clean: + ${RM} ${RMFLAGS} *~ main main.out libfoo.dylib + diff --git a/unit-tests/test-cases/pie-dylib/foo.c b/unit-tests/test-cases/pie-dylib/foo.c new file mode 100644 index 0000000..0c9d080 --- /dev/null +++ b/unit-tests/test-cases/pie-dylib/foo.c @@ -0,0 +1,7 @@ +#include +#include + +void foo() +{ + printf("&foo=%p\n", &foo); +} diff --git a/unit-tests/test-cases/pie-dylib/main.c b/unit-tests/test-cases/pie-dylib/main.c new file mode 100644 index 0000000..d36c9b8 --- /dev/null +++ b/unit-tests/test-cases/pie-dylib/main.c @@ -0,0 +1,10 @@ +#include +#include + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/unit-tests/test-cases/pie-text-reloc/Makefile b/unit-tests/test-cases/pie-text-reloc/Makefile new file mode 100644 index 0000000..497174e --- /dev/null +++ b/unit-tests/test-cases/pie-text-reloc/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +EXTRA_OPTIONS = -mdynamic-no-pic -read_only_relocs suppress + +ifeq "x86_64" "$(ARCH)" + EXTRA_OPTIONS = +endif + + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "FAIL pie-basic"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c -mmacosx-version-min=10.5 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/src/ImageLoaderPE.h b/unit-tests/test-cases/pie-text-reloc/main.c similarity index 89% rename from src/ImageLoaderPE.h rename to unit-tests/test-cases/pie-text-reloc/main.c index 24812d5..f1c8b85 100644 --- a/src/ImageLoaderPE.h +++ b/unit-tests/test-cases/pie-text-reloc/main.c @@ -1,5 +1,4 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * +/* * Copyright (c) 2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ @@ -21,5 +20,14 @@ * * @APPLE_LICENSE_HEADER_END@ */ +#include +#include + +static int data=0; +int main() +{ + printf("&data=%p\n", &data); + return 0; +} diff --git a/unit-tests/test-cases/prebased-performance/Makefile b/unit-tests/test-cases/prebased-performance/Makefile index b469f9f..a3edccc 100644 --- a/unit-tests/test-cases/prebased-performance/Makefile +++ b/unit-tests/test-cases/prebased-performance/Makefile @@ -35,7 +35,9 @@ endif -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/progname/Makefile b/unit-tests/test-cases/progname/Makefile index b0ee016..5caec06 100644 --- a/unit-tests/test-cases/progname/Makefile +++ b/unit-tests/test-cases/progname/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: diff --git a/unit-tests/test-cases/pthread-keys/Makefile b/unit-tests/test-cases/pthread-keys/Makefile index 4bb9ecb..a6a3242 100644 --- a/unit-tests/test-cases/pthread-keys/Makefile +++ b/unit-tests/test-cases/pthread-keys/Makefile @@ -23,9 +23,12 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: main +all-check: all check + +check: ./main +all: main main : main.c ${CC} -I${TESTROOT}/include main.c -o main diff --git a/unit-tests/test-cases/re-export-dylib/Makefile b/unit-tests/test-cases/re-export-dylib/Makefile index cb28183..da9f97a 100644 --- a/unit-tests/test-cases/re-export-dylib/Makefile +++ b/unit-tests/test-cases/re-export-dylib/Makefile @@ -30,31 +30,33 @@ PWD = `pwd` # Test that the 10.4 and 10.5 ways to re-export a dylib work # -run : all +all-check: all check + +check: ./main4 ./main5 -all : main4 main5 +all: main4 main5 libbar4.dylib : bar.c - gcc bar.c -dynamiclib -o $(PWD)/libbar4.dylib -mmacosx-version-min=10.4 + ${CC} bar.c -dynamiclib -o $(PWD)/libbar4.dylib -mmacosx-version-min=10.4 libfoo4.dylib : foo.c libbar4.dylib - gcc foo.c -dynamiclib libbar4.dylib -sub_library libbar4 -o $(PWD)/libfoo4.dylib -mmacosx-version-min=10.4 + ${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 - gcc main.c -I${TESTROOT}/include -o main4 libfoo4.dylib -mmacosx-version-min=10.4 + ${CC} main.c -I${TESTROOT}/include -o main4 libfoo4.dylib -mmacosx-version-min=10.4 libbar5.dylib : bar.c - gcc bar.c -dynamiclib -o $(PWD)/libbar5.dylib -mmacosx-version-min=10.5 + ${CC} bar.c -dynamiclib -o $(PWD)/libbar5.dylib -mmacosx-version-min=10.5 libfoo5.dylib : foo.c libbar5.dylib - gcc foo.c -dynamiclib libbar5.dylib -sub_library libbar5 -o $(PWD)/libfoo5.dylib -mmacosx-version-min=10.5 + ${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 - gcc main.c -I${TESTROOT}/include -o main5 libfoo5.dylib -mmacosx-version-min=10.5 + ${CC} main.c -I${TESTROOT}/include -o main5 libfoo5.dylib -mmacosx-version-min=10.5 clean: diff --git a/unit-tests/test-cases/re-export-framework/Makefile b/unit-tests/test-cases/re-export-framework/Makefile index b474053..7082b05 100644 --- a/unit-tests/test-cases/re-export-framework/Makefile +++ b/unit-tests/test-cases/re-export-framework/Makefile @@ -30,35 +30,37 @@ PWD = `pwd` # Test that the 10.4 and 10.5 ways to re-export a framework work # -run : all +all-check: all check + +check: ./main4 ./main5 -all : main4 main5 +all: main4 main5 Bar4.framework/Bar4 : bar.c mkdir -p Bar4.framework - gcc bar.c -dynamiclib -o $(PWD)/Bar4.framework/Bar4 -mmacosx-version-min=10.4 + ${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 - gcc foo.c -dynamiclib Bar4.framework/Bar4 -sub_umbrella Bar4 -o $(PWD)/Foo4.framework/Foo4 -mmacosx-version-min=10.4 + ${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 - gcc main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 + ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 Bar5.framework/Bar5 : bar.c mkdir -p Bar5.framework - gcc bar.c -dynamiclib -o $(PWD)/Bar5.framework/Bar5 -mmacosx-version-min=10.5 + ${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 - gcc foo.c -dynamiclib Bar5.framework/Bar5 -sub_umbrella Bar5 -o $(PWD)/Foo5.framework/Foo5 -mmacosx-version-min=10.5 + ${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 - gcc main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 + ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 diff --git a/unit-tests/test-cases/re-export-sub-framework/Makefile b/unit-tests/test-cases/re-export-sub-framework/Makefile index 1b761f5..c2f76e7 100644 --- a/unit-tests/test-cases/re-export-sub-framework/Makefile +++ b/unit-tests/test-cases/re-export-sub-framework/Makefile @@ -30,34 +30,36 @@ PWD = `pwd` # Test that the 10.4 and 10.5 ways to re-export a sub framework work # -run : all +all-check: all check + +check: ./main4 ./main5 -all : main4 main5 +all: main4 main5 Bar4.framework/Bar4 : bar.c mkdir -p Bar4.framework - gcc bar.c -dynamiclib -o $(PWD)/Bar4.framework/Bar4 -umbrella Foo4 -mmacosx-version-min=10.4 + ${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 - gcc foo.c -dynamiclib Bar4.framework/Bar4 -o $(PWD)/Foo4.framework/Foo4 -mmacosx-version-min=10.4 + ${CC} foo.c -dynamiclib Bar4.framework/Bar4 -install_name $(PWD)/Foo4.framework/Foo4 -o Foo4.framework/Foo4 -mmacosx-version-min=10.4 main4 : main.c Foo4.framework/Foo4 - gcc main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 + ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4 Bar5.framework/Bar5 : bar.c mkdir -p Bar5.framework - gcc bar.c -dynamiclib -o $(PWD)/Bar5.framework/Bar5 -umbrella Foo5 -mmacosx-version-min=10.5 + ${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 - gcc foo.c -dynamiclib Bar5.framework/Bar5 -o $(PWD)/Foo5.framework/Foo5 -mmacosx-version-min=10.5 + ${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 - gcc main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 + ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 diff --git a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile index efd6319..5364cbd 100644 --- a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile +++ b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile @@ -24,7 +24,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile index 26eb706..59cf69c 100644 --- a/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile +++ b/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile @@ -24,7 +24,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main all: main libmymalloc.dylib diff --git a/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile index c8b9346..e0e6bcb 100644 --- a/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile +++ b/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile @@ -24,19 +24,20 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: + cp /usr/lib/libSystem.B.dylib ./libSystem.B.dylib export DYLD_LIBRARY_PATH=`pwd` && ./main -all: main libSystem.B.dylib +all: main main: main.c libfoo.dylib - ${CC} ${CCFLAGS} -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib : foo.c - ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c -framework Cocoa + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c -framework CoreFoundation -libSystem.B.dylib: /usr/lib/libSystem.B.dylib - cp /usr/lib/libSystem.B.dylib ./libSystem.B.dylib clean: 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 index 322485e..a5b10db 100644 --- 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 @@ -8,8 +8,9 @@ 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 ) - PASS("read-only-import-shared-cache-override"); + 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 index cc404ed..9b3996c 100644 --- 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 @@ -1,10 +1,16 @@ #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/Makefile b/unit-tests/test-cases/read-only-stubs/Makefile index d3a63ab..7e70097 100644 --- a/unit-tests/test-cases/read-only-stubs/Makefile +++ b/unit-tests/test-cases/read-only-stubs/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/read-only-stubs/foo.c b/unit-tests/test-cases/read-only-stubs/foo.c index 01fc9a8..c5f12e7 100644 --- a/unit-tests/test-cases/read-only-stubs/foo.c +++ b/unit-tests/test-cases/read-only-stubs/foo.c @@ -73,6 +73,8 @@ static void* getStubAddr() 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__ + return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &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 3c3dca9..45f6d8f 100644 --- a/unit-tests/test-cases/read-only-stubs/main.c +++ b/unit-tests/test-cases/read-only-stubs/main.c @@ -70,6 +70,8 @@ static void* getStubAddr() return getsectdata("__TEXT", "__picsymbolstub1", &size); #elif __x86_64__ return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __arm__ + return getsectdata("__TEXT", "__symbol_stub4", &size); #else #error unknown arch #endif @@ -89,10 +91,15 @@ static void checkStubs(void* addr) int main() { void* stubAddr = getStubAddr(); +#if __i386__ + if ( stubAddr != NULL ) +#endif + { checkStubs(stubAddr); fooData = 1; foo(); checkStubs(stubAddr); + } PASS("read-only-stubs"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/restrict-environ/Makefile b/unit-tests/test-cases/restrict-environ/Makefile new file mode 100644 index 0000000..a383d1a --- /dev/null +++ b/unit-tests/test-cases/restrict-environ/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_LIBRARY_PATH=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main + +all: main + +main: main.c + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c -sectcreate __RESTRICT __restrict /dev/null + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/restrict-environ/main.c b/unit-tests/test-cases/restrict-environ/main.c new file mode 100644 index 0000000..c4c0da8 --- /dev/null +++ b/unit-tests/test-cases/restrict-environ/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never see any DYLD_ environment variables +// + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify no DYLD_ variables + const char** p; + for(p = envp; *p != NULL; p++) { + //fprintf(stderr, "%s\n", *p); + if ( strncmp(*p, "DYLD_", 5) == 0 ) { + FAIL("restrict-environ: found %s", *p); + return EXIT_SUCCESS; + } + } + // verify same as apple parameter + ++p; + if ( apple != p ) { + FAIL("restrict-environ: apple parameter not at end of envp"); + return EXIT_SUCCESS; + } + + // verify apple parameter is not NULL and ends in main + if ( *apple == NULL ) { + FAIL("restrict-environ: apple parameter is empty"); + return EXIT_SUCCESS; + } + if ( strstr(*apple, "/main") == NULL ) { + FAIL("restrict-environ: apple parameter is not path to main"); + return EXIT_SUCCESS; + } + + PASS("restrict-environ"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/restrict-executable_path/Makefile b/unit-tests/test-cases/restrict-executable_path/Makefile new file mode 100644 index 0000000..b222951 --- /dev/null +++ b/unit-tests/test-cases/restrict-executable_path/Makefile @@ -0,0 +1,71 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Use of @exectuable_path in restricted binaries is not allowed +# Use of @loader_path in restricted binaries is not allowed +# Use of relative paths in restricted binaries is not allowed +# + +all-check: all check + +check: + ${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" + + + +all: main_exe main_loader main_rel + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib -sectcreate __RESTRICT __restrict /dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_loader main_rel dir1 dir2 dir3 + diff --git a/unit-tests/test-cases/restrict-executable_path/foo.c b/unit-tests/test-cases/restrict-executable_path/foo.c new file mode 100644 index 0000000..d2b44c4 --- /dev/null +++ b/unit-tests/test-cases/restrict-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/unit-tests/test-cases/restrict-executable_path/main.c b/unit-tests/test-cases/restrict-executable_path/main.c new file mode 100644 index 0000000..de19bb6 --- /dev/null +++ b/unit-tests/test-cases/restrict-executable_path/main.c @@ -0,0 +1,14 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // issetugid +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + foo(); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile index a011834..d85c89b 100644 --- a/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile +++ b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -30,7 +30,9 @@ include ${TESTROOT}/include/common.makefile # -run: all +all-check: all check + +check: export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/bad && ./main export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/good && ./main2 diff --git a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile index 153f718..6a7c133 100644 --- a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile +++ b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile @@ -28,8 +28,9 @@ include ${TESTROOT}/include/common.makefile # a main executable run with DYLD_LIBRARY_PATH will override its rpath # +all-check: all check -run: all +check: export DYLD_LIBRARY_PATH=`pwd`/hide1 && ./main all: main hide2/libfoo.dylib hide1/libfoo.dylib diff --git a/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile new file mode 100644 index 0000000..26692af --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile @@ -0,0 +1,28 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# DYLD_ROOT_PATH should apply to LC_RPATH rpaths +# + +all-check: all check + +check: + export DYLD_ROOT_PATH=`pwd` && ${PASS_IFF} ./main + +all: main + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} foo.c -dynamiclib -o hide/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libfoo.dylib -Wl,-rpath -Wl,/hide + + +clean: + ${RM} ${RMFLAGS} *~ main hide diff --git a/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c new file mode 100644 index 0000000..c5563c5 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c @@ -0,0 +1,4 @@ +int foo() +{ + return 1; +} diff --git a/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c new file mode 100644 index 0000000..b379c36 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c @@ -0,0 +1,16 @@ + +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile index ba5400d..f235c02 100644 --- a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile +++ b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile @@ -28,8 +28,9 @@ include ${TESTROOT}/include/common.makefile # a main executable run with LD_LIBRARY_PATH can locate a dylib it links against # +all-check: all check -run: all +check: export LD_LIBRARY_PATH=`pwd`/hide/hole && ./main all: main diff --git a/unit-tests/test-cases/rpath-basic/Makefile b/unit-tests/test-cases/rpath-basic/Makefile index 536d651..c559f75 100644 --- a/unit-tests/test-cases/rpath-basic/Makefile +++ b/unit-tests/test-cases/rpath-basic/Makefile @@ -23,13 +23,15 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # a main executable linked with -rpath used to locate a dylib it links against # +all-check: all check -run: all +check: ./main all: main @@ -43,7 +45,7 @@ hide/hole/libfoo.dylib : foo.c hide/hole/libbar.dylib ${CC} foo.c hide/hole/libbar.dylib -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib main : main.c hide/hole/libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole clean: ${RM} -rf *~ main hide diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile index d94f47d..f5ebbc7 100644 --- a/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # a main executable linked with -rpath calls into a dylib which calls @@ -30,8 +31,9 @@ include ${TESTROOT}/include/common.makefile # find the dlopen path. # +all-check: all check -run: all +check: ./main all: main @@ -41,7 +43,7 @@ hide/hole/libfoo.dylib : foo.c ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib libbar.dylib : bar.c hide/hole/libfoo.dylib - ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include -Wl,-rpath -Wl,`pwd`/hide/hole + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include -Wl,-rpath -Wl,${PWD}/hide/hole main : main.c libbar.dylib ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/Makefile b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile index b679f77..5856ea7 100644 --- a/unit-tests/test-cases/rpath-dlopen-indirect/Makefile +++ b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # a main executable linked with -rpath calls into a dylib which calls @@ -30,8 +31,9 @@ include ${TESTROOT}/include/common.makefile # find the dlopen path. # +all-check: all check -run: all +check: ./main all: main @@ -44,7 +46,7 @@ libbar.dylib : bar.c hide/hole/libfoo.dylib ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include main : main.c libbar.dylib - ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,${PWD}/hide/hole clean: ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-leak/Makefile b/unit-tests/test-cases/rpath-dlopen-leak/Makefile index 2261028..0bea13a 100644 --- a/unit-tests/test-cases/rpath-dlopen-leak/Makefile +++ b/unit-tests/test-cases/rpath-dlopen-leak/Makefile @@ -37,8 +37,9 @@ ifeq "ppc" "$(ARCH)" endif endif +all-check: all check -run: all +check: if [ ${EMULATED} == 0 ]; \ then \ ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-leak" "rpath-dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null"; \ diff --git a/unit-tests/test-cases/rpath-dlopen/Makefile b/unit-tests/test-cases/rpath-dlopen/Makefile index ce34746..f08dfc9 100644 --- a/unit-tests/test-cases/rpath-dlopen/Makefile +++ b/unit-tests/test-cases/rpath-dlopen/Makefile @@ -23,13 +23,15 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # a main executable linked with -rpath and uses dlopen can find a dylib # +all-check: all check -run: all +check: ./main all: main @@ -40,7 +42,7 @@ hide/hole/libfoo.dylib : foo.c main : main.c hide/hole/libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,`pwd`/hide/hole + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,${PWD}/hide/hole clean: ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-executable_path/Makefile b/unit-tests/test-cases/rpath-executable_path/Makefile index 76dde9f..de9f672 100644 --- a/unit-tests/test-cases/rpath-executable_path/Makefile +++ b/unit-tests/test-cases/rpath-executable_path/Makefile @@ -30,8 +30,9 @@ include ${TESTROOT}/include/common.makefile # of the main executable. # +all-check: all check -run: all +check: ./main all: main diff --git a/unit-tests/test-cases/rpath-indirect-suid/Makefile b/unit-tests/test-cases/rpath-indirect-suid/Makefile index a1ec99e..fa534f2 100644 --- a/unit-tests/test-cases/rpath-indirect-suid/Makefile +++ b/unit-tests/test-cases/rpath-indirect-suid/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # a setuid main executable linked with -rpath links against a dylib @@ -30,8 +31,9 @@ include ${TESTROOT}/include/common.makefile # LC_RPATH uses @loader_path or a relative path, but ok if it is an absolute path # +all-check: all check -run: all +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 @@ -44,10 +46,10 @@ hide/hole/libbar.dylib : bar.c ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib libfoo.dylib : foo.c hide/hole/libbar.dylib - ${CC} foo.c -dynamiclib -o "`pwd`/libfoo.dylib" hide/hole/libbar.dylib + ${CC} foo.c -dynamiclib -o "${PWD}/libfoo.dylib" hide/hole/libbar.dylib main : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole sudo chown root main sudo chmod 4755 main diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile index d83c5e8..220b1ad 100644 --- a/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile @@ -30,8 +30,9 @@ include ${TESTROOT}/include/common.makefile # of the main executable during calls to dlopen() # +all-check: all check -run: all +check: ./main all: main diff --git a/unit-tests/test-cases/rpath-loader_path/Makefile b/unit-tests/test-cases/rpath-loader_path/Makefile index 6bd18a9..addee06 100644 --- a/unit-tests/test-cases/rpath-loader_path/Makefile +++ b/unit-tests/test-cases/rpath-loader_path/Makefile @@ -30,8 +30,9 @@ include ${TESTROOT}/include/common.makefile # of the binary. # +all-check: all check -run: all +check: ./main all: main diff --git a/unit-tests/test-cases/rpath-nesting/Makefile b/unit-tests/test-cases/rpath-nesting/Makefile index 0364549..14e3a99 100644 --- a/unit-tests/test-cases/rpath-nesting/Makefile +++ b/unit-tests/test-cases/rpath-nesting/Makefile @@ -23,6 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PWD = $(shell pwd) # # The main executable supplies an rpath. libfoo.dylib supplies an @@ -31,8 +32,9 @@ include ${TESTROOT}/include/common.makefile # rpath. # +all-check: all check -run: all +check: ./main all: main @@ -46,10 +48,10 @@ hide2/libbaz.dylib : baz.c ${CC} baz.c -dynamiclib -o hide2/libbaz.dylib -install_name @rpath/libbaz.dylib libfoo.dylib : foo.c hide1/libbar.dylib hide2/libbaz.dylib - ${CC} foo.c -dynamiclib -o libfoo.dylib hide1/libbar.dylib hide2/libbaz.dylib -Wl,-rpath -Wl,`pwd`/hide2 + ${CC} foo.c -dynamiclib -o libfoo.dylib hide1/libbar.dylib hide2/libbaz.dylib -Wl,-rpath -Wl,${PWD}/hide2 main : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide1 + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide1 clean: ${RM} ${RMFLAGS} *~ main hide1 hide2 libfoo.dylib diff --git a/unit-tests/test-cases/shared-cache-symlink/Makefile b/unit-tests/test-cases/shared-cache-symlink/Makefile index 92ea1b0..b8a5145 100644 --- a/unit-tests/test-cases/shared-cache-symlink/Makefile +++ b/unit-tests/test-cases/shared-cache-symlink/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./main all: main diff --git a/unit-tests/test-cases/suid-environ/Makefile b/unit-tests/test-cases/suid-environ/Makefile index 8ef3c6d..85aa954 100644 --- a/unit-tests/test-cases/suid-environ/Makefile +++ b/unit-tests/test-cases/suid-environ/Makefile @@ -23,7 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: export DYLD_INSERT_LIBRARIES=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main all: main diff --git a/unit-tests/test-cases/suid-executable_path/Makefile b/unit-tests/test-cases/suid-executable_path/Makefile index 1dd7f1e..8769082 100644 --- a/unit-tests/test-cases/suid-executable_path/Makefile +++ b/unit-tests/test-cases/suid-executable_path/Makefile @@ -32,7 +32,9 @@ SHELL = bash # use bash shell so we can redirect just stderr # Use of relative paths in setuid binaries is not allowed # -run: all +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" diff --git a/unit-tests/test-cases/sym-link-load/Makefile b/unit-tests/test-cases/sym-link-load/Makefile index 00dd9ac..a0feb3a 100644 --- a/unit-tests/test-cases/sym-link-load/Makefile +++ b/unit-tests/test-cases/sym-link-load/Makefile @@ -36,11 +36,13 @@ include ${TESTROOT}/include/common.makefile ### ### +PWD = $(shell pwd) +all-check: all check -run: all +check: ./main - export DYLD_LIBRARY_PATH="`pwd`/fake" && ./main + export DYLD_LIBRARY_PATH="${PWD}/fake" && ./main all: main real/liblink.dylib real/libtest.dylib fake/libtest.dylib @@ -51,20 +53,20 @@ main: main.c stub/libtest.dylib stub/liblink.dylib real/libbase.dylib stub/libtest.dylib: test.c mkdir -p stub - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c -DDO_NOTHING -o stub/libtest.dylib -install_name "`pwd`/real/libtest.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c -DDO_NOTHING -o stub/libtest.dylib -install_name "${PWD}/real/libtest.dylib" stub/liblink.dylib: link.c mkdir -p stub - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib link.c -o stub/liblink.dylib -install_name "`pwd`/real/liblink.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib link.c -o stub/liblink.dylib -install_name "${PWD}/real/liblink.dylib" real/libbase.dylib: base.c mkdir -p real - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib base.c -o "`pwd`/real/libbase.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib base.c -o "${PWD}/real/libbase.dylib" real/libtest.dylib: test.c real/libbase.dylib mkdir -p real - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "`pwd`/real/libtest.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/real/libtest.dylib" real/liblink.dylib: link.c mkdir -p real @@ -73,7 +75,7 @@ real/liblink.dylib: link.c fake/libtest.dylib: test.c real/libbase.dylib mkdir -p fake - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "`pwd`/fake/libtest.dylib" -install_name "`pwd`/real/libtest.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/fake/libtest.dylib" -install_name "${PWD}/real/libtest.dylib" diff --git a/unit-tests/test-cases/template/Makefile b/unit-tests/test-cases/template/Makefile index b0ee016..ec6d1cb 100644 --- a/unit-tests/test-cases/template/Makefile +++ b/unit-tests/test-cases/template/Makefile @@ -23,7 +23,10 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all + +all-check: all check + +check: ./main all: diff --git a/unit-tests/test-cases/text-relocs/Makefile b/unit-tests/test-cases/text-relocs/Makefile index 92225e9..cfadeda 100644 --- a/unit-tests/test-cases/text-relocs/Makefile +++ b/unit-tests/test-cases/text-relocs/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,13 +24,26 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile ### -### This test case is to verify __TEXT reliocations work +### This test case is to verify __TEXT reliocations work in dylibs ### ### +TEXT_RELOC_FLAGS = -mdynamic-no-pic -read_only_relocs suppress -Wl,-w -run: all +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 + +check: ./main all: main @@ -38,8 +51,8 @@ all: main main: main.c libbar.dylib ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib -libbar.dylib: foo.s bar.c - ${CC} ${CCFLAGS} -dynamiclib -o libbar.dylib bar.c foo.s -read_only_relocs suppress -Os +libbar.dylib: bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libbar.dylib bar.c -Os ${TEXT_RELOC_FLAGS} clean: ${RM} ${RMFLAGS} *~ main libbar.dylib diff --git a/unit-tests/test-cases/text-relocs/bar.c b/unit-tests/test-cases/text-relocs/bar.c index 9224e92..acf643c 100644 --- a/unit-tests/test-cases/text-relocs/bar.c +++ b/unit-tests/test-cases/text-relocs/bar.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -24,12 +24,8 @@ #include // exit(), EXIT_SUCCESS #include -extern int* foo; +static int x = 0; -bool testBar() -{ - return (*foo == 10); -} - -int bar = 10; +int getx() { return x; } +void setx(int a) { x = a; } diff --git a/unit-tests/test-cases/text-relocs/main.c b/unit-tests/test-cases/text-relocs/main.c index a702c2e..453b17b 100644 --- a/unit-tests/test-cases/text-relocs/main.c +++ b/unit-tests/test-cases/text-relocs/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,14 +26,21 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() -extern bool testBar(); +extern int getx(); +extern void setx(int a); int main(int argc, const char* argv[]) { - if ( testBar() ) - PASS("text-reloc"); - else + setx(20); + if ( getx() != 20 ) FAIL("text-reloc"); + else { + setx(99); + if ( getx() == 99 ) + PASS("text-reloc"); + else + FAIL("text-reloc"); + } return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/threaded-lazy-bind/Makefile b/unit-tests/test-cases/threaded-lazy-bind/Makefile new file mode 100644 index 0000000..7b38cee --- /dev/null +++ b/unit-tests/test-cases/threaded-lazy-bind/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/threaded-lazy-bind/foo.c b/unit-tests/test-cases/threaded-lazy-bind/foo.c new file mode 100644 index 0000000..d647bd5 --- /dev/null +++ b/unit-tests/test-cases/threaded-lazy-bind/foo.c @@ -0,0 +1,1001 @@ + +int do_000() { return 0; } +int do_001() { return 1; } +int do_002() { return 2; } +int do_003() { return 3; } +int do_004() { return 4; } +int do_005() { return 5; } +int do_006() { return 6; } +int do_007() { return 7; } +int do_008() { return 8; } +int do_009() { return 9; } +int do_010() { return 10; } +int do_011() { return 11; } +int do_012() { return 12; } +int do_013() { return 13; } +int do_014() { return 14; } +int do_015() { return 15; } +int do_016() { return 16; } +int do_017() { return 17; } +int do_018() { return 18; } +int do_019() { return 19; } +int do_020() { return 20; } +int do_021() { return 21; } +int do_022() { return 22; } +int do_023() { return 23; } +int do_024() { return 24; } +int do_025() { return 25; } +int do_026() { return 26; } +int do_027() { return 27; } +int do_028() { return 28; } +int do_029() { return 29; } +int do_030() { return 30; } +int do_031() { return 31; } +int do_032() { return 32; } +int do_033() { return 33; } +int do_034() { return 34; } +int do_035() { return 35; } +int do_036() { return 36; } +int do_037() { return 37; } +int do_038() { return 38; } +int do_039() { return 39; } +int do_040() { return 40; } +int do_041() { return 41; } +int do_042() { return 42; } +int do_043() { return 43; } +int do_044() { return 44; } +int do_045() { return 45; } +int do_046() { return 46; } +int do_047() { return 47; } +int do_048() { return 48; } +int do_049() { return 49; } +int do_050() { return 50; } +int do_051() { return 51; } +int do_052() { return 52; } +int do_053() { return 53; } +int do_054() { return 54; } +int do_055() { return 55; } +int do_056() { return 56; } +int do_057() { return 57; } +int do_058() { return 58; } +int do_059() { return 59; } +int do_060() { return 60; } +int do_061() { return 61; } +int do_062() { return 62; } +int do_063() { return 63; } +int do_064() { return 64; } +int do_065() { return 65; } +int do_066() { return 66; } +int do_067() { return 67; } +int do_068() { return 68; } +int do_069() { return 69; } +int do_070() { return 70; } +int do_071() { return 71; } +int do_072() { return 72; } +int do_073() { return 73; } +int do_074() { return 74; } +int do_075() { return 75; } +int do_076() { return 76; } +int do_077() { return 77; } +int do_078() { return 78; } +int do_079() { return 79; } +int do_080() { return 80; } +int do_081() { return 81; } +int do_082() { return 82; } +int do_083() { return 83; } +int do_084() { return 84; } +int do_085() { return 85; } +int do_086() { return 86; } +int do_087() { return 87; } +int do_088() { return 88; } +int do_089() { return 89; } +int do_090() { return 90; } +int do_091() { return 91; } +int do_092() { return 92; } +int do_093() { return 93; } +int do_094() { return 94; } +int do_095() { return 95; } +int do_096() { return 96; } +int do_097() { return 97; } +int do_098() { return 98; } +int do_099() { return 99; } +int do_100() { return 100; } +int do_101() { return 101; } +int do_102() { return 102; } +int do_103() { return 103; } +int do_104() { return 104; } +int do_105() { return 105; } +int do_106() { return 106; } +int do_107() { return 107; } +int do_108() { return 108; } +int do_109() { return 109; } +int do_110() { return 110; } +int do_111() { return 111; } +int do_112() { return 112; } +int do_113() { return 113; } +int do_114() { return 114; } +int do_115() { return 115; } +int do_116() { return 116; } +int do_117() { return 117; } +int do_118() { return 118; } +int do_119() { return 119; } +int do_120() { return 120; } +int do_121() { return 121; } +int do_122() { return 122; } +int do_123() { return 123; } +int do_124() { return 124; } +int do_125() { return 125; } +int do_126() { return 126; } +int do_127() { return 127; } +int do_128() { return 128; } +int do_129() { return 129; } +int do_130() { return 130; } +int do_131() { return 131; } +int do_132() { return 132; } +int do_133() { return 133; } +int do_134() { return 134; } +int do_135() { return 135; } +int do_136() { return 136; } +int do_137() { return 137; } +int do_138() { return 138; } +int do_139() { return 139; } +int do_140() { return 140; } +int do_141() { return 141; } +int do_142() { return 142; } +int do_143() { return 143; } +int do_144() { return 144; } +int do_145() { return 145; } +int do_146() { return 146; } +int do_147() { return 147; } +int do_148() { return 148; } +int do_149() { return 149; } +int do_150() { return 150; } +int do_151() { return 151; } +int do_152() { return 152; } +int do_153() { return 153; } +int do_154() { return 154; } +int do_155() { return 155; } +int do_156() { return 156; } +int do_157() { return 157; } +int do_158() { return 158; } +int do_159() { return 159; } +int do_160() { return 160; } +int do_161() { return 161; } +int do_162() { return 162; } +int do_163() { return 163; } +int do_164() { return 164; } +int do_165() { return 165; } +int do_166() { return 166; } +int do_167() { return 167; } +int do_168() { return 168; } +int do_169() { return 169; } +int do_170() { return 170; } +int do_171() { return 171; } +int do_172() { return 172; } +int do_173() { return 173; } +int do_174() { return 174; } +int do_175() { return 175; } +int do_176() { return 176; } +int do_177() { return 177; } +int do_178() { return 178; } +int do_179() { return 179; } +int do_180() { return 180; } +int do_181() { return 181; } +int do_182() { return 182; } +int do_183() { return 183; } +int do_184() { return 184; } +int do_185() { return 185; } +int do_186() { return 186; } +int do_187() { return 187; } +int do_188() { return 188; } +int do_189() { return 189; } +int do_190() { return 190; } +int do_191() { return 191; } +int do_192() { return 192; } +int do_193() { return 193; } +int do_194() { return 194; } +int do_195() { return 195; } +int do_196() { return 196; } +int do_197() { return 197; } +int do_198() { return 198; } +int do_199() { return 199; } +int do_200() { return 200; } +int do_201() { return 201; } +int do_202() { return 202; } +int do_203() { return 203; } +int do_204() { return 204; } +int do_205() { return 205; } +int do_206() { return 206; } +int do_207() { return 207; } +int do_208() { return 208; } +int do_209() { return 209; } +int do_210() { return 210; } +int do_211() { return 211; } +int do_212() { return 212; } +int do_213() { return 213; } +int do_214() { return 214; } +int do_215() { return 215; } +int do_216() { return 216; } +int do_217() { return 217; } +int do_218() { return 218; } +int do_219() { return 219; } +int do_220() { return 220; } +int do_221() { return 221; } +int do_222() { return 222; } +int do_223() { return 223; } +int do_224() { return 224; } +int do_225() { return 225; } +int do_226() { return 226; } +int do_227() { return 227; } +int do_228() { return 228; } +int do_229() { return 229; } +int do_230() { return 230; } +int do_231() { return 231; } +int do_232() { return 232; } +int do_233() { return 233; } +int do_234() { return 234; } +int do_235() { return 235; } +int do_236() { return 236; } +int do_237() { return 237; } +int do_238() { return 238; } +int do_239() { return 239; } +int do_240() { return 240; } +int do_241() { return 241; } +int do_242() { return 242; } +int do_243() { return 243; } +int do_244() { return 244; } +int do_245() { return 245; } +int do_246() { return 246; } +int do_247() { return 247; } +int do_248() { return 248; } +int do_249() { return 249; } +int do_250() { return 250; } +int do_251() { return 251; } +int do_252() { return 252; } +int do_253() { return 253; } +int do_254() { return 254; } +int do_255() { return 255; } +int do_256() { return 256; } +int do_257() { return 257; } +int do_258() { return 258; } +int do_259() { return 259; } +int do_260() { return 260; } +int do_261() { return 261; } +int do_262() { return 262; } +int do_263() { return 263; } +int do_264() { return 264; } +int do_265() { return 265; } +int do_266() { return 266; } +int do_267() { return 267; } +int do_268() { return 268; } +int do_269() { return 269; } +int do_270() { return 270; } +int do_271() { return 271; } +int do_272() { return 272; } +int do_273() { return 273; } +int do_274() { return 274; } +int do_275() { return 275; } +int do_276() { return 276; } +int do_277() { return 277; } +int do_278() { return 278; } +int do_279() { return 279; } +int do_280() { return 280; } +int do_281() { return 281; } +int do_282() { return 282; } +int do_283() { return 283; } +int do_284() { return 284; } +int do_285() { return 285; } +int do_286() { return 286; } +int do_287() { return 287; } +int do_288() { return 288; } +int do_289() { return 289; } +int do_290() { return 290; } +int do_291() { return 291; } +int do_292() { return 292; } +int do_293() { return 293; } +int do_294() { return 294; } +int do_295() { return 295; } +int do_296() { return 296; } +int do_297() { return 297; } +int do_298() { return 298; } +int do_299() { return 299; } +int do_300() { return 300; } +int do_301() { return 301; } +int do_302() { return 302; } +int do_303() { return 303; } +int do_304() { return 304; } +int do_305() { return 305; } +int do_306() { return 306; } +int do_307() { return 307; } +int do_308() { return 308; } +int do_309() { return 309; } +int do_310() { return 310; } +int do_311() { return 311; } +int do_312() { return 312; } +int do_313() { return 313; } +int do_314() { return 314; } +int do_315() { return 315; } +int do_316() { return 316; } +int do_317() { return 317; } +int do_318() { return 318; } +int do_319() { return 319; } +int do_320() { return 320; } +int do_321() { return 321; } +int do_322() { return 322; } +int do_323() { return 323; } +int do_324() { return 324; } +int do_325() { return 325; } +int do_326() { return 326; } +int do_327() { return 327; } +int do_328() { return 328; } +int do_329() { return 329; } +int do_330() { return 330; } +int do_331() { return 331; } +int do_332() { return 332; } +int do_333() { return 333; } +int do_334() { return 334; } +int do_335() { return 335; } +int do_336() { return 336; } +int do_337() { return 337; } +int do_338() { return 338; } +int do_339() { return 339; } +int do_340() { return 340; } +int do_341() { return 341; } +int do_342() { return 342; } +int do_343() { return 343; } +int do_344() { return 344; } +int do_345() { return 345; } +int do_346() { return 346; } +int do_347() { return 347; } +int do_348() { return 348; } +int do_349() { return 349; } +int do_350() { return 350; } +int do_351() { return 351; } +int do_352() { return 352; } +int do_353() { return 353; } +int do_354() { return 354; } +int do_355() { return 355; } +int do_356() { return 356; } +int do_357() { return 357; } +int do_358() { return 358; } +int do_359() { return 359; } +int do_360() { return 360; } +int do_361() { return 361; } +int do_362() { return 362; } +int do_363() { return 363; } +int do_364() { return 364; } +int do_365() { return 365; } +int do_366() { return 366; } +int do_367() { return 367; } +int do_368() { return 368; } +int do_369() { return 369; } +int do_370() { return 370; } +int do_371() { return 371; } +int do_372() { return 372; } +int do_373() { return 373; } +int do_374() { return 374; } +int do_375() { return 375; } +int do_376() { return 376; } +int do_377() { return 377; } +int do_378() { return 378; } +int do_379() { return 379; } +int do_380() { return 380; } +int do_381() { return 381; } +int do_382() { return 382; } +int do_383() { return 383; } +int do_384() { return 384; } +int do_385() { return 385; } +int do_386() { return 386; } +int do_387() { return 387; } +int do_388() { return 388; } +int do_389() { return 389; } +int do_390() { return 390; } +int do_391() { return 391; } +int do_392() { return 392; } +int do_393() { return 393; } +int do_394() { return 394; } +int do_395() { return 395; } +int do_396() { return 396; } +int do_397() { return 397; } +int do_398() { return 398; } +int do_399() { return 399; } +int do_400() { return 400; } +int do_401() { return 401; } +int do_402() { return 402; } +int do_403() { return 403; } +int do_404() { return 404; } +int do_405() { return 405; } +int do_406() { return 406; } +int do_407() { return 407; } +int do_408() { return 408; } +int do_409() { return 409; } +int do_410() { return 410; } +int do_411() { return 411; } +int do_412() { return 412; } +int do_413() { return 413; } +int do_414() { return 414; } +int do_415() { return 415; } +int do_416() { return 416; } +int do_417() { return 417; } +int do_418() { return 418; } +int do_419() { return 419; } +int do_420() { return 420; } +int do_421() { return 421; } +int do_422() { return 422; } +int do_423() { return 423; } +int do_424() { return 424; } +int do_425() { return 425; } +int do_426() { return 426; } +int do_427() { return 427; } +int do_428() { return 428; } +int do_429() { return 429; } +int do_430() { return 430; } +int do_431() { return 431; } +int do_432() { return 432; } +int do_433() { return 433; } +int do_434() { return 434; } +int do_435() { return 435; } +int do_436() { return 436; } +int do_437() { return 437; } +int do_438() { return 438; } +int do_439() { return 439; } +int do_440() { return 440; } +int do_441() { return 441; } +int do_442() { return 442; } +int do_443() { return 443; } +int do_444() { return 444; } +int do_445() { return 445; } +int do_446() { return 446; } +int do_447() { return 447; } +int do_448() { return 448; } +int do_449() { return 449; } +int do_450() { return 450; } +int do_451() { return 451; } +int do_452() { return 452; } +int do_453() { return 453; } +int do_454() { return 454; } +int do_455() { return 455; } +int do_456() { return 456; } +int do_457() { return 457; } +int do_458() { return 458; } +int do_459() { return 459; } +int do_460() { return 460; } +int do_461() { return 461; } +int do_462() { return 462; } +int do_463() { return 463; } +int do_464() { return 464; } +int do_465() { return 465; } +int do_466() { return 466; } +int do_467() { return 467; } +int do_468() { return 468; } +int do_469() { return 469; } +int do_470() { return 470; } +int do_471() { return 471; } +int do_472() { return 472; } +int do_473() { return 473; } +int do_474() { return 474; } +int do_475() { return 475; } +int do_476() { return 476; } +int do_477() { return 477; } +int do_478() { return 478; } +int do_479() { return 479; } +int do_480() { return 480; } +int do_481() { return 481; } +int do_482() { return 482; } +int do_483() { return 483; } +int do_484() { return 484; } +int do_485() { return 485; } +int do_486() { return 486; } +int do_487() { return 487; } +int do_488() { return 488; } +int do_489() { return 489; } +int do_490() { return 490; } +int do_491() { return 491; } +int do_492() { return 492; } +int do_493() { return 493; } +int do_494() { return 494; } +int do_495() { return 495; } +int do_496() { return 496; } +int do_497() { return 497; } +int do_498() { return 498; } +int do_499() { return 499; } +int do_500() { return 500; } +int do_501() { return 501; } +int do_502() { return 502; } +int do_503() { return 503; } +int do_504() { return 504; } +int do_505() { return 505; } +int do_506() { return 506; } +int do_507() { return 507; } +int do_508() { return 508; } +int do_509() { return 509; } +int do_510() { return 510; } +int do_511() { return 511; } +int do_512() { return 512; } +int do_513() { return 513; } +int do_514() { return 514; } +int do_515() { return 515; } +int do_516() { return 516; } +int do_517() { return 517; } +int do_518() { return 518; } +int do_519() { return 519; } +int do_520() { return 520; } +int do_521() { return 521; } +int do_522() { return 522; } +int do_523() { return 523; } +int do_524() { return 524; } +int do_525() { return 525; } +int do_526() { return 526; } +int do_527() { return 527; } +int do_528() { return 528; } +int do_529() { return 529; } +int do_530() { return 530; } +int do_531() { return 531; } +int do_532() { return 532; } +int do_533() { return 533; } +int do_534() { return 534; } +int do_535() { return 535; } +int do_536() { return 536; } +int do_537() { return 537; } +int do_538() { return 538; } +int do_539() { return 539; } +int do_540() { return 540; } +int do_541() { return 541; } +int do_542() { return 542; } +int do_543() { return 543; } +int do_544() { return 544; } +int do_545() { return 545; } +int do_546() { return 546; } +int do_547() { return 547; } +int do_548() { return 548; } +int do_549() { return 549; } +int do_550() { return 550; } +int do_551() { return 551; } +int do_552() { return 552; } +int do_553() { return 553; } +int do_554() { return 554; } +int do_555() { return 555; } +int do_556() { return 556; } +int do_557() { return 557; } +int do_558() { return 558; } +int do_559() { return 559; } +int do_560() { return 560; } +int do_561() { return 561; } +int do_562() { return 562; } +int do_563() { return 563; } +int do_564() { return 564; } +int do_565() { return 565; } +int do_566() { return 566; } +int do_567() { return 567; } +int do_568() { return 568; } +int do_569() { return 569; } +int do_570() { return 570; } +int do_571() { return 571; } +int do_572() { return 572; } +int do_573() { return 573; } +int do_574() { return 574; } +int do_575() { return 575; } +int do_576() { return 576; } +int do_577() { return 577; } +int do_578() { return 578; } +int do_579() { return 579; } +int do_580() { return 580; } +int do_581() { return 581; } +int do_582() { return 582; } +int do_583() { return 583; } +int do_584() { return 584; } +int do_585() { return 585; } +int do_586() { return 586; } +int do_587() { return 587; } +int do_588() { return 588; } +int do_589() { return 589; } +int do_590() { return 590; } +int do_591() { return 591; } +int do_592() { return 592; } +int do_593() { return 593; } +int do_594() { return 594; } +int do_595() { return 595; } +int do_596() { return 596; } +int do_597() { return 597; } +int do_598() { return 598; } +int do_599() { return 599; } +int do_600() { return 600; } +int do_601() { return 601; } +int do_602() { return 602; } +int do_603() { return 603; } +int do_604() { return 604; } +int do_605() { return 605; } +int do_606() { return 606; } +int do_607() { return 607; } +int do_608() { return 608; } +int do_609() { return 609; } +int do_610() { return 610; } +int do_611() { return 611; } +int do_612() { return 612; } +int do_613() { return 613; } +int do_614() { return 614; } +int do_615() { return 615; } +int do_616() { return 616; } +int do_617() { return 617; } +int do_618() { return 618; } +int do_619() { return 619; } +int do_620() { return 620; } +int do_621() { return 621; } +int do_622() { return 622; } +int do_623() { return 623; } +int do_624() { return 624; } +int do_625() { return 625; } +int do_626() { return 626; } +int do_627() { return 627; } +int do_628() { return 628; } +int do_629() { return 629; } +int do_630() { return 630; } +int do_631() { return 631; } +int do_632() { return 632; } +int do_633() { return 633; } +int do_634() { return 634; } +int do_635() { return 635; } +int do_636() { return 636; } +int do_637() { return 637; } +int do_638() { return 638; } +int do_639() { return 639; } +int do_640() { return 640; } +int do_641() { return 641; } +int do_642() { return 642; } +int do_643() { return 643; } +int do_644() { return 644; } +int do_645() { return 645; } +int do_646() { return 646; } +int do_647() { return 647; } +int do_648() { return 648; } +int do_649() { return 649; } +int do_650() { return 650; } +int do_651() { return 651; } +int do_652() { return 652; } +int do_653() { return 653; } +int do_654() { return 654; } +int do_655() { return 655; } +int do_656() { return 656; } +int do_657() { return 657; } +int do_658() { return 658; } +int do_659() { return 659; } +int do_660() { return 660; } +int do_661() { return 661; } +int do_662() { return 662; } +int do_663() { return 663; } +int do_664() { return 664; } +int do_665() { return 665; } +int do_666() { return 666; } +int do_667() { return 667; } +int do_668() { return 668; } +int do_669() { return 669; } +int do_670() { return 670; } +int do_671() { return 671; } +int do_672() { return 672; } +int do_673() { return 673; } +int do_674() { return 674; } +int do_675() { return 675; } +int do_676() { return 676; } +int do_677() { return 677; } +int do_678() { return 678; } +int do_679() { return 679; } +int do_680() { return 680; } +int do_681() { return 681; } +int do_682() { return 682; } +int do_683() { return 683; } +int do_684() { return 684; } +int do_685() { return 685; } +int do_686() { return 686; } +int do_687() { return 687; } +int do_688() { return 688; } +int do_689() { return 689; } +int do_690() { return 690; } +int do_691() { return 691; } +int do_692() { return 692; } +int do_693() { return 693; } +int do_694() { return 694; } +int do_695() { return 695; } +int do_696() { return 696; } +int do_697() { return 697; } +int do_698() { return 698; } +int do_699() { return 699; } +int do_700() { return 700; } +int do_701() { return 701; } +int do_702() { return 702; } +int do_703() { return 703; } +int do_704() { return 704; } +int do_705() { return 705; } +int do_706() { return 706; } +int do_707() { return 707; } +int do_708() { return 708; } +int do_709() { return 709; } +int do_710() { return 710; } +int do_711() { return 711; } +int do_712() { return 712; } +int do_713() { return 713; } +int do_714() { return 714; } +int do_715() { return 715; } +int do_716() { return 716; } +int do_717() { return 717; } +int do_718() { return 718; } +int do_719() { return 719; } +int do_720() { return 720; } +int do_721() { return 721; } +int do_722() { return 722; } +int do_723() { return 723; } +int do_724() { return 724; } +int do_725() { return 725; } +int do_726() { return 726; } +int do_727() { return 727; } +int do_728() { return 728; } +int do_729() { return 729; } +int do_730() { return 730; } +int do_731() { return 731; } +int do_732() { return 732; } +int do_733() { return 733; } +int do_734() { return 734; } +int do_735() { return 735; } +int do_736() { return 736; } +int do_737() { return 737; } +int do_738() { return 738; } +int do_739() { return 739; } +int do_740() { return 740; } +int do_741() { return 741; } +int do_742() { return 742; } +int do_743() { return 743; } +int do_744() { return 744; } +int do_745() { return 745; } +int do_746() { return 746; } +int do_747() { return 747; } +int do_748() { return 748; } +int do_749() { return 749; } +int do_750() { return 750; } +int do_751() { return 751; } +int do_752() { return 752; } +int do_753() { return 753; } +int do_754() { return 754; } +int do_755() { return 755; } +int do_756() { return 756; } +int do_757() { return 757; } +int do_758() { return 758; } +int do_759() { return 759; } +int do_760() { return 760; } +int do_761() { return 761; } +int do_762() { return 762; } +int do_763() { return 763; } +int do_764() { return 764; } +int do_765() { return 765; } +int do_766() { return 766; } +int do_767() { return 767; } +int do_768() { return 768; } +int do_769() { return 769; } +int do_770() { return 770; } +int do_771() { return 771; } +int do_772() { return 772; } +int do_773() { return 773; } +int do_774() { return 774; } +int do_775() { return 775; } +int do_776() { return 776; } +int do_777() { return 777; } +int do_778() { return 778; } +int do_779() { return 779; } +int do_780() { return 780; } +int do_781() { return 781; } +int do_782() { return 782; } +int do_783() { return 783; } +int do_784() { return 784; } +int do_785() { return 785; } +int do_786() { return 786; } +int do_787() { return 787; } +int do_788() { return 788; } +int do_789() { return 789; } +int do_790() { return 790; } +int do_791() { return 791; } +int do_792() { return 792; } +int do_793() { return 793; } +int do_794() { return 794; } +int do_795() { return 795; } +int do_796() { return 796; } +int do_797() { return 797; } +int do_798() { return 798; } +int do_799() { return 799; } +int do_800() { return 800; } +int do_801() { return 801; } +int do_802() { return 802; } +int do_803() { return 803; } +int do_804() { return 804; } +int do_805() { return 805; } +int do_806() { return 806; } +int do_807() { return 807; } +int do_808() { return 808; } +int do_809() { return 809; } +int do_810() { return 810; } +int do_811() { return 811; } +int do_812() { return 812; } +int do_813() { return 813; } +int do_814() { return 814; } +int do_815() { return 815; } +int do_816() { return 816; } +int do_817() { return 817; } +int do_818() { return 818; } +int do_819() { return 819; } +int do_820() { return 820; } +int do_821() { return 821; } +int do_822() { return 822; } +int do_823() { return 823; } +int do_824() { return 824; } +int do_825() { return 825; } +int do_826() { return 826; } +int do_827() { return 827; } +int do_828() { return 828; } +int do_829() { return 829; } +int do_830() { return 830; } +int do_831() { return 831; } +int do_832() { return 832; } +int do_833() { return 833; } +int do_834() { return 834; } +int do_835() { return 835; } +int do_836() { return 836; } +int do_837() { return 837; } +int do_838() { return 838; } +int do_839() { return 839; } +int do_840() { return 840; } +int do_841() { return 841; } +int do_842() { return 842; } +int do_843() { return 843; } +int do_844() { return 844; } +int do_845() { return 845; } +int do_846() { return 846; } +int do_847() { return 847; } +int do_848() { return 848; } +int do_849() { return 849; } +int do_850() { return 850; } +int do_851() { return 851; } +int do_852() { return 852; } +int do_853() { return 853; } +int do_854() { return 854; } +int do_855() { return 855; } +int do_856() { return 856; } +int do_857() { return 857; } +int do_858() { return 858; } +int do_859() { return 859; } +int do_860() { return 860; } +int do_861() { return 861; } +int do_862() { return 862; } +int do_863() { return 863; } +int do_864() { return 864; } +int do_865() { return 865; } +int do_866() { return 866; } +int do_867() { return 867; } +int do_868() { return 868; } +int do_869() { return 869; } +int do_870() { return 870; } +int do_871() { return 871; } +int do_872() { return 872; } +int do_873() { return 873; } +int do_874() { return 874; } +int do_875() { return 875; } +int do_876() { return 876; } +int do_877() { return 877; } +int do_878() { return 878; } +int do_879() { return 879; } +int do_880() { return 880; } +int do_881() { return 881; } +int do_882() { return 882; } +int do_883() { return 883; } +int do_884() { return 884; } +int do_885() { return 885; } +int do_886() { return 886; } +int do_887() { return 887; } +int do_888() { return 888; } +int do_889() { return 889; } +int do_890() { return 890; } +int do_891() { return 891; } +int do_892() { return 892; } +int do_893() { return 893; } +int do_894() { return 894; } +int do_895() { return 895; } +int do_896() { return 896; } +int do_897() { return 897; } +int do_898() { return 898; } +int do_899() { return 899; } +int do_900() { return 900; } +int do_901() { return 901; } +int do_902() { return 902; } +int do_903() { return 903; } +int do_904() { return 904; } +int do_905() { return 905; } +int do_906() { return 906; } +int do_907() { return 907; } +int do_908() { return 908; } +int do_909() { return 909; } +int do_910() { return 910; } +int do_911() { return 911; } +int do_912() { return 912; } +int do_913() { return 913; } +int do_914() { return 914; } +int do_915() { return 915; } +int do_916() { return 916; } +int do_917() { return 917; } +int do_918() { return 918; } +int do_919() { return 919; } +int do_920() { return 920; } +int do_921() { return 921; } +int do_922() { return 922; } +int do_923() { return 923; } +int do_924() { return 924; } +int do_925() { return 925; } +int do_926() { return 926; } +int do_927() { return 927; } +int do_928() { return 928; } +int do_929() { return 929; } +int do_930() { return 930; } +int do_931() { return 931; } +int do_932() { return 932; } +int do_933() { return 933; } +int do_934() { return 934; } +int do_935() { return 935; } +int do_936() { return 936; } +int do_937() { return 937; } +int do_938() { return 938; } +int do_939() { return 939; } +int do_940() { return 940; } +int do_941() { return 941; } +int do_942() { return 942; } +int do_943() { return 943; } +int do_944() { return 944; } +int do_945() { return 945; } +int do_946() { return 946; } +int do_947() { return 947; } +int do_948() { return 948; } +int do_949() { return 949; } +int do_950() { return 950; } +int do_951() { return 951; } +int do_952() { return 952; } +int do_953() { return 953; } +int do_954() { return 954; } +int do_955() { return 955; } +int do_956() { return 956; } +int do_957() { return 957; } +int do_958() { return 958; } +int do_959() { return 959; } +int do_960() { return 960; } +int do_961() { return 961; } +int do_962() { return 962; } +int do_963() { return 963; } +int do_964() { return 964; } +int do_965() { return 965; } +int do_966() { return 966; } +int do_967() { return 967; } +int do_968() { return 968; } +int do_969() { return 969; } +int do_970() { return 970; } +int do_971() { return 971; } +int do_972() { return 972; } +int do_973() { return 973; } +int do_974() { return 974; } +int do_975() { return 975; } +int do_976() { return 976; } +int do_977() { return 977; } +int do_978() { return 978; } +int do_979() { return 979; } +int do_980() { return 980; } +int do_981() { return 981; } +int do_982() { return 982; } +int do_983() { return 983; } +int do_984() { return 984; } +int do_985() { return 985; } +int do_986() { return 986; } +int do_987() { return 987; } +int do_988() { return 988; } +int do_989() { return 989; } +int do_990() { return 990; } +int do_991() { return 991; } +int do_992() { return 992; } +int do_993() { return 993; } +int do_994() { return 994; } +int do_995() { return 995; } +int do_996() { return 996; } +int do_997() { return 997; } +int do_998() { return 998; } +int do_999() { return 999; } diff --git a/unit-tests/test-cases/threaded-lazy-bind/gen.c b/unit-tests/test-cases/threaded-lazy-bind/gen.c new file mode 100644 index 0000000..03bd744 --- /dev/null +++ b/unit-tests/test-cases/threaded-lazy-bind/gen.c @@ -0,0 +1,19 @@ + +#include + +int main() +{ + int i; + for (i = 0; i < 1000; ++i) { + printf("int do_%03d() { return %d; }\n", i, i); + } + + for (i = 0; i < 1000; ++i) { + printf("extern int do_%03d();\n", i); + } + for (i = 0; i < 1000; ++i) { + printf("if ( do_%03d() != %d ) { FAIL(\"iteration %d\"); exit(0); }\n", i, i, i); + } + + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/threaded-lazy-bind/main.c b/unit-tests/test-cases/threaded-lazy-bind/main.c new file mode 100644 index 0000000..ebb0e0d --- /dev/null +++ b/unit-tests/test-cases/threaded-lazy-bind/main.c @@ -0,0 +1,2060 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +/// +/// Test that lazy binding is thread safe +/// + +extern int do_000(); +extern int do_001(); +extern int do_002(); +extern int do_003(); +extern int do_004(); +extern int do_005(); +extern int do_006(); +extern int do_007(); +extern int do_008(); +extern int do_009(); +extern int do_010(); +extern int do_011(); +extern int do_012(); +extern int do_013(); +extern int do_014(); +extern int do_015(); +extern int do_016(); +extern int do_017(); +extern int do_018(); +extern int do_019(); +extern int do_020(); +extern int do_021(); +extern int do_022(); +extern int do_023(); +extern int do_024(); +extern int do_025(); +extern int do_026(); +extern int do_027(); +extern int do_028(); +extern int do_029(); +extern int do_030(); +extern int do_031(); +extern int do_032(); +extern int do_033(); +extern int do_034(); +extern int do_035(); +extern int do_036(); +extern int do_037(); +extern int do_038(); +extern int do_039(); +extern int do_040(); +extern int do_041(); +extern int do_042(); +extern int do_043(); +extern int do_044(); +extern int do_045(); +extern int do_046(); +extern int do_047(); +extern int do_048(); +extern int do_049(); +extern int do_050(); +extern int do_051(); +extern int do_052(); +extern int do_053(); +extern int do_054(); +extern int do_055(); +extern int do_056(); +extern int do_057(); +extern int do_058(); +extern int do_059(); +extern int do_060(); +extern int do_061(); +extern int do_062(); +extern int do_063(); +extern int do_064(); +extern int do_065(); +extern int do_066(); +extern int do_067(); +extern int do_068(); +extern int do_069(); +extern int do_070(); +extern int do_071(); +extern int do_072(); +extern int do_073(); +extern int do_074(); +extern int do_075(); +extern int do_076(); +extern int do_077(); +extern int do_078(); +extern int do_079(); +extern int do_080(); +extern int do_081(); +extern int do_082(); +extern int do_083(); +extern int do_084(); +extern int do_085(); +extern int do_086(); +extern int do_087(); +extern int do_088(); +extern int do_089(); +extern int do_090(); +extern int do_091(); +extern int do_092(); +extern int do_093(); +extern int do_094(); +extern int do_095(); +extern int do_096(); +extern int do_097(); +extern int do_098(); +extern int do_099(); +extern int do_100(); +extern int do_101(); +extern int do_102(); +extern int do_103(); +extern int do_104(); +extern int do_105(); +extern int do_106(); +extern int do_107(); +extern int do_108(); +extern int do_109(); +extern int do_110(); +extern int do_111(); +extern int do_112(); +extern int do_113(); +extern int do_114(); +extern int do_115(); +extern int do_116(); +extern int do_117(); +extern int do_118(); +extern int do_119(); +extern int do_120(); +extern int do_121(); +extern int do_122(); +extern int do_123(); +extern int do_124(); +extern int do_125(); +extern int do_126(); +extern int do_127(); +extern int do_128(); +extern int do_129(); +extern int do_130(); +extern int do_131(); +extern int do_132(); +extern int do_133(); +extern int do_134(); +extern int do_135(); +extern int do_136(); +extern int do_137(); +extern int do_138(); +extern int do_139(); +extern int do_140(); +extern int do_141(); +extern int do_142(); +extern int do_143(); +extern int do_144(); +extern int do_145(); +extern int do_146(); +extern int do_147(); +extern int do_148(); +extern int do_149(); +extern int do_150(); +extern int do_151(); +extern int do_152(); +extern int do_153(); +extern int do_154(); +extern int do_155(); +extern int do_156(); +extern int do_157(); +extern int do_158(); +extern int do_159(); +extern int do_160(); +extern int do_161(); +extern int do_162(); +extern int do_163(); +extern int do_164(); +extern int do_165(); +extern int do_166(); +extern int do_167(); +extern int do_168(); +extern int do_169(); +extern int do_170(); +extern int do_171(); +extern int do_172(); +extern int do_173(); +extern int do_174(); +extern int do_175(); +extern int do_176(); +extern int do_177(); +extern int do_178(); +extern int do_179(); +extern int do_180(); +extern int do_181(); +extern int do_182(); +extern int do_183(); +extern int do_184(); +extern int do_185(); +extern int do_186(); +extern int do_187(); +extern int do_188(); +extern int do_189(); +extern int do_190(); +extern int do_191(); +extern int do_192(); +extern int do_193(); +extern int do_194(); +extern int do_195(); +extern int do_196(); +extern int do_197(); +extern int do_198(); +extern int do_199(); +extern int do_200(); +extern int do_201(); +extern int do_202(); +extern int do_203(); +extern int do_204(); +extern int do_205(); +extern int do_206(); +extern int do_207(); +extern int do_208(); +extern int do_209(); +extern int do_210(); +extern int do_211(); +extern int do_212(); +extern int do_213(); +extern int do_214(); +extern int do_215(); +extern int do_216(); +extern int do_217(); +extern int do_218(); +extern int do_219(); +extern int do_220(); +extern int do_221(); +extern int do_222(); +extern int do_223(); +extern int do_224(); +extern int do_225(); +extern int do_226(); +extern int do_227(); +extern int do_228(); +extern int do_229(); +extern int do_230(); +extern int do_231(); +extern int do_232(); +extern int do_233(); +extern int do_234(); +extern int do_235(); +extern int do_236(); +extern int do_237(); +extern int do_238(); +extern int do_239(); +extern int do_240(); +extern int do_241(); +extern int do_242(); +extern int do_243(); +extern int do_244(); +extern int do_245(); +extern int do_246(); +extern int do_247(); +extern int do_248(); +extern int do_249(); +extern int do_250(); +extern int do_251(); +extern int do_252(); +extern int do_253(); +extern int do_254(); +extern int do_255(); +extern int do_256(); +extern int do_257(); +extern int do_258(); +extern int do_259(); +extern int do_260(); +extern int do_261(); +extern int do_262(); +extern int do_263(); +extern int do_264(); +extern int do_265(); +extern int do_266(); +extern int do_267(); +extern int do_268(); +extern int do_269(); +extern int do_270(); +extern int do_271(); +extern int do_272(); +extern int do_273(); +extern int do_274(); +extern int do_275(); +extern int do_276(); +extern int do_277(); +extern int do_278(); +extern int do_279(); +extern int do_280(); +extern int do_281(); +extern int do_282(); +extern int do_283(); +extern int do_284(); +extern int do_285(); +extern int do_286(); +extern int do_287(); +extern int do_288(); +extern int do_289(); +extern int do_290(); +extern int do_291(); +extern int do_292(); +extern int do_293(); +extern int do_294(); +extern int do_295(); +extern int do_296(); +extern int do_297(); +extern int do_298(); +extern int do_299(); +extern int do_300(); +extern int do_301(); +extern int do_302(); +extern int do_303(); +extern int do_304(); +extern int do_305(); +extern int do_306(); +extern int do_307(); +extern int do_308(); +extern int do_309(); +extern int do_310(); +extern int do_311(); +extern int do_312(); +extern int do_313(); +extern int do_314(); +extern int do_315(); +extern int do_316(); +extern int do_317(); +extern int do_318(); +extern int do_319(); +extern int do_320(); +extern int do_321(); +extern int do_322(); +extern int do_323(); +extern int do_324(); +extern int do_325(); +extern int do_326(); +extern int do_327(); +extern int do_328(); +extern int do_329(); +extern int do_330(); +extern int do_331(); +extern int do_332(); +extern int do_333(); +extern int do_334(); +extern int do_335(); +extern int do_336(); +extern int do_337(); +extern int do_338(); +extern int do_339(); +extern int do_340(); +extern int do_341(); +extern int do_342(); +extern int do_343(); +extern int do_344(); +extern int do_345(); +extern int do_346(); +extern int do_347(); +extern int do_348(); +extern int do_349(); +extern int do_350(); +extern int do_351(); +extern int do_352(); +extern int do_353(); +extern int do_354(); +extern int do_355(); +extern int do_356(); +extern int do_357(); +extern int do_358(); +extern int do_359(); +extern int do_360(); +extern int do_361(); +extern int do_362(); +extern int do_363(); +extern int do_364(); +extern int do_365(); +extern int do_366(); +extern int do_367(); +extern int do_368(); +extern int do_369(); +extern int do_370(); +extern int do_371(); +extern int do_372(); +extern int do_373(); +extern int do_374(); +extern int do_375(); +extern int do_376(); +extern int do_377(); +extern int do_378(); +extern int do_379(); +extern int do_380(); +extern int do_381(); +extern int do_382(); +extern int do_383(); +extern int do_384(); +extern int do_385(); +extern int do_386(); +extern int do_387(); +extern int do_388(); +extern int do_389(); +extern int do_390(); +extern int do_391(); +extern int do_392(); +extern int do_393(); +extern int do_394(); +extern int do_395(); +extern int do_396(); +extern int do_397(); +extern int do_398(); +extern int do_399(); +extern int do_400(); +extern int do_401(); +extern int do_402(); +extern int do_403(); +extern int do_404(); +extern int do_405(); +extern int do_406(); +extern int do_407(); +extern int do_408(); +extern int do_409(); +extern int do_410(); +extern int do_411(); +extern int do_412(); +extern int do_413(); +extern int do_414(); +extern int do_415(); +extern int do_416(); +extern int do_417(); +extern int do_418(); +extern int do_419(); +extern int do_420(); +extern int do_421(); +extern int do_422(); +extern int do_423(); +extern int do_424(); +extern int do_425(); +extern int do_426(); +extern int do_427(); +extern int do_428(); +extern int do_429(); +extern int do_430(); +extern int do_431(); +extern int do_432(); +extern int do_433(); +extern int do_434(); +extern int do_435(); +extern int do_436(); +extern int do_437(); +extern int do_438(); +extern int do_439(); +extern int do_440(); +extern int do_441(); +extern int do_442(); +extern int do_443(); +extern int do_444(); +extern int do_445(); +extern int do_446(); +extern int do_447(); +extern int do_448(); +extern int do_449(); +extern int do_450(); +extern int do_451(); +extern int do_452(); +extern int do_453(); +extern int do_454(); +extern int do_455(); +extern int do_456(); +extern int do_457(); +extern int do_458(); +extern int do_459(); +extern int do_460(); +extern int do_461(); +extern int do_462(); +extern int do_463(); +extern int do_464(); +extern int do_465(); +extern int do_466(); +extern int do_467(); +extern int do_468(); +extern int do_469(); +extern int do_470(); +extern int do_471(); +extern int do_472(); +extern int do_473(); +extern int do_474(); +extern int do_475(); +extern int do_476(); +extern int do_477(); +extern int do_478(); +extern int do_479(); +extern int do_480(); +extern int do_481(); +extern int do_482(); +extern int do_483(); +extern int do_484(); +extern int do_485(); +extern int do_486(); +extern int do_487(); +extern int do_488(); +extern int do_489(); +extern int do_490(); +extern int do_491(); +extern int do_492(); +extern int do_493(); +extern int do_494(); +extern int do_495(); +extern int do_496(); +extern int do_497(); +extern int do_498(); +extern int do_499(); +extern int do_500(); +extern int do_501(); +extern int do_502(); +extern int do_503(); +extern int do_504(); +extern int do_505(); +extern int do_506(); +extern int do_507(); +extern int do_508(); +extern int do_509(); +extern int do_510(); +extern int do_511(); +extern int do_512(); +extern int do_513(); +extern int do_514(); +extern int do_515(); +extern int do_516(); +extern int do_517(); +extern int do_518(); +extern int do_519(); +extern int do_520(); +extern int do_521(); +extern int do_522(); +extern int do_523(); +extern int do_524(); +extern int do_525(); +extern int do_526(); +extern int do_527(); +extern int do_528(); +extern int do_529(); +extern int do_530(); +extern int do_531(); +extern int do_532(); +extern int do_533(); +extern int do_534(); +extern int do_535(); +extern int do_536(); +extern int do_537(); +extern int do_538(); +extern int do_539(); +extern int do_540(); +extern int do_541(); +extern int do_542(); +extern int do_543(); +extern int do_544(); +extern int do_545(); +extern int do_546(); +extern int do_547(); +extern int do_548(); +extern int do_549(); +extern int do_550(); +extern int do_551(); +extern int do_552(); +extern int do_553(); +extern int do_554(); +extern int do_555(); +extern int do_556(); +extern int do_557(); +extern int do_558(); +extern int do_559(); +extern int do_560(); +extern int do_561(); +extern int do_562(); +extern int do_563(); +extern int do_564(); +extern int do_565(); +extern int do_566(); +extern int do_567(); +extern int do_568(); +extern int do_569(); +extern int do_570(); +extern int do_571(); +extern int do_572(); +extern int do_573(); +extern int do_574(); +extern int do_575(); +extern int do_576(); +extern int do_577(); +extern int do_578(); +extern int do_579(); +extern int do_580(); +extern int do_581(); +extern int do_582(); +extern int do_583(); +extern int do_584(); +extern int do_585(); +extern int do_586(); +extern int do_587(); +extern int do_588(); +extern int do_589(); +extern int do_590(); +extern int do_591(); +extern int do_592(); +extern int do_593(); +extern int do_594(); +extern int do_595(); +extern int do_596(); +extern int do_597(); +extern int do_598(); +extern int do_599(); +extern int do_600(); +extern int do_601(); +extern int do_602(); +extern int do_603(); +extern int do_604(); +extern int do_605(); +extern int do_606(); +extern int do_607(); +extern int do_608(); +extern int do_609(); +extern int do_610(); +extern int do_611(); +extern int do_612(); +extern int do_613(); +extern int do_614(); +extern int do_615(); +extern int do_616(); +extern int do_617(); +extern int do_618(); +extern int do_619(); +extern int do_620(); +extern int do_621(); +extern int do_622(); +extern int do_623(); +extern int do_624(); +extern int do_625(); +extern int do_626(); +extern int do_627(); +extern int do_628(); +extern int do_629(); +extern int do_630(); +extern int do_631(); +extern int do_632(); +extern int do_633(); +extern int do_634(); +extern int do_635(); +extern int do_636(); +extern int do_637(); +extern int do_638(); +extern int do_639(); +extern int do_640(); +extern int do_641(); +extern int do_642(); +extern int do_643(); +extern int do_644(); +extern int do_645(); +extern int do_646(); +extern int do_647(); +extern int do_648(); +extern int do_649(); +extern int do_650(); +extern int do_651(); +extern int do_652(); +extern int do_653(); +extern int do_654(); +extern int do_655(); +extern int do_656(); +extern int do_657(); +extern int do_658(); +extern int do_659(); +extern int do_660(); +extern int do_661(); +extern int do_662(); +extern int do_663(); +extern int do_664(); +extern int do_665(); +extern int do_666(); +extern int do_667(); +extern int do_668(); +extern int do_669(); +extern int do_670(); +extern int do_671(); +extern int do_672(); +extern int do_673(); +extern int do_674(); +extern int do_675(); +extern int do_676(); +extern int do_677(); +extern int do_678(); +extern int do_679(); +extern int do_680(); +extern int do_681(); +extern int do_682(); +extern int do_683(); +extern int do_684(); +extern int do_685(); +extern int do_686(); +extern int do_687(); +extern int do_688(); +extern int do_689(); +extern int do_690(); +extern int do_691(); +extern int do_692(); +extern int do_693(); +extern int do_694(); +extern int do_695(); +extern int do_696(); +extern int do_697(); +extern int do_698(); +extern int do_699(); +extern int do_700(); +extern int do_701(); +extern int do_702(); +extern int do_703(); +extern int do_704(); +extern int do_705(); +extern int do_706(); +extern int do_707(); +extern int do_708(); +extern int do_709(); +extern int do_710(); +extern int do_711(); +extern int do_712(); +extern int do_713(); +extern int do_714(); +extern int do_715(); +extern int do_716(); +extern int do_717(); +extern int do_718(); +extern int do_719(); +extern int do_720(); +extern int do_721(); +extern int do_722(); +extern int do_723(); +extern int do_724(); +extern int do_725(); +extern int do_726(); +extern int do_727(); +extern int do_728(); +extern int do_729(); +extern int do_730(); +extern int do_731(); +extern int do_732(); +extern int do_733(); +extern int do_734(); +extern int do_735(); +extern int do_736(); +extern int do_737(); +extern int do_738(); +extern int do_739(); +extern int do_740(); +extern int do_741(); +extern int do_742(); +extern int do_743(); +extern int do_744(); +extern int do_745(); +extern int do_746(); +extern int do_747(); +extern int do_748(); +extern int do_749(); +extern int do_750(); +extern int do_751(); +extern int do_752(); +extern int do_753(); +extern int do_754(); +extern int do_755(); +extern int do_756(); +extern int do_757(); +extern int do_758(); +extern int do_759(); +extern int do_760(); +extern int do_761(); +extern int do_762(); +extern int do_763(); +extern int do_764(); +extern int do_765(); +extern int do_766(); +extern int do_767(); +extern int do_768(); +extern int do_769(); +extern int do_770(); +extern int do_771(); +extern int do_772(); +extern int do_773(); +extern int do_774(); +extern int do_775(); +extern int do_776(); +extern int do_777(); +extern int do_778(); +extern int do_779(); +extern int do_780(); +extern int do_781(); +extern int do_782(); +extern int do_783(); +extern int do_784(); +extern int do_785(); +extern int do_786(); +extern int do_787(); +extern int do_788(); +extern int do_789(); +extern int do_790(); +extern int do_791(); +extern int do_792(); +extern int do_793(); +extern int do_794(); +extern int do_795(); +extern int do_796(); +extern int do_797(); +extern int do_798(); +extern int do_799(); +extern int do_800(); +extern int do_801(); +extern int do_802(); +extern int do_803(); +extern int do_804(); +extern int do_805(); +extern int do_806(); +extern int do_807(); +extern int do_808(); +extern int do_809(); +extern int do_810(); +extern int do_811(); +extern int do_812(); +extern int do_813(); +extern int do_814(); +extern int do_815(); +extern int do_816(); +extern int do_817(); +extern int do_818(); +extern int do_819(); +extern int do_820(); +extern int do_821(); +extern int do_822(); +extern int do_823(); +extern int do_824(); +extern int do_825(); +extern int do_826(); +extern int do_827(); +extern int do_828(); +extern int do_829(); +extern int do_830(); +extern int do_831(); +extern int do_832(); +extern int do_833(); +extern int do_834(); +extern int do_835(); +extern int do_836(); +extern int do_837(); +extern int do_838(); +extern int do_839(); +extern int do_840(); +extern int do_841(); +extern int do_842(); +extern int do_843(); +extern int do_844(); +extern int do_845(); +extern int do_846(); +extern int do_847(); +extern int do_848(); +extern int do_849(); +extern int do_850(); +extern int do_851(); +extern int do_852(); +extern int do_853(); +extern int do_854(); +extern int do_855(); +extern int do_856(); +extern int do_857(); +extern int do_858(); +extern int do_859(); +extern int do_860(); +extern int do_861(); +extern int do_862(); +extern int do_863(); +extern int do_864(); +extern int do_865(); +extern int do_866(); +extern int do_867(); +extern int do_868(); +extern int do_869(); +extern int do_870(); +extern int do_871(); +extern int do_872(); +extern int do_873(); +extern int do_874(); +extern int do_875(); +extern int do_876(); +extern int do_877(); +extern int do_878(); +extern int do_879(); +extern int do_880(); +extern int do_881(); +extern int do_882(); +extern int do_883(); +extern int do_884(); +extern int do_885(); +extern int do_886(); +extern int do_887(); +extern int do_888(); +extern int do_889(); +extern int do_890(); +extern int do_891(); +extern int do_892(); +extern int do_893(); +extern int do_894(); +extern int do_895(); +extern int do_896(); +extern int do_897(); +extern int do_898(); +extern int do_899(); +extern int do_900(); +extern int do_901(); +extern int do_902(); +extern int do_903(); +extern int do_904(); +extern int do_905(); +extern int do_906(); +extern int do_907(); +extern int do_908(); +extern int do_909(); +extern int do_910(); +extern int do_911(); +extern int do_912(); +extern int do_913(); +extern int do_914(); +extern int do_915(); +extern int do_916(); +extern int do_917(); +extern int do_918(); +extern int do_919(); +extern int do_920(); +extern int do_921(); +extern int do_922(); +extern int do_923(); +extern int do_924(); +extern int do_925(); +extern int do_926(); +extern int do_927(); +extern int do_928(); +extern int do_929(); +extern int do_930(); +extern int do_931(); +extern int do_932(); +extern int do_933(); +extern int do_934(); +extern int do_935(); +extern int do_936(); +extern int do_937(); +extern int do_938(); +extern int do_939(); +extern int do_940(); +extern int do_941(); +extern int do_942(); +extern int do_943(); +extern int do_944(); +extern int do_945(); +extern int do_946(); +extern int do_947(); +extern int do_948(); +extern int do_949(); +extern int do_950(); +extern int do_951(); +extern int do_952(); +extern int do_953(); +extern int do_954(); +extern int do_955(); +extern int do_956(); +extern int do_957(); +extern int do_958(); +extern int do_959(); +extern int do_960(); +extern int do_961(); +extern int do_962(); +extern int do_963(); +extern int do_964(); +extern int do_965(); +extern int do_966(); +extern int do_967(); +extern int do_968(); +extern int do_969(); +extern int do_970(); +extern int do_971(); +extern int do_972(); +extern int do_973(); +extern int do_974(); +extern int do_975(); +extern int do_976(); +extern int do_977(); +extern int do_978(); +extern int do_979(); +extern int do_980(); +extern int do_981(); +extern int do_982(); +extern int do_983(); +extern int do_984(); +extern int do_985(); +extern int do_986(); +extern int do_987(); +extern int do_988(); +extern int do_989(); +extern int do_990(); +extern int do_991(); +extern int do_992(); +extern int do_993(); +extern int do_994(); +extern int do_995(); +extern int do_996(); +extern int do_997(); +extern int do_998(); +extern int do_999(); + + +static void* work(void* ignore) +{ + if ( do_000() != 0 ) { FAIL("iteration 0"); exit(0); } + if ( do_001() != 1 ) { FAIL("iteration 1"); exit(0); } + if ( do_002() != 2 ) { FAIL("iteration 2"); exit(0); } + if ( do_003() != 3 ) { FAIL("iteration 3"); exit(0); } + if ( do_004() != 4 ) { FAIL("iteration 4"); exit(0); } + if ( do_005() != 5 ) { FAIL("iteration 5"); exit(0); } + if ( do_006() != 6 ) { FAIL("iteration 6"); exit(0); } + if ( do_007() != 7 ) { FAIL("iteration 7"); exit(0); } + if ( do_008() != 8 ) { FAIL("iteration 8"); exit(0); } + if ( do_009() != 9 ) { FAIL("iteration 9"); exit(0); } + if ( do_010() != 10 ) { FAIL("iteration 10"); exit(0); } + if ( do_011() != 11 ) { FAIL("iteration 11"); exit(0); } + if ( do_012() != 12 ) { FAIL("iteration 12"); exit(0); } + if ( do_013() != 13 ) { FAIL("iteration 13"); exit(0); } + if ( do_014() != 14 ) { FAIL("iteration 14"); exit(0); } + if ( do_015() != 15 ) { FAIL("iteration 15"); exit(0); } + if ( do_016() != 16 ) { FAIL("iteration 16"); exit(0); } + if ( do_017() != 17 ) { FAIL("iteration 17"); exit(0); } + if ( do_018() != 18 ) { FAIL("iteration 18"); exit(0); } + if ( do_019() != 19 ) { FAIL("iteration 19"); exit(0); } + if ( do_020() != 20 ) { FAIL("iteration 20"); exit(0); } + if ( do_021() != 21 ) { FAIL("iteration 21"); exit(0); } + if ( do_022() != 22 ) { FAIL("iteration 22"); exit(0); } + if ( do_023() != 23 ) { FAIL("iteration 23"); exit(0); } + if ( do_024() != 24 ) { FAIL("iteration 24"); exit(0); } + if ( do_025() != 25 ) { FAIL("iteration 25"); exit(0); } + if ( do_026() != 26 ) { FAIL("iteration 26"); exit(0); } + if ( do_027() != 27 ) { FAIL("iteration 27"); exit(0); } + if ( do_028() != 28 ) { FAIL("iteration 28"); exit(0); } + if ( do_029() != 29 ) { FAIL("iteration 29"); exit(0); } + if ( do_030() != 30 ) { FAIL("iteration 30"); exit(0); } + if ( do_031() != 31 ) { FAIL("iteration 31"); exit(0); } + if ( do_032() != 32 ) { FAIL("iteration 32"); exit(0); } + if ( do_033() != 33 ) { FAIL("iteration 33"); exit(0); } + if ( do_034() != 34 ) { FAIL("iteration 34"); exit(0); } + if ( do_035() != 35 ) { FAIL("iteration 35"); exit(0); } + if ( do_036() != 36 ) { FAIL("iteration 36"); exit(0); } + if ( do_037() != 37 ) { FAIL("iteration 37"); exit(0); } + if ( do_038() != 38 ) { FAIL("iteration 38"); exit(0); } + if ( do_039() != 39 ) { FAIL("iteration 39"); exit(0); } + if ( do_040() != 40 ) { FAIL("iteration 40"); exit(0); } + if ( do_041() != 41 ) { FAIL("iteration 41"); exit(0); } + if ( do_042() != 42 ) { FAIL("iteration 42"); exit(0); } + if ( do_043() != 43 ) { FAIL("iteration 43"); exit(0); } + if ( do_044() != 44 ) { FAIL("iteration 44"); exit(0); } + if ( do_045() != 45 ) { FAIL("iteration 45"); exit(0); } + if ( do_046() != 46 ) { FAIL("iteration 46"); exit(0); } + if ( do_047() != 47 ) { FAIL("iteration 47"); exit(0); } + if ( do_048() != 48 ) { FAIL("iteration 48"); exit(0); } + if ( do_049() != 49 ) { FAIL("iteration 49"); exit(0); } + if ( do_050() != 50 ) { FAIL("iteration 50"); exit(0); } + if ( do_051() != 51 ) { FAIL("iteration 51"); exit(0); } + if ( do_052() != 52 ) { FAIL("iteration 52"); exit(0); } + if ( do_053() != 53 ) { FAIL("iteration 53"); exit(0); } + if ( do_054() != 54 ) { FAIL("iteration 54"); exit(0); } + if ( do_055() != 55 ) { FAIL("iteration 55"); exit(0); } + if ( do_056() != 56 ) { FAIL("iteration 56"); exit(0); } + if ( do_057() != 57 ) { FAIL("iteration 57"); exit(0); } + if ( do_058() != 58 ) { FAIL("iteration 58"); exit(0); } + if ( do_059() != 59 ) { FAIL("iteration 59"); exit(0); } + if ( do_060() != 60 ) { FAIL("iteration 60"); exit(0); } + if ( do_061() != 61 ) { FAIL("iteration 61"); exit(0); } + if ( do_062() != 62 ) { FAIL("iteration 62"); exit(0); } + if ( do_063() != 63 ) { FAIL("iteration 63"); exit(0); } + if ( do_064() != 64 ) { FAIL("iteration 64"); exit(0); } + if ( do_065() != 65 ) { FAIL("iteration 65"); exit(0); } + if ( do_066() != 66 ) { FAIL("iteration 66"); exit(0); } + if ( do_067() != 67 ) { FAIL("iteration 67"); exit(0); } + if ( do_068() != 68 ) { FAIL("iteration 68"); exit(0); } + if ( do_069() != 69 ) { FAIL("iteration 69"); exit(0); } + if ( do_070() != 70 ) { FAIL("iteration 70"); exit(0); } + if ( do_071() != 71 ) { FAIL("iteration 71"); exit(0); } + if ( do_072() != 72 ) { FAIL("iteration 72"); exit(0); } + if ( do_073() != 73 ) { FAIL("iteration 73"); exit(0); } + if ( do_074() != 74 ) { FAIL("iteration 74"); exit(0); } + if ( do_075() != 75 ) { FAIL("iteration 75"); exit(0); } + if ( do_076() != 76 ) { FAIL("iteration 76"); exit(0); } + if ( do_077() != 77 ) { FAIL("iteration 77"); exit(0); } + if ( do_078() != 78 ) { FAIL("iteration 78"); exit(0); } + if ( do_079() != 79 ) { FAIL("iteration 79"); exit(0); } + if ( do_080() != 80 ) { FAIL("iteration 80"); exit(0); } + if ( do_081() != 81 ) { FAIL("iteration 81"); exit(0); } + if ( do_082() != 82 ) { FAIL("iteration 82"); exit(0); } + if ( do_083() != 83 ) { FAIL("iteration 83"); exit(0); } + if ( do_084() != 84 ) { FAIL("iteration 84"); exit(0); } + if ( do_085() != 85 ) { FAIL("iteration 85"); exit(0); } + if ( do_086() != 86 ) { FAIL("iteration 86"); exit(0); } + if ( do_087() != 87 ) { FAIL("iteration 87"); exit(0); } + if ( do_088() != 88 ) { FAIL("iteration 88"); exit(0); } + if ( do_089() != 89 ) { FAIL("iteration 89"); exit(0); } + if ( do_090() != 90 ) { FAIL("iteration 90"); exit(0); } + if ( do_091() != 91 ) { FAIL("iteration 91"); exit(0); } + if ( do_092() != 92 ) { FAIL("iteration 92"); exit(0); } + if ( do_093() != 93 ) { FAIL("iteration 93"); exit(0); } + if ( do_094() != 94 ) { FAIL("iteration 94"); exit(0); } + if ( do_095() != 95 ) { FAIL("iteration 95"); exit(0); } + if ( do_096() != 96 ) { FAIL("iteration 96"); exit(0); } + if ( do_097() != 97 ) { FAIL("iteration 97"); exit(0); } + if ( do_098() != 98 ) { FAIL("iteration 98"); exit(0); } + if ( do_099() != 99 ) { FAIL("iteration 99"); exit(0); } + if ( do_100() != 100 ) { FAIL("iteration 100"); exit(0); } + if ( do_101() != 101 ) { FAIL("iteration 101"); exit(0); } + if ( do_102() != 102 ) { FAIL("iteration 102"); exit(0); } + if ( do_103() != 103 ) { FAIL("iteration 103"); exit(0); } + if ( do_104() != 104 ) { FAIL("iteration 104"); exit(0); } + if ( do_105() != 105 ) { FAIL("iteration 105"); exit(0); } + if ( do_106() != 106 ) { FAIL("iteration 106"); exit(0); } + if ( do_107() != 107 ) { FAIL("iteration 107"); exit(0); } + if ( do_108() != 108 ) { FAIL("iteration 108"); exit(0); } + if ( do_109() != 109 ) { FAIL("iteration 109"); exit(0); } + if ( do_110() != 110 ) { FAIL("iteration 110"); exit(0); } + if ( do_111() != 111 ) { FAIL("iteration 111"); exit(0); } + if ( do_112() != 112 ) { FAIL("iteration 112"); exit(0); } + if ( do_113() != 113 ) { FAIL("iteration 113"); exit(0); } + if ( do_114() != 114 ) { FAIL("iteration 114"); exit(0); } + if ( do_115() != 115 ) { FAIL("iteration 115"); exit(0); } + if ( do_116() != 116 ) { FAIL("iteration 116"); exit(0); } + if ( do_117() != 117 ) { FAIL("iteration 117"); exit(0); } + if ( do_118() != 118 ) { FAIL("iteration 118"); exit(0); } + if ( do_119() != 119 ) { FAIL("iteration 119"); exit(0); } + if ( do_120() != 120 ) { FAIL("iteration 120"); exit(0); } + if ( do_121() != 121 ) { FAIL("iteration 121"); exit(0); } + if ( do_122() != 122 ) { FAIL("iteration 122"); exit(0); } + if ( do_123() != 123 ) { FAIL("iteration 123"); exit(0); } + if ( do_124() != 124 ) { FAIL("iteration 124"); exit(0); } + if ( do_125() != 125 ) { FAIL("iteration 125"); exit(0); } + if ( do_126() != 126 ) { FAIL("iteration 126"); exit(0); } + if ( do_127() != 127 ) { FAIL("iteration 127"); exit(0); } + if ( do_128() != 128 ) { FAIL("iteration 128"); exit(0); } + if ( do_129() != 129 ) { FAIL("iteration 129"); exit(0); } + if ( do_130() != 130 ) { FAIL("iteration 130"); exit(0); } + if ( do_131() != 131 ) { FAIL("iteration 131"); exit(0); } + if ( do_132() != 132 ) { FAIL("iteration 132"); exit(0); } + if ( do_133() != 133 ) { FAIL("iteration 133"); exit(0); } + if ( do_134() != 134 ) { FAIL("iteration 134"); exit(0); } + if ( do_135() != 135 ) { FAIL("iteration 135"); exit(0); } + if ( do_136() != 136 ) { FAIL("iteration 136"); exit(0); } + if ( do_137() != 137 ) { FAIL("iteration 137"); exit(0); } + if ( do_138() != 138 ) { FAIL("iteration 138"); exit(0); } + if ( do_139() != 139 ) { FAIL("iteration 139"); exit(0); } + if ( do_140() != 140 ) { FAIL("iteration 140"); exit(0); } + if ( do_141() != 141 ) { FAIL("iteration 141"); exit(0); } + if ( do_142() != 142 ) { FAIL("iteration 142"); exit(0); } + if ( do_143() != 143 ) { FAIL("iteration 143"); exit(0); } + if ( do_144() != 144 ) { FAIL("iteration 144"); exit(0); } + if ( do_145() != 145 ) { FAIL("iteration 145"); exit(0); } + if ( do_146() != 146 ) { FAIL("iteration 146"); exit(0); } + if ( do_147() != 147 ) { FAIL("iteration 147"); exit(0); } + if ( do_148() != 148 ) { FAIL("iteration 148"); exit(0); } + if ( do_149() != 149 ) { FAIL("iteration 149"); exit(0); } + if ( do_150() != 150 ) { FAIL("iteration 150"); exit(0); } + if ( do_151() != 151 ) { FAIL("iteration 151"); exit(0); } + if ( do_152() != 152 ) { FAIL("iteration 152"); exit(0); } + if ( do_153() != 153 ) { FAIL("iteration 153"); exit(0); } + if ( do_154() != 154 ) { FAIL("iteration 154"); exit(0); } + if ( do_155() != 155 ) { FAIL("iteration 155"); exit(0); } + if ( do_156() != 156 ) { FAIL("iteration 156"); exit(0); } + if ( do_157() != 157 ) { FAIL("iteration 157"); exit(0); } + if ( do_158() != 158 ) { FAIL("iteration 158"); exit(0); } + if ( do_159() != 159 ) { FAIL("iteration 159"); exit(0); } + if ( do_160() != 160 ) { FAIL("iteration 160"); exit(0); } + if ( do_161() != 161 ) { FAIL("iteration 161"); exit(0); } + if ( do_162() != 162 ) { FAIL("iteration 162"); exit(0); } + if ( do_163() != 163 ) { FAIL("iteration 163"); exit(0); } + if ( do_164() != 164 ) { FAIL("iteration 164"); exit(0); } + if ( do_165() != 165 ) { FAIL("iteration 165"); exit(0); } + if ( do_166() != 166 ) { FAIL("iteration 166"); exit(0); } + if ( do_167() != 167 ) { FAIL("iteration 167"); exit(0); } + if ( do_168() != 168 ) { FAIL("iteration 168"); exit(0); } + if ( do_169() != 169 ) { FAIL("iteration 169"); exit(0); } + if ( do_170() != 170 ) { FAIL("iteration 170"); exit(0); } + if ( do_171() != 171 ) { FAIL("iteration 171"); exit(0); } + if ( do_172() != 172 ) { FAIL("iteration 172"); exit(0); } + if ( do_173() != 173 ) { FAIL("iteration 173"); exit(0); } + if ( do_174() != 174 ) { FAIL("iteration 174"); exit(0); } + if ( do_175() != 175 ) { FAIL("iteration 175"); exit(0); } + if ( do_176() != 176 ) { FAIL("iteration 176"); exit(0); } + if ( do_177() != 177 ) { FAIL("iteration 177"); exit(0); } + if ( do_178() != 178 ) { FAIL("iteration 178"); exit(0); } + if ( do_179() != 179 ) { FAIL("iteration 179"); exit(0); } + if ( do_180() != 180 ) { FAIL("iteration 180"); exit(0); } + if ( do_181() != 181 ) { FAIL("iteration 181"); exit(0); } + if ( do_182() != 182 ) { FAIL("iteration 182"); exit(0); } + if ( do_183() != 183 ) { FAIL("iteration 183"); exit(0); } + if ( do_184() != 184 ) { FAIL("iteration 184"); exit(0); } + if ( do_185() != 185 ) { FAIL("iteration 185"); exit(0); } + if ( do_186() != 186 ) { FAIL("iteration 186"); exit(0); } + if ( do_187() != 187 ) { FAIL("iteration 187"); exit(0); } + if ( do_188() != 188 ) { FAIL("iteration 188"); exit(0); } + if ( do_189() != 189 ) { FAIL("iteration 189"); exit(0); } + if ( do_190() != 190 ) { FAIL("iteration 190"); exit(0); } + if ( do_191() != 191 ) { FAIL("iteration 191"); exit(0); } + if ( do_192() != 192 ) { FAIL("iteration 192"); exit(0); } + if ( do_193() != 193 ) { FAIL("iteration 193"); exit(0); } + if ( do_194() != 194 ) { FAIL("iteration 194"); exit(0); } + if ( do_195() != 195 ) { FAIL("iteration 195"); exit(0); } + if ( do_196() != 196 ) { FAIL("iteration 196"); exit(0); } + if ( do_197() != 197 ) { FAIL("iteration 197"); exit(0); } + if ( do_198() != 198 ) { FAIL("iteration 198"); exit(0); } + if ( do_199() != 199 ) { FAIL("iteration 199"); exit(0); } + if ( do_200() != 200 ) { FAIL("iteration 200"); exit(0); } + if ( do_201() != 201 ) { FAIL("iteration 201"); exit(0); } + if ( do_202() != 202 ) { FAIL("iteration 202"); exit(0); } + if ( do_203() != 203 ) { FAIL("iteration 203"); exit(0); } + if ( do_204() != 204 ) { FAIL("iteration 204"); exit(0); } + if ( do_205() != 205 ) { FAIL("iteration 205"); exit(0); } + if ( do_206() != 206 ) { FAIL("iteration 206"); exit(0); } + if ( do_207() != 207 ) { FAIL("iteration 207"); exit(0); } + if ( do_208() != 208 ) { FAIL("iteration 208"); exit(0); } + if ( do_209() != 209 ) { FAIL("iteration 209"); exit(0); } + if ( do_210() != 210 ) { FAIL("iteration 210"); exit(0); } + if ( do_211() != 211 ) { FAIL("iteration 211"); exit(0); } + if ( do_212() != 212 ) { FAIL("iteration 212"); exit(0); } + if ( do_213() != 213 ) { FAIL("iteration 213"); exit(0); } + if ( do_214() != 214 ) { FAIL("iteration 214"); exit(0); } + if ( do_215() != 215 ) { FAIL("iteration 215"); exit(0); } + if ( do_216() != 216 ) { FAIL("iteration 216"); exit(0); } + if ( do_217() != 217 ) { FAIL("iteration 217"); exit(0); } + if ( do_218() != 218 ) { FAIL("iteration 218"); exit(0); } + if ( do_219() != 219 ) { FAIL("iteration 219"); exit(0); } + if ( do_220() != 220 ) { FAIL("iteration 220"); exit(0); } + if ( do_221() != 221 ) { FAIL("iteration 221"); exit(0); } + if ( do_222() != 222 ) { FAIL("iteration 222"); exit(0); } + if ( do_223() != 223 ) { FAIL("iteration 223"); exit(0); } + if ( do_224() != 224 ) { FAIL("iteration 224"); exit(0); } + if ( do_225() != 225 ) { FAIL("iteration 225"); exit(0); } + if ( do_226() != 226 ) { FAIL("iteration 226"); exit(0); } + if ( do_227() != 227 ) { FAIL("iteration 227"); exit(0); } + if ( do_228() != 228 ) { FAIL("iteration 228"); exit(0); } + if ( do_229() != 229 ) { FAIL("iteration 229"); exit(0); } + if ( do_230() != 230 ) { FAIL("iteration 230"); exit(0); } + if ( do_231() != 231 ) { FAIL("iteration 231"); exit(0); } + if ( do_232() != 232 ) { FAIL("iteration 232"); exit(0); } + if ( do_233() != 233 ) { FAIL("iteration 233"); exit(0); } + if ( do_234() != 234 ) { FAIL("iteration 234"); exit(0); } + if ( do_235() != 235 ) { FAIL("iteration 235"); exit(0); } + if ( do_236() != 236 ) { FAIL("iteration 236"); exit(0); } + if ( do_237() != 237 ) { FAIL("iteration 237"); exit(0); } + if ( do_238() != 238 ) { FAIL("iteration 238"); exit(0); } + if ( do_239() != 239 ) { FAIL("iteration 239"); exit(0); } + if ( do_240() != 240 ) { FAIL("iteration 240"); exit(0); } + if ( do_241() != 241 ) { FAIL("iteration 241"); exit(0); } + if ( do_242() != 242 ) { FAIL("iteration 242"); exit(0); } + if ( do_243() != 243 ) { FAIL("iteration 243"); exit(0); } + if ( do_244() != 244 ) { FAIL("iteration 244"); exit(0); } + if ( do_245() != 245 ) { FAIL("iteration 245"); exit(0); } + if ( do_246() != 246 ) { FAIL("iteration 246"); exit(0); } + if ( do_247() != 247 ) { FAIL("iteration 247"); exit(0); } + if ( do_248() != 248 ) { FAIL("iteration 248"); exit(0); } + if ( do_249() != 249 ) { FAIL("iteration 249"); exit(0); } + if ( do_250() != 250 ) { FAIL("iteration 250"); exit(0); } + if ( do_251() != 251 ) { FAIL("iteration 251"); exit(0); } + if ( do_252() != 252 ) { FAIL("iteration 252"); exit(0); } + if ( do_253() != 253 ) { FAIL("iteration 253"); exit(0); } + if ( do_254() != 254 ) { FAIL("iteration 254"); exit(0); } + if ( do_255() != 255 ) { FAIL("iteration 255"); exit(0); } + if ( do_256() != 256 ) { FAIL("iteration 256"); exit(0); } + if ( do_257() != 257 ) { FAIL("iteration 257"); exit(0); } + if ( do_258() != 258 ) { FAIL("iteration 258"); exit(0); } + if ( do_259() != 259 ) { FAIL("iteration 259"); exit(0); } + if ( do_260() != 260 ) { FAIL("iteration 260"); exit(0); } + if ( do_261() != 261 ) { FAIL("iteration 261"); exit(0); } + if ( do_262() != 262 ) { FAIL("iteration 262"); exit(0); } + if ( do_263() != 263 ) { FAIL("iteration 263"); exit(0); } + if ( do_264() != 264 ) { FAIL("iteration 264"); exit(0); } + if ( do_265() != 265 ) { FAIL("iteration 265"); exit(0); } + if ( do_266() != 266 ) { FAIL("iteration 266"); exit(0); } + if ( do_267() != 267 ) { FAIL("iteration 267"); exit(0); } + if ( do_268() != 268 ) { FAIL("iteration 268"); exit(0); } + if ( do_269() != 269 ) { FAIL("iteration 269"); exit(0); } + if ( do_270() != 270 ) { FAIL("iteration 270"); exit(0); } + if ( do_271() != 271 ) { FAIL("iteration 271"); exit(0); } + if ( do_272() != 272 ) { FAIL("iteration 272"); exit(0); } + if ( do_273() != 273 ) { FAIL("iteration 273"); exit(0); } + if ( do_274() != 274 ) { FAIL("iteration 274"); exit(0); } + if ( do_275() != 275 ) { FAIL("iteration 275"); exit(0); } + if ( do_276() != 276 ) { FAIL("iteration 276"); exit(0); } + if ( do_277() != 277 ) { FAIL("iteration 277"); exit(0); } + if ( do_278() != 278 ) { FAIL("iteration 278"); exit(0); } + if ( do_279() != 279 ) { FAIL("iteration 279"); exit(0); } + if ( do_280() != 280 ) { FAIL("iteration 280"); exit(0); } + if ( do_281() != 281 ) { FAIL("iteration 281"); exit(0); } + if ( do_282() != 282 ) { FAIL("iteration 282"); exit(0); } + if ( do_283() != 283 ) { FAIL("iteration 283"); exit(0); } + if ( do_284() != 284 ) { FAIL("iteration 284"); exit(0); } + if ( do_285() != 285 ) { FAIL("iteration 285"); exit(0); } + if ( do_286() != 286 ) { FAIL("iteration 286"); exit(0); } + if ( do_287() != 287 ) { FAIL("iteration 287"); exit(0); } + if ( do_288() != 288 ) { FAIL("iteration 288"); exit(0); } + if ( do_289() != 289 ) { FAIL("iteration 289"); exit(0); } + if ( do_290() != 290 ) { FAIL("iteration 290"); exit(0); } + if ( do_291() != 291 ) { FAIL("iteration 291"); exit(0); } + if ( do_292() != 292 ) { FAIL("iteration 292"); exit(0); } + if ( do_293() != 293 ) { FAIL("iteration 293"); exit(0); } + if ( do_294() != 294 ) { FAIL("iteration 294"); exit(0); } + if ( do_295() != 295 ) { FAIL("iteration 295"); exit(0); } + if ( do_296() != 296 ) { FAIL("iteration 296"); exit(0); } + if ( do_297() != 297 ) { FAIL("iteration 297"); exit(0); } + if ( do_298() != 298 ) { FAIL("iteration 298"); exit(0); } + if ( do_299() != 299 ) { FAIL("iteration 299"); exit(0); } + if ( do_300() != 300 ) { FAIL("iteration 300"); exit(0); } + if ( do_301() != 301 ) { FAIL("iteration 301"); exit(0); } + if ( do_302() != 302 ) { FAIL("iteration 302"); exit(0); } + if ( do_303() != 303 ) { FAIL("iteration 303"); exit(0); } + if ( do_304() != 304 ) { FAIL("iteration 304"); exit(0); } + if ( do_305() != 305 ) { FAIL("iteration 305"); exit(0); } + if ( do_306() != 306 ) { FAIL("iteration 306"); exit(0); } + if ( do_307() != 307 ) { FAIL("iteration 307"); exit(0); } + if ( do_308() != 308 ) { FAIL("iteration 308"); exit(0); } + if ( do_309() != 309 ) { FAIL("iteration 309"); exit(0); } + if ( do_310() != 310 ) { FAIL("iteration 310"); exit(0); } + if ( do_311() != 311 ) { FAIL("iteration 311"); exit(0); } + if ( do_312() != 312 ) { FAIL("iteration 312"); exit(0); } + if ( do_313() != 313 ) { FAIL("iteration 313"); exit(0); } + if ( do_314() != 314 ) { FAIL("iteration 314"); exit(0); } + if ( do_315() != 315 ) { FAIL("iteration 315"); exit(0); } + if ( do_316() != 316 ) { FAIL("iteration 316"); exit(0); } + if ( do_317() != 317 ) { FAIL("iteration 317"); exit(0); } + if ( do_318() != 318 ) { FAIL("iteration 318"); exit(0); } + if ( do_319() != 319 ) { FAIL("iteration 319"); exit(0); } + if ( do_320() != 320 ) { FAIL("iteration 320"); exit(0); } + if ( do_321() != 321 ) { FAIL("iteration 321"); exit(0); } + if ( do_322() != 322 ) { FAIL("iteration 322"); exit(0); } + if ( do_323() != 323 ) { FAIL("iteration 323"); exit(0); } + if ( do_324() != 324 ) { FAIL("iteration 324"); exit(0); } + if ( do_325() != 325 ) { FAIL("iteration 325"); exit(0); } + if ( do_326() != 326 ) { FAIL("iteration 326"); exit(0); } + if ( do_327() != 327 ) { FAIL("iteration 327"); exit(0); } + if ( do_328() != 328 ) { FAIL("iteration 328"); exit(0); } + if ( do_329() != 329 ) { FAIL("iteration 329"); exit(0); } + if ( do_330() != 330 ) { FAIL("iteration 330"); exit(0); } + if ( do_331() != 331 ) { FAIL("iteration 331"); exit(0); } + if ( do_332() != 332 ) { FAIL("iteration 332"); exit(0); } + if ( do_333() != 333 ) { FAIL("iteration 333"); exit(0); } + if ( do_334() != 334 ) { FAIL("iteration 334"); exit(0); } + if ( do_335() != 335 ) { FAIL("iteration 335"); exit(0); } + if ( do_336() != 336 ) { FAIL("iteration 336"); exit(0); } + if ( do_337() != 337 ) { FAIL("iteration 337"); exit(0); } + if ( do_338() != 338 ) { FAIL("iteration 338"); exit(0); } + if ( do_339() != 339 ) { FAIL("iteration 339"); exit(0); } + if ( do_340() != 340 ) { FAIL("iteration 340"); exit(0); } + if ( do_341() != 341 ) { FAIL("iteration 341"); exit(0); } + if ( do_342() != 342 ) { FAIL("iteration 342"); exit(0); } + if ( do_343() != 343 ) { FAIL("iteration 343"); exit(0); } + if ( do_344() != 344 ) { FAIL("iteration 344"); exit(0); } + if ( do_345() != 345 ) { FAIL("iteration 345"); exit(0); } + if ( do_346() != 346 ) { FAIL("iteration 346"); exit(0); } + if ( do_347() != 347 ) { FAIL("iteration 347"); exit(0); } + if ( do_348() != 348 ) { FAIL("iteration 348"); exit(0); } + if ( do_349() != 349 ) { FAIL("iteration 349"); exit(0); } + if ( do_350() != 350 ) { FAIL("iteration 350"); exit(0); } + if ( do_351() != 351 ) { FAIL("iteration 351"); exit(0); } + if ( do_352() != 352 ) { FAIL("iteration 352"); exit(0); } + if ( do_353() != 353 ) { FAIL("iteration 353"); exit(0); } + if ( do_354() != 354 ) { FAIL("iteration 354"); exit(0); } + if ( do_355() != 355 ) { FAIL("iteration 355"); exit(0); } + if ( do_356() != 356 ) { FAIL("iteration 356"); exit(0); } + if ( do_357() != 357 ) { FAIL("iteration 357"); exit(0); } + if ( do_358() != 358 ) { FAIL("iteration 358"); exit(0); } + if ( do_359() != 359 ) { FAIL("iteration 359"); exit(0); } + if ( do_360() != 360 ) { FAIL("iteration 360"); exit(0); } + if ( do_361() != 361 ) { FAIL("iteration 361"); exit(0); } + if ( do_362() != 362 ) { FAIL("iteration 362"); exit(0); } + if ( do_363() != 363 ) { FAIL("iteration 363"); exit(0); } + if ( do_364() != 364 ) { FAIL("iteration 364"); exit(0); } + if ( do_365() != 365 ) { FAIL("iteration 365"); exit(0); } + if ( do_366() != 366 ) { FAIL("iteration 366"); exit(0); } + if ( do_367() != 367 ) { FAIL("iteration 367"); exit(0); } + if ( do_368() != 368 ) { FAIL("iteration 368"); exit(0); } + if ( do_369() != 369 ) { FAIL("iteration 369"); exit(0); } + if ( do_370() != 370 ) { FAIL("iteration 370"); exit(0); } + if ( do_371() != 371 ) { FAIL("iteration 371"); exit(0); } + if ( do_372() != 372 ) { FAIL("iteration 372"); exit(0); } + if ( do_373() != 373 ) { FAIL("iteration 373"); exit(0); } + if ( do_374() != 374 ) { FAIL("iteration 374"); exit(0); } + if ( do_375() != 375 ) { FAIL("iteration 375"); exit(0); } + if ( do_376() != 376 ) { FAIL("iteration 376"); exit(0); } + if ( do_377() != 377 ) { FAIL("iteration 377"); exit(0); } + if ( do_378() != 378 ) { FAIL("iteration 378"); exit(0); } + if ( do_379() != 379 ) { FAIL("iteration 379"); exit(0); } + if ( do_380() != 380 ) { FAIL("iteration 380"); exit(0); } + if ( do_381() != 381 ) { FAIL("iteration 381"); exit(0); } + if ( do_382() != 382 ) { FAIL("iteration 382"); exit(0); } + if ( do_383() != 383 ) { FAIL("iteration 383"); exit(0); } + if ( do_384() != 384 ) { FAIL("iteration 384"); exit(0); } + if ( do_385() != 385 ) { FAIL("iteration 385"); exit(0); } + if ( do_386() != 386 ) { FAIL("iteration 386"); exit(0); } + if ( do_387() != 387 ) { FAIL("iteration 387"); exit(0); } + if ( do_388() != 388 ) { FAIL("iteration 388"); exit(0); } + if ( do_389() != 389 ) { FAIL("iteration 389"); exit(0); } + if ( do_390() != 390 ) { FAIL("iteration 390"); exit(0); } + if ( do_391() != 391 ) { FAIL("iteration 391"); exit(0); } + if ( do_392() != 392 ) { FAIL("iteration 392"); exit(0); } + if ( do_393() != 393 ) { FAIL("iteration 393"); exit(0); } + if ( do_394() != 394 ) { FAIL("iteration 394"); exit(0); } + if ( do_395() != 395 ) { FAIL("iteration 395"); exit(0); } + if ( do_396() != 396 ) { FAIL("iteration 396"); exit(0); } + if ( do_397() != 397 ) { FAIL("iteration 397"); exit(0); } + if ( do_398() != 398 ) { FAIL("iteration 398"); exit(0); } + if ( do_399() != 399 ) { FAIL("iteration 399"); exit(0); } + if ( do_400() != 400 ) { FAIL("iteration 400"); exit(0); } + if ( do_401() != 401 ) { FAIL("iteration 401"); exit(0); } + if ( do_402() != 402 ) { FAIL("iteration 402"); exit(0); } + if ( do_403() != 403 ) { FAIL("iteration 403"); exit(0); } + if ( do_404() != 404 ) { FAIL("iteration 404"); exit(0); } + if ( do_405() != 405 ) { FAIL("iteration 405"); exit(0); } + if ( do_406() != 406 ) { FAIL("iteration 406"); exit(0); } + if ( do_407() != 407 ) { FAIL("iteration 407"); exit(0); } + if ( do_408() != 408 ) { FAIL("iteration 408"); exit(0); } + if ( do_409() != 409 ) { FAIL("iteration 409"); exit(0); } + if ( do_410() != 410 ) { FAIL("iteration 410"); exit(0); } + if ( do_411() != 411 ) { FAIL("iteration 411"); exit(0); } + if ( do_412() != 412 ) { FAIL("iteration 412"); exit(0); } + if ( do_413() != 413 ) { FAIL("iteration 413"); exit(0); } + if ( do_414() != 414 ) { FAIL("iteration 414"); exit(0); } + if ( do_415() != 415 ) { FAIL("iteration 415"); exit(0); } + if ( do_416() != 416 ) { FAIL("iteration 416"); exit(0); } + if ( do_417() != 417 ) { FAIL("iteration 417"); exit(0); } + if ( do_418() != 418 ) { FAIL("iteration 418"); exit(0); } + if ( do_419() != 419 ) { FAIL("iteration 419"); exit(0); } + if ( do_420() != 420 ) { FAIL("iteration 420"); exit(0); } + if ( do_421() != 421 ) { FAIL("iteration 421"); exit(0); } + if ( do_422() != 422 ) { FAIL("iteration 422"); exit(0); } + if ( do_423() != 423 ) { FAIL("iteration 423"); exit(0); } + if ( do_424() != 424 ) { FAIL("iteration 424"); exit(0); } + if ( do_425() != 425 ) { FAIL("iteration 425"); exit(0); } + if ( do_426() != 426 ) { FAIL("iteration 426"); exit(0); } + if ( do_427() != 427 ) { FAIL("iteration 427"); exit(0); } + if ( do_428() != 428 ) { FAIL("iteration 428"); exit(0); } + if ( do_429() != 429 ) { FAIL("iteration 429"); exit(0); } + if ( do_430() != 430 ) { FAIL("iteration 430"); exit(0); } + if ( do_431() != 431 ) { FAIL("iteration 431"); exit(0); } + if ( do_432() != 432 ) { FAIL("iteration 432"); exit(0); } + if ( do_433() != 433 ) { FAIL("iteration 433"); exit(0); } + if ( do_434() != 434 ) { FAIL("iteration 434"); exit(0); } + if ( do_435() != 435 ) { FAIL("iteration 435"); exit(0); } + if ( do_436() != 436 ) { FAIL("iteration 436"); exit(0); } + if ( do_437() != 437 ) { FAIL("iteration 437"); exit(0); } + if ( do_438() != 438 ) { FAIL("iteration 438"); exit(0); } + if ( do_439() != 439 ) { FAIL("iteration 439"); exit(0); } + if ( do_440() != 440 ) { FAIL("iteration 440"); exit(0); } + if ( do_441() != 441 ) { FAIL("iteration 441"); exit(0); } + if ( do_442() != 442 ) { FAIL("iteration 442"); exit(0); } + if ( do_443() != 443 ) { FAIL("iteration 443"); exit(0); } + if ( do_444() != 444 ) { FAIL("iteration 444"); exit(0); } + if ( do_445() != 445 ) { FAIL("iteration 445"); exit(0); } + if ( do_446() != 446 ) { FAIL("iteration 446"); exit(0); } + if ( do_447() != 447 ) { FAIL("iteration 447"); exit(0); } + if ( do_448() != 448 ) { FAIL("iteration 448"); exit(0); } + if ( do_449() != 449 ) { FAIL("iteration 449"); exit(0); } + if ( do_450() != 450 ) { FAIL("iteration 450"); exit(0); } + if ( do_451() != 451 ) { FAIL("iteration 451"); exit(0); } + if ( do_452() != 452 ) { FAIL("iteration 452"); exit(0); } + if ( do_453() != 453 ) { FAIL("iteration 453"); exit(0); } + if ( do_454() != 454 ) { FAIL("iteration 454"); exit(0); } + if ( do_455() != 455 ) { FAIL("iteration 455"); exit(0); } + if ( do_456() != 456 ) { FAIL("iteration 456"); exit(0); } + if ( do_457() != 457 ) { FAIL("iteration 457"); exit(0); } + if ( do_458() != 458 ) { FAIL("iteration 458"); exit(0); } + if ( do_459() != 459 ) { FAIL("iteration 459"); exit(0); } + if ( do_460() != 460 ) { FAIL("iteration 460"); exit(0); } + if ( do_461() != 461 ) { FAIL("iteration 461"); exit(0); } + if ( do_462() != 462 ) { FAIL("iteration 462"); exit(0); } + if ( do_463() != 463 ) { FAIL("iteration 463"); exit(0); } + if ( do_464() != 464 ) { FAIL("iteration 464"); exit(0); } + if ( do_465() != 465 ) { FAIL("iteration 465"); exit(0); } + if ( do_466() != 466 ) { FAIL("iteration 466"); exit(0); } + if ( do_467() != 467 ) { FAIL("iteration 467"); exit(0); } + if ( do_468() != 468 ) { FAIL("iteration 468"); exit(0); } + if ( do_469() != 469 ) { FAIL("iteration 469"); exit(0); } + if ( do_470() != 470 ) { FAIL("iteration 470"); exit(0); } + if ( do_471() != 471 ) { FAIL("iteration 471"); exit(0); } + if ( do_472() != 472 ) { FAIL("iteration 472"); exit(0); } + if ( do_473() != 473 ) { FAIL("iteration 473"); exit(0); } + if ( do_474() != 474 ) { FAIL("iteration 474"); exit(0); } + if ( do_475() != 475 ) { FAIL("iteration 475"); exit(0); } + if ( do_476() != 476 ) { FAIL("iteration 476"); exit(0); } + if ( do_477() != 477 ) { FAIL("iteration 477"); exit(0); } + if ( do_478() != 478 ) { FAIL("iteration 478"); exit(0); } + if ( do_479() != 479 ) { FAIL("iteration 479"); exit(0); } + if ( do_480() != 480 ) { FAIL("iteration 480"); exit(0); } + if ( do_481() != 481 ) { FAIL("iteration 481"); exit(0); } + if ( do_482() != 482 ) { FAIL("iteration 482"); exit(0); } + if ( do_483() != 483 ) { FAIL("iteration 483"); exit(0); } + if ( do_484() != 484 ) { FAIL("iteration 484"); exit(0); } + if ( do_485() != 485 ) { FAIL("iteration 485"); exit(0); } + if ( do_486() != 486 ) { FAIL("iteration 486"); exit(0); } + if ( do_487() != 487 ) { FAIL("iteration 487"); exit(0); } + if ( do_488() != 488 ) { FAIL("iteration 488"); exit(0); } + if ( do_489() != 489 ) { FAIL("iteration 489"); exit(0); } + if ( do_490() != 490 ) { FAIL("iteration 490"); exit(0); } + if ( do_491() != 491 ) { FAIL("iteration 491"); exit(0); } + if ( do_492() != 492 ) { FAIL("iteration 492"); exit(0); } + if ( do_493() != 493 ) { FAIL("iteration 493"); exit(0); } + if ( do_494() != 494 ) { FAIL("iteration 494"); exit(0); } + if ( do_495() != 495 ) { FAIL("iteration 495"); exit(0); } + if ( do_496() != 496 ) { FAIL("iteration 496"); exit(0); } + if ( do_497() != 497 ) { FAIL("iteration 497"); exit(0); } + if ( do_498() != 498 ) { FAIL("iteration 498"); exit(0); } + if ( do_499() != 499 ) { FAIL("iteration 499"); exit(0); } + if ( do_500() != 500 ) { FAIL("iteration 500"); exit(0); } + if ( do_501() != 501 ) { FAIL("iteration 501"); exit(0); } + if ( do_502() != 502 ) { FAIL("iteration 502"); exit(0); } + if ( do_503() != 503 ) { FAIL("iteration 503"); exit(0); } + if ( do_504() != 504 ) { FAIL("iteration 504"); exit(0); } + if ( do_505() != 505 ) { FAIL("iteration 505"); exit(0); } + if ( do_506() != 506 ) { FAIL("iteration 506"); exit(0); } + if ( do_507() != 507 ) { FAIL("iteration 507"); exit(0); } + if ( do_508() != 508 ) { FAIL("iteration 508"); exit(0); } + if ( do_509() != 509 ) { FAIL("iteration 509"); exit(0); } + if ( do_510() != 510 ) { FAIL("iteration 510"); exit(0); } + if ( do_511() != 511 ) { FAIL("iteration 511"); exit(0); } + if ( do_512() != 512 ) { FAIL("iteration 512"); exit(0); } + if ( do_513() != 513 ) { FAIL("iteration 513"); exit(0); } + if ( do_514() != 514 ) { FAIL("iteration 514"); exit(0); } + if ( do_515() != 515 ) { FAIL("iteration 515"); exit(0); } + if ( do_516() != 516 ) { FAIL("iteration 516"); exit(0); } + if ( do_517() != 517 ) { FAIL("iteration 517"); exit(0); } + if ( do_518() != 518 ) { FAIL("iteration 518"); exit(0); } + if ( do_519() != 519 ) { FAIL("iteration 519"); exit(0); } + if ( do_520() != 520 ) { FAIL("iteration 520"); exit(0); } + if ( do_521() != 521 ) { FAIL("iteration 521"); exit(0); } + if ( do_522() != 522 ) { FAIL("iteration 522"); exit(0); } + if ( do_523() != 523 ) { FAIL("iteration 523"); exit(0); } + if ( do_524() != 524 ) { FAIL("iteration 524"); exit(0); } + if ( do_525() != 525 ) { FAIL("iteration 525"); exit(0); } + if ( do_526() != 526 ) { FAIL("iteration 526"); exit(0); } + if ( do_527() != 527 ) { FAIL("iteration 527"); exit(0); } + if ( do_528() != 528 ) { FAIL("iteration 528"); exit(0); } + if ( do_529() != 529 ) { FAIL("iteration 529"); exit(0); } + if ( do_530() != 530 ) { FAIL("iteration 530"); exit(0); } + if ( do_531() != 531 ) { FAIL("iteration 531"); exit(0); } + if ( do_532() != 532 ) { FAIL("iteration 532"); exit(0); } + if ( do_533() != 533 ) { FAIL("iteration 533"); exit(0); } + if ( do_534() != 534 ) { FAIL("iteration 534"); exit(0); } + if ( do_535() != 535 ) { FAIL("iteration 535"); exit(0); } + if ( do_536() != 536 ) { FAIL("iteration 536"); exit(0); } + if ( do_537() != 537 ) { FAIL("iteration 537"); exit(0); } + if ( do_538() != 538 ) { FAIL("iteration 538"); exit(0); } + if ( do_539() != 539 ) { FAIL("iteration 539"); exit(0); } + if ( do_540() != 540 ) { FAIL("iteration 540"); exit(0); } + if ( do_541() != 541 ) { FAIL("iteration 541"); exit(0); } + if ( do_542() != 542 ) { FAIL("iteration 542"); exit(0); } + if ( do_543() != 543 ) { FAIL("iteration 543"); exit(0); } + if ( do_544() != 544 ) { FAIL("iteration 544"); exit(0); } + if ( do_545() != 545 ) { FAIL("iteration 545"); exit(0); } + if ( do_546() != 546 ) { FAIL("iteration 546"); exit(0); } + if ( do_547() != 547 ) { FAIL("iteration 547"); exit(0); } + if ( do_548() != 548 ) { FAIL("iteration 548"); exit(0); } + if ( do_549() != 549 ) { FAIL("iteration 549"); exit(0); } + if ( do_550() != 550 ) { FAIL("iteration 550"); exit(0); } + if ( do_551() != 551 ) { FAIL("iteration 551"); exit(0); } + if ( do_552() != 552 ) { FAIL("iteration 552"); exit(0); } + if ( do_553() != 553 ) { FAIL("iteration 553"); exit(0); } + if ( do_554() != 554 ) { FAIL("iteration 554"); exit(0); } + if ( do_555() != 555 ) { FAIL("iteration 555"); exit(0); } + if ( do_556() != 556 ) { FAIL("iteration 556"); exit(0); } + if ( do_557() != 557 ) { FAIL("iteration 557"); exit(0); } + if ( do_558() != 558 ) { FAIL("iteration 558"); exit(0); } + if ( do_559() != 559 ) { FAIL("iteration 559"); exit(0); } + if ( do_560() != 560 ) { FAIL("iteration 560"); exit(0); } + if ( do_561() != 561 ) { FAIL("iteration 561"); exit(0); } + if ( do_562() != 562 ) { FAIL("iteration 562"); exit(0); } + if ( do_563() != 563 ) { FAIL("iteration 563"); exit(0); } + if ( do_564() != 564 ) { FAIL("iteration 564"); exit(0); } + if ( do_565() != 565 ) { FAIL("iteration 565"); exit(0); } + if ( do_566() != 566 ) { FAIL("iteration 566"); exit(0); } + if ( do_567() != 567 ) { FAIL("iteration 567"); exit(0); } + if ( do_568() != 568 ) { FAIL("iteration 568"); exit(0); } + if ( do_569() != 569 ) { FAIL("iteration 569"); exit(0); } + if ( do_570() != 570 ) { FAIL("iteration 570"); exit(0); } + if ( do_571() != 571 ) { FAIL("iteration 571"); exit(0); } + if ( do_572() != 572 ) { FAIL("iteration 572"); exit(0); } + if ( do_573() != 573 ) { FAIL("iteration 573"); exit(0); } + if ( do_574() != 574 ) { FAIL("iteration 574"); exit(0); } + if ( do_575() != 575 ) { FAIL("iteration 575"); exit(0); } + if ( do_576() != 576 ) { FAIL("iteration 576"); exit(0); } + if ( do_577() != 577 ) { FAIL("iteration 577"); exit(0); } + if ( do_578() != 578 ) { FAIL("iteration 578"); exit(0); } + if ( do_579() != 579 ) { FAIL("iteration 579"); exit(0); } + if ( do_580() != 580 ) { FAIL("iteration 580"); exit(0); } + if ( do_581() != 581 ) { FAIL("iteration 581"); exit(0); } + if ( do_582() != 582 ) { FAIL("iteration 582"); exit(0); } + if ( do_583() != 583 ) { FAIL("iteration 583"); exit(0); } + if ( do_584() != 584 ) { FAIL("iteration 584"); exit(0); } + if ( do_585() != 585 ) { FAIL("iteration 585"); exit(0); } + if ( do_586() != 586 ) { FAIL("iteration 586"); exit(0); } + if ( do_587() != 587 ) { FAIL("iteration 587"); exit(0); } + if ( do_588() != 588 ) { FAIL("iteration 588"); exit(0); } + if ( do_589() != 589 ) { FAIL("iteration 589"); exit(0); } + if ( do_590() != 590 ) { FAIL("iteration 590"); exit(0); } + if ( do_591() != 591 ) { FAIL("iteration 591"); exit(0); } + if ( do_592() != 592 ) { FAIL("iteration 592"); exit(0); } + if ( do_593() != 593 ) { FAIL("iteration 593"); exit(0); } + if ( do_594() != 594 ) { FAIL("iteration 594"); exit(0); } + if ( do_595() != 595 ) { FAIL("iteration 595"); exit(0); } + if ( do_596() != 596 ) { FAIL("iteration 596"); exit(0); } + if ( do_597() != 597 ) { FAIL("iteration 597"); exit(0); } + if ( do_598() != 598 ) { FAIL("iteration 598"); exit(0); } + if ( do_599() != 599 ) { FAIL("iteration 599"); exit(0); } + if ( do_600() != 600 ) { FAIL("iteration 600"); exit(0); } + if ( do_601() != 601 ) { FAIL("iteration 601"); exit(0); } + if ( do_602() != 602 ) { FAIL("iteration 602"); exit(0); } + if ( do_603() != 603 ) { FAIL("iteration 603"); exit(0); } + if ( do_604() != 604 ) { FAIL("iteration 604"); exit(0); } + if ( do_605() != 605 ) { FAIL("iteration 605"); exit(0); } + if ( do_606() != 606 ) { FAIL("iteration 606"); exit(0); } + if ( do_607() != 607 ) { FAIL("iteration 607"); exit(0); } + if ( do_608() != 608 ) { FAIL("iteration 608"); exit(0); } + if ( do_609() != 609 ) { FAIL("iteration 609"); exit(0); } + if ( do_610() != 610 ) { FAIL("iteration 610"); exit(0); } + if ( do_611() != 611 ) { FAIL("iteration 611"); exit(0); } + if ( do_612() != 612 ) { FAIL("iteration 612"); exit(0); } + if ( do_613() != 613 ) { FAIL("iteration 613"); exit(0); } + if ( do_614() != 614 ) { FAIL("iteration 614"); exit(0); } + if ( do_615() != 615 ) { FAIL("iteration 615"); exit(0); } + if ( do_616() != 616 ) { FAIL("iteration 616"); exit(0); } + if ( do_617() != 617 ) { FAIL("iteration 617"); exit(0); } + if ( do_618() != 618 ) { FAIL("iteration 618"); exit(0); } + if ( do_619() != 619 ) { FAIL("iteration 619"); exit(0); } + if ( do_620() != 620 ) { FAIL("iteration 620"); exit(0); } + if ( do_621() != 621 ) { FAIL("iteration 621"); exit(0); } + if ( do_622() != 622 ) { FAIL("iteration 622"); exit(0); } + if ( do_623() != 623 ) { FAIL("iteration 623"); exit(0); } + if ( do_624() != 624 ) { FAIL("iteration 624"); exit(0); } + if ( do_625() != 625 ) { FAIL("iteration 625"); exit(0); } + if ( do_626() != 626 ) { FAIL("iteration 626"); exit(0); } + if ( do_627() != 627 ) { FAIL("iteration 627"); exit(0); } + if ( do_628() != 628 ) { FAIL("iteration 628"); exit(0); } + if ( do_629() != 629 ) { FAIL("iteration 629"); exit(0); } + if ( do_630() != 630 ) { FAIL("iteration 630"); exit(0); } + if ( do_631() != 631 ) { FAIL("iteration 631"); exit(0); } + if ( do_632() != 632 ) { FAIL("iteration 632"); exit(0); } + if ( do_633() != 633 ) { FAIL("iteration 633"); exit(0); } + if ( do_634() != 634 ) { FAIL("iteration 634"); exit(0); } + if ( do_635() != 635 ) { FAIL("iteration 635"); exit(0); } + if ( do_636() != 636 ) { FAIL("iteration 636"); exit(0); } + if ( do_637() != 637 ) { FAIL("iteration 637"); exit(0); } + if ( do_638() != 638 ) { FAIL("iteration 638"); exit(0); } + if ( do_639() != 639 ) { FAIL("iteration 639"); exit(0); } + if ( do_640() != 640 ) { FAIL("iteration 640"); exit(0); } + if ( do_641() != 641 ) { FAIL("iteration 641"); exit(0); } + if ( do_642() != 642 ) { FAIL("iteration 642"); exit(0); } + if ( do_643() != 643 ) { FAIL("iteration 643"); exit(0); } + if ( do_644() != 644 ) { FAIL("iteration 644"); exit(0); } + if ( do_645() != 645 ) { FAIL("iteration 645"); exit(0); } + if ( do_646() != 646 ) { FAIL("iteration 646"); exit(0); } + if ( do_647() != 647 ) { FAIL("iteration 647"); exit(0); } + if ( do_648() != 648 ) { FAIL("iteration 648"); exit(0); } + if ( do_649() != 649 ) { FAIL("iteration 649"); exit(0); } + if ( do_650() != 650 ) { FAIL("iteration 650"); exit(0); } + if ( do_651() != 651 ) { FAIL("iteration 651"); exit(0); } + if ( do_652() != 652 ) { FAIL("iteration 652"); exit(0); } + if ( do_653() != 653 ) { FAIL("iteration 653"); exit(0); } + if ( do_654() != 654 ) { FAIL("iteration 654"); exit(0); } + if ( do_655() != 655 ) { FAIL("iteration 655"); exit(0); } + if ( do_656() != 656 ) { FAIL("iteration 656"); exit(0); } + if ( do_657() != 657 ) { FAIL("iteration 657"); exit(0); } + if ( do_658() != 658 ) { FAIL("iteration 658"); exit(0); } + if ( do_659() != 659 ) { FAIL("iteration 659"); exit(0); } + if ( do_660() != 660 ) { FAIL("iteration 660"); exit(0); } + if ( do_661() != 661 ) { FAIL("iteration 661"); exit(0); } + if ( do_662() != 662 ) { FAIL("iteration 662"); exit(0); } + if ( do_663() != 663 ) { FAIL("iteration 663"); exit(0); } + if ( do_664() != 664 ) { FAIL("iteration 664"); exit(0); } + if ( do_665() != 665 ) { FAIL("iteration 665"); exit(0); } + if ( do_666() != 666 ) { FAIL("iteration 666"); exit(0); } + if ( do_667() != 667 ) { FAIL("iteration 667"); exit(0); } + if ( do_668() != 668 ) { FAIL("iteration 668"); exit(0); } + if ( do_669() != 669 ) { FAIL("iteration 669"); exit(0); } + if ( do_670() != 670 ) { FAIL("iteration 670"); exit(0); } + if ( do_671() != 671 ) { FAIL("iteration 671"); exit(0); } + if ( do_672() != 672 ) { FAIL("iteration 672"); exit(0); } + if ( do_673() != 673 ) { FAIL("iteration 673"); exit(0); } + if ( do_674() != 674 ) { FAIL("iteration 674"); exit(0); } + if ( do_675() != 675 ) { FAIL("iteration 675"); exit(0); } + if ( do_676() != 676 ) { FAIL("iteration 676"); exit(0); } + if ( do_677() != 677 ) { FAIL("iteration 677"); exit(0); } + if ( do_678() != 678 ) { FAIL("iteration 678"); exit(0); } + if ( do_679() != 679 ) { FAIL("iteration 679"); exit(0); } + if ( do_680() != 680 ) { FAIL("iteration 680"); exit(0); } + if ( do_681() != 681 ) { FAIL("iteration 681"); exit(0); } + if ( do_682() != 682 ) { FAIL("iteration 682"); exit(0); } + if ( do_683() != 683 ) { FAIL("iteration 683"); exit(0); } + if ( do_684() != 684 ) { FAIL("iteration 684"); exit(0); } + if ( do_685() != 685 ) { FAIL("iteration 685"); exit(0); } + if ( do_686() != 686 ) { FAIL("iteration 686"); exit(0); } + if ( do_687() != 687 ) { FAIL("iteration 687"); exit(0); } + if ( do_688() != 688 ) { FAIL("iteration 688"); exit(0); } + if ( do_689() != 689 ) { FAIL("iteration 689"); exit(0); } + if ( do_690() != 690 ) { FAIL("iteration 690"); exit(0); } + if ( do_691() != 691 ) { FAIL("iteration 691"); exit(0); } + if ( do_692() != 692 ) { FAIL("iteration 692"); exit(0); } + if ( do_693() != 693 ) { FAIL("iteration 693"); exit(0); } + if ( do_694() != 694 ) { FAIL("iteration 694"); exit(0); } + if ( do_695() != 695 ) { FAIL("iteration 695"); exit(0); } + if ( do_696() != 696 ) { FAIL("iteration 696"); exit(0); } + if ( do_697() != 697 ) { FAIL("iteration 697"); exit(0); } + if ( do_698() != 698 ) { FAIL("iteration 698"); exit(0); } + if ( do_699() != 699 ) { FAIL("iteration 699"); exit(0); } + if ( do_700() != 700 ) { FAIL("iteration 700"); exit(0); } + if ( do_701() != 701 ) { FAIL("iteration 701"); exit(0); } + if ( do_702() != 702 ) { FAIL("iteration 702"); exit(0); } + if ( do_703() != 703 ) { FAIL("iteration 703"); exit(0); } + if ( do_704() != 704 ) { FAIL("iteration 704"); exit(0); } + if ( do_705() != 705 ) { FAIL("iteration 705"); exit(0); } + if ( do_706() != 706 ) { FAIL("iteration 706"); exit(0); } + if ( do_707() != 707 ) { FAIL("iteration 707"); exit(0); } + if ( do_708() != 708 ) { FAIL("iteration 708"); exit(0); } + if ( do_709() != 709 ) { FAIL("iteration 709"); exit(0); } + if ( do_710() != 710 ) { FAIL("iteration 710"); exit(0); } + if ( do_711() != 711 ) { FAIL("iteration 711"); exit(0); } + if ( do_712() != 712 ) { FAIL("iteration 712"); exit(0); } + if ( do_713() != 713 ) { FAIL("iteration 713"); exit(0); } + if ( do_714() != 714 ) { FAIL("iteration 714"); exit(0); } + if ( do_715() != 715 ) { FAIL("iteration 715"); exit(0); } + if ( do_716() != 716 ) { FAIL("iteration 716"); exit(0); } + if ( do_717() != 717 ) { FAIL("iteration 717"); exit(0); } + if ( do_718() != 718 ) { FAIL("iteration 718"); exit(0); } + if ( do_719() != 719 ) { FAIL("iteration 719"); exit(0); } + if ( do_720() != 720 ) { FAIL("iteration 720"); exit(0); } + if ( do_721() != 721 ) { FAIL("iteration 721"); exit(0); } + if ( do_722() != 722 ) { FAIL("iteration 722"); exit(0); } + if ( do_723() != 723 ) { FAIL("iteration 723"); exit(0); } + if ( do_724() != 724 ) { FAIL("iteration 724"); exit(0); } + if ( do_725() != 725 ) { FAIL("iteration 725"); exit(0); } + if ( do_726() != 726 ) { FAIL("iteration 726"); exit(0); } + if ( do_727() != 727 ) { FAIL("iteration 727"); exit(0); } + if ( do_728() != 728 ) { FAIL("iteration 728"); exit(0); } + if ( do_729() != 729 ) { FAIL("iteration 729"); exit(0); } + if ( do_730() != 730 ) { FAIL("iteration 730"); exit(0); } + if ( do_731() != 731 ) { FAIL("iteration 731"); exit(0); } + if ( do_732() != 732 ) { FAIL("iteration 732"); exit(0); } + if ( do_733() != 733 ) { FAIL("iteration 733"); exit(0); } + if ( do_734() != 734 ) { FAIL("iteration 734"); exit(0); } + if ( do_735() != 735 ) { FAIL("iteration 735"); exit(0); } + if ( do_736() != 736 ) { FAIL("iteration 736"); exit(0); } + if ( do_737() != 737 ) { FAIL("iteration 737"); exit(0); } + if ( do_738() != 738 ) { FAIL("iteration 738"); exit(0); } + if ( do_739() != 739 ) { FAIL("iteration 739"); exit(0); } + if ( do_740() != 740 ) { FAIL("iteration 740"); exit(0); } + if ( do_741() != 741 ) { FAIL("iteration 741"); exit(0); } + if ( do_742() != 742 ) { FAIL("iteration 742"); exit(0); } + if ( do_743() != 743 ) { FAIL("iteration 743"); exit(0); } + if ( do_744() != 744 ) { FAIL("iteration 744"); exit(0); } + if ( do_745() != 745 ) { FAIL("iteration 745"); exit(0); } + if ( do_746() != 746 ) { FAIL("iteration 746"); exit(0); } + if ( do_747() != 747 ) { FAIL("iteration 747"); exit(0); } + if ( do_748() != 748 ) { FAIL("iteration 748"); exit(0); } + if ( do_749() != 749 ) { FAIL("iteration 749"); exit(0); } + if ( do_750() != 750 ) { FAIL("iteration 750"); exit(0); } + if ( do_751() != 751 ) { FAIL("iteration 751"); exit(0); } + if ( do_752() != 752 ) { FAIL("iteration 752"); exit(0); } + if ( do_753() != 753 ) { FAIL("iteration 753"); exit(0); } + if ( do_754() != 754 ) { FAIL("iteration 754"); exit(0); } + if ( do_755() != 755 ) { FAIL("iteration 755"); exit(0); } + if ( do_756() != 756 ) { FAIL("iteration 756"); exit(0); } + if ( do_757() != 757 ) { FAIL("iteration 757"); exit(0); } + if ( do_758() != 758 ) { FAIL("iteration 758"); exit(0); } + if ( do_759() != 759 ) { FAIL("iteration 759"); exit(0); } + if ( do_760() != 760 ) { FAIL("iteration 760"); exit(0); } + if ( do_761() != 761 ) { FAIL("iteration 761"); exit(0); } + if ( do_762() != 762 ) { FAIL("iteration 762"); exit(0); } + if ( do_763() != 763 ) { FAIL("iteration 763"); exit(0); } + if ( do_764() != 764 ) { FAIL("iteration 764"); exit(0); } + if ( do_765() != 765 ) { FAIL("iteration 765"); exit(0); } + if ( do_766() != 766 ) { FAIL("iteration 766"); exit(0); } + if ( do_767() != 767 ) { FAIL("iteration 767"); exit(0); } + if ( do_768() != 768 ) { FAIL("iteration 768"); exit(0); } + if ( do_769() != 769 ) { FAIL("iteration 769"); exit(0); } + if ( do_770() != 770 ) { FAIL("iteration 770"); exit(0); } + if ( do_771() != 771 ) { FAIL("iteration 771"); exit(0); } + if ( do_772() != 772 ) { FAIL("iteration 772"); exit(0); } + if ( do_773() != 773 ) { FAIL("iteration 773"); exit(0); } + if ( do_774() != 774 ) { FAIL("iteration 774"); exit(0); } + if ( do_775() != 775 ) { FAIL("iteration 775"); exit(0); } + if ( do_776() != 776 ) { FAIL("iteration 776"); exit(0); } + if ( do_777() != 777 ) { FAIL("iteration 777"); exit(0); } + if ( do_778() != 778 ) { FAIL("iteration 778"); exit(0); } + if ( do_779() != 779 ) { FAIL("iteration 779"); exit(0); } + if ( do_780() != 780 ) { FAIL("iteration 780"); exit(0); } + if ( do_781() != 781 ) { FAIL("iteration 781"); exit(0); } + if ( do_782() != 782 ) { FAIL("iteration 782"); exit(0); } + if ( do_783() != 783 ) { FAIL("iteration 783"); exit(0); } + if ( do_784() != 784 ) { FAIL("iteration 784"); exit(0); } + if ( do_785() != 785 ) { FAIL("iteration 785"); exit(0); } + if ( do_786() != 786 ) { FAIL("iteration 786"); exit(0); } + if ( do_787() != 787 ) { FAIL("iteration 787"); exit(0); } + if ( do_788() != 788 ) { FAIL("iteration 788"); exit(0); } + if ( do_789() != 789 ) { FAIL("iteration 789"); exit(0); } + if ( do_790() != 790 ) { FAIL("iteration 790"); exit(0); } + if ( do_791() != 791 ) { FAIL("iteration 791"); exit(0); } + if ( do_792() != 792 ) { FAIL("iteration 792"); exit(0); } + if ( do_793() != 793 ) { FAIL("iteration 793"); exit(0); } + if ( do_794() != 794 ) { FAIL("iteration 794"); exit(0); } + if ( do_795() != 795 ) { FAIL("iteration 795"); exit(0); } + if ( do_796() != 796 ) { FAIL("iteration 796"); exit(0); } + if ( do_797() != 797 ) { FAIL("iteration 797"); exit(0); } + if ( do_798() != 798 ) { FAIL("iteration 798"); exit(0); } + if ( do_799() != 799 ) { FAIL("iteration 799"); exit(0); } + if ( do_800() != 800 ) { FAIL("iteration 800"); exit(0); } + if ( do_801() != 801 ) { FAIL("iteration 801"); exit(0); } + if ( do_802() != 802 ) { FAIL("iteration 802"); exit(0); } + if ( do_803() != 803 ) { FAIL("iteration 803"); exit(0); } + if ( do_804() != 804 ) { FAIL("iteration 804"); exit(0); } + if ( do_805() != 805 ) { FAIL("iteration 805"); exit(0); } + if ( do_806() != 806 ) { FAIL("iteration 806"); exit(0); } + if ( do_807() != 807 ) { FAIL("iteration 807"); exit(0); } + if ( do_808() != 808 ) { FAIL("iteration 808"); exit(0); } + if ( do_809() != 809 ) { FAIL("iteration 809"); exit(0); } + if ( do_810() != 810 ) { FAIL("iteration 810"); exit(0); } + if ( do_811() != 811 ) { FAIL("iteration 811"); exit(0); } + if ( do_812() != 812 ) { FAIL("iteration 812"); exit(0); } + if ( do_813() != 813 ) { FAIL("iteration 813"); exit(0); } + if ( do_814() != 814 ) { FAIL("iteration 814"); exit(0); } + if ( do_815() != 815 ) { FAIL("iteration 815"); exit(0); } + if ( do_816() != 816 ) { FAIL("iteration 816"); exit(0); } + if ( do_817() != 817 ) { FAIL("iteration 817"); exit(0); } + if ( do_818() != 818 ) { FAIL("iteration 818"); exit(0); } + if ( do_819() != 819 ) { FAIL("iteration 819"); exit(0); } + if ( do_820() != 820 ) { FAIL("iteration 820"); exit(0); } + if ( do_821() != 821 ) { FAIL("iteration 821"); exit(0); } + if ( do_822() != 822 ) { FAIL("iteration 822"); exit(0); } + if ( do_823() != 823 ) { FAIL("iteration 823"); exit(0); } + if ( do_824() != 824 ) { FAIL("iteration 824"); exit(0); } + if ( do_825() != 825 ) { FAIL("iteration 825"); exit(0); } + if ( do_826() != 826 ) { FAIL("iteration 826"); exit(0); } + if ( do_827() != 827 ) { FAIL("iteration 827"); exit(0); } + if ( do_828() != 828 ) { FAIL("iteration 828"); exit(0); } + if ( do_829() != 829 ) { FAIL("iteration 829"); exit(0); } + if ( do_830() != 830 ) { FAIL("iteration 830"); exit(0); } + if ( do_831() != 831 ) { FAIL("iteration 831"); exit(0); } + if ( do_832() != 832 ) { FAIL("iteration 832"); exit(0); } + if ( do_833() != 833 ) { FAIL("iteration 833"); exit(0); } + if ( do_834() != 834 ) { FAIL("iteration 834"); exit(0); } + if ( do_835() != 835 ) { FAIL("iteration 835"); exit(0); } + if ( do_836() != 836 ) { FAIL("iteration 836"); exit(0); } + if ( do_837() != 837 ) { FAIL("iteration 837"); exit(0); } + if ( do_838() != 838 ) { FAIL("iteration 838"); exit(0); } + if ( do_839() != 839 ) { FAIL("iteration 839"); exit(0); } + if ( do_840() != 840 ) { FAIL("iteration 840"); exit(0); } + if ( do_841() != 841 ) { FAIL("iteration 841"); exit(0); } + if ( do_842() != 842 ) { FAIL("iteration 842"); exit(0); } + if ( do_843() != 843 ) { FAIL("iteration 843"); exit(0); } + if ( do_844() != 844 ) { FAIL("iteration 844"); exit(0); } + if ( do_845() != 845 ) { FAIL("iteration 845"); exit(0); } + if ( do_846() != 846 ) { FAIL("iteration 846"); exit(0); } + if ( do_847() != 847 ) { FAIL("iteration 847"); exit(0); } + if ( do_848() != 848 ) { FAIL("iteration 848"); exit(0); } + if ( do_849() != 849 ) { FAIL("iteration 849"); exit(0); } + if ( do_850() != 850 ) { FAIL("iteration 850"); exit(0); } + if ( do_851() != 851 ) { FAIL("iteration 851"); exit(0); } + if ( do_852() != 852 ) { FAIL("iteration 852"); exit(0); } + if ( do_853() != 853 ) { FAIL("iteration 853"); exit(0); } + if ( do_854() != 854 ) { FAIL("iteration 854"); exit(0); } + if ( do_855() != 855 ) { FAIL("iteration 855"); exit(0); } + if ( do_856() != 856 ) { FAIL("iteration 856"); exit(0); } + if ( do_857() != 857 ) { FAIL("iteration 857"); exit(0); } + if ( do_858() != 858 ) { FAIL("iteration 858"); exit(0); } + if ( do_859() != 859 ) { FAIL("iteration 859"); exit(0); } + if ( do_860() != 860 ) { FAIL("iteration 860"); exit(0); } + if ( do_861() != 861 ) { FAIL("iteration 861"); exit(0); } + if ( do_862() != 862 ) { FAIL("iteration 862"); exit(0); } + if ( do_863() != 863 ) { FAIL("iteration 863"); exit(0); } + if ( do_864() != 864 ) { FAIL("iteration 864"); exit(0); } + if ( do_865() != 865 ) { FAIL("iteration 865"); exit(0); } + if ( do_866() != 866 ) { FAIL("iteration 866"); exit(0); } + if ( do_867() != 867 ) { FAIL("iteration 867"); exit(0); } + if ( do_868() != 868 ) { FAIL("iteration 868"); exit(0); } + if ( do_869() != 869 ) { FAIL("iteration 869"); exit(0); } + if ( do_870() != 870 ) { FAIL("iteration 870"); exit(0); } + if ( do_871() != 871 ) { FAIL("iteration 871"); exit(0); } + if ( do_872() != 872 ) { FAIL("iteration 872"); exit(0); } + if ( do_873() != 873 ) { FAIL("iteration 873"); exit(0); } + if ( do_874() != 874 ) { FAIL("iteration 874"); exit(0); } + if ( do_875() != 875 ) { FAIL("iteration 875"); exit(0); } + if ( do_876() != 876 ) { FAIL("iteration 876"); exit(0); } + if ( do_877() != 877 ) { FAIL("iteration 877"); exit(0); } + if ( do_878() != 878 ) { FAIL("iteration 878"); exit(0); } + if ( do_879() != 879 ) { FAIL("iteration 879"); exit(0); } + if ( do_880() != 880 ) { FAIL("iteration 880"); exit(0); } + if ( do_881() != 881 ) { FAIL("iteration 881"); exit(0); } + if ( do_882() != 882 ) { FAIL("iteration 882"); exit(0); } + if ( do_883() != 883 ) { FAIL("iteration 883"); exit(0); } + if ( do_884() != 884 ) { FAIL("iteration 884"); exit(0); } + if ( do_885() != 885 ) { FAIL("iteration 885"); exit(0); } + if ( do_886() != 886 ) { FAIL("iteration 886"); exit(0); } + if ( do_887() != 887 ) { FAIL("iteration 887"); exit(0); } + if ( do_888() != 888 ) { FAIL("iteration 888"); exit(0); } + if ( do_889() != 889 ) { FAIL("iteration 889"); exit(0); } + if ( do_890() != 890 ) { FAIL("iteration 890"); exit(0); } + if ( do_891() != 891 ) { FAIL("iteration 891"); exit(0); } + if ( do_892() != 892 ) { FAIL("iteration 892"); exit(0); } + if ( do_893() != 893 ) { FAIL("iteration 893"); exit(0); } + if ( do_894() != 894 ) { FAIL("iteration 894"); exit(0); } + if ( do_895() != 895 ) { FAIL("iteration 895"); exit(0); } + if ( do_896() != 896 ) { FAIL("iteration 896"); exit(0); } + if ( do_897() != 897 ) { FAIL("iteration 897"); exit(0); } + if ( do_898() != 898 ) { FAIL("iteration 898"); exit(0); } + if ( do_899() != 899 ) { FAIL("iteration 899"); exit(0); } + if ( do_900() != 900 ) { FAIL("iteration 900"); exit(0); } + if ( do_901() != 901 ) { FAIL("iteration 901"); exit(0); } + if ( do_902() != 902 ) { FAIL("iteration 902"); exit(0); } + if ( do_903() != 903 ) { FAIL("iteration 903"); exit(0); } + if ( do_904() != 904 ) { FAIL("iteration 904"); exit(0); } + if ( do_905() != 905 ) { FAIL("iteration 905"); exit(0); } + if ( do_906() != 906 ) { FAIL("iteration 906"); exit(0); } + if ( do_907() != 907 ) { FAIL("iteration 907"); exit(0); } + if ( do_908() != 908 ) { FAIL("iteration 908"); exit(0); } + if ( do_909() != 909 ) { FAIL("iteration 909"); exit(0); } + if ( do_910() != 910 ) { FAIL("iteration 910"); exit(0); } + if ( do_911() != 911 ) { FAIL("iteration 911"); exit(0); } + if ( do_912() != 912 ) { FAIL("iteration 912"); exit(0); } + if ( do_913() != 913 ) { FAIL("iteration 913"); exit(0); } + if ( do_914() != 914 ) { FAIL("iteration 914"); exit(0); } + if ( do_915() != 915 ) { FAIL("iteration 915"); exit(0); } + if ( do_916() != 916 ) { FAIL("iteration 916"); exit(0); } + if ( do_917() != 917 ) { FAIL("iteration 917"); exit(0); } + if ( do_918() != 918 ) { FAIL("iteration 918"); exit(0); } + if ( do_919() != 919 ) { FAIL("iteration 919"); exit(0); } + if ( do_920() != 920 ) { FAIL("iteration 920"); exit(0); } + if ( do_921() != 921 ) { FAIL("iteration 921"); exit(0); } + if ( do_922() != 922 ) { FAIL("iteration 922"); exit(0); } + if ( do_923() != 923 ) { FAIL("iteration 923"); exit(0); } + if ( do_924() != 924 ) { FAIL("iteration 924"); exit(0); } + if ( do_925() != 925 ) { FAIL("iteration 925"); exit(0); } + if ( do_926() != 926 ) { FAIL("iteration 926"); exit(0); } + if ( do_927() != 927 ) { FAIL("iteration 927"); exit(0); } + if ( do_928() != 928 ) { FAIL("iteration 928"); exit(0); } + if ( do_929() != 929 ) { FAIL("iteration 929"); exit(0); } + if ( do_930() != 930 ) { FAIL("iteration 930"); exit(0); } + if ( do_931() != 931 ) { FAIL("iteration 931"); exit(0); } + if ( do_932() != 932 ) { FAIL("iteration 932"); exit(0); } + if ( do_933() != 933 ) { FAIL("iteration 933"); exit(0); } + if ( do_934() != 934 ) { FAIL("iteration 934"); exit(0); } + if ( do_935() != 935 ) { FAIL("iteration 935"); exit(0); } + if ( do_936() != 936 ) { FAIL("iteration 936"); exit(0); } + if ( do_937() != 937 ) { FAIL("iteration 937"); exit(0); } + if ( do_938() != 938 ) { FAIL("iteration 938"); exit(0); } + if ( do_939() != 939 ) { FAIL("iteration 939"); exit(0); } + if ( do_940() != 940 ) { FAIL("iteration 940"); exit(0); } + if ( do_941() != 941 ) { FAIL("iteration 941"); exit(0); } + if ( do_942() != 942 ) { FAIL("iteration 942"); exit(0); } + if ( do_943() != 943 ) { FAIL("iteration 943"); exit(0); } + if ( do_944() != 944 ) { FAIL("iteration 944"); exit(0); } + if ( do_945() != 945 ) { FAIL("iteration 945"); exit(0); } + if ( do_946() != 946 ) { FAIL("iteration 946"); exit(0); } + if ( do_947() != 947 ) { FAIL("iteration 947"); exit(0); } + if ( do_948() != 948 ) { FAIL("iteration 948"); exit(0); } + if ( do_949() != 949 ) { FAIL("iteration 949"); exit(0); } + if ( do_950() != 950 ) { FAIL("iteration 950"); exit(0); } + if ( do_951() != 951 ) { FAIL("iteration 951"); exit(0); } + if ( do_952() != 952 ) { FAIL("iteration 952"); exit(0); } + if ( do_953() != 953 ) { FAIL("iteration 953"); exit(0); } + if ( do_954() != 954 ) { FAIL("iteration 954"); exit(0); } + if ( do_955() != 955 ) { FAIL("iteration 955"); exit(0); } + if ( do_956() != 956 ) { FAIL("iteration 956"); exit(0); } + if ( do_957() != 957 ) { FAIL("iteration 957"); exit(0); } + if ( do_958() != 958 ) { FAIL("iteration 958"); exit(0); } + if ( do_959() != 959 ) { FAIL("iteration 959"); exit(0); } + if ( do_960() != 960 ) { FAIL("iteration 960"); exit(0); } + if ( do_961() != 961 ) { FAIL("iteration 961"); exit(0); } + if ( do_962() != 962 ) { FAIL("iteration 962"); exit(0); } + if ( do_963() != 963 ) { FAIL("iteration 963"); exit(0); } + if ( do_964() != 964 ) { FAIL("iteration 964"); exit(0); } + if ( do_965() != 965 ) { FAIL("iteration 965"); exit(0); } + if ( do_966() != 966 ) { FAIL("iteration 966"); exit(0); } + if ( do_967() != 967 ) { FAIL("iteration 967"); exit(0); } + if ( do_968() != 968 ) { FAIL("iteration 968"); exit(0); } + if ( do_969() != 969 ) { FAIL("iteration 969"); exit(0); } + if ( do_970() != 970 ) { FAIL("iteration 970"); exit(0); } + if ( do_971() != 971 ) { FAIL("iteration 971"); exit(0); } + if ( do_972() != 972 ) { FAIL("iteration 972"); exit(0); } + if ( do_973() != 973 ) { FAIL("iteration 973"); exit(0); } + if ( do_974() != 974 ) { FAIL("iteration 974"); exit(0); } + if ( do_975() != 975 ) { FAIL("iteration 975"); exit(0); } + if ( do_976() != 976 ) { FAIL("iteration 976"); exit(0); } + if ( do_977() != 977 ) { FAIL("iteration 977"); exit(0); } + if ( do_978() != 978 ) { FAIL("iteration 978"); exit(0); } + if ( do_979() != 979 ) { FAIL("iteration 979"); exit(0); } + if ( do_980() != 980 ) { FAIL("iteration 980"); exit(0); } + if ( do_981() != 981 ) { FAIL("iteration 981"); exit(0); } + if ( do_982() != 982 ) { FAIL("iteration 982"); exit(0); } + if ( do_983() != 983 ) { FAIL("iteration 983"); exit(0); } + if ( do_984() != 984 ) { FAIL("iteration 984"); exit(0); } + if ( do_985() != 985 ) { FAIL("iteration 985"); exit(0); } + if ( do_986() != 986 ) { FAIL("iteration 986"); exit(0); } + if ( do_987() != 987 ) { FAIL("iteration 987"); exit(0); } + if ( do_988() != 988 ) { FAIL("iteration 988"); exit(0); } + if ( do_989() != 989 ) { FAIL("iteration 989"); exit(0); } + if ( do_990() != 990 ) { FAIL("iteration 990"); exit(0); } + if ( do_991() != 991 ) { FAIL("iteration 991"); exit(0); } + if ( do_992() != 992 ) { FAIL("iteration 992"); exit(0); } + if ( do_993() != 993 ) { FAIL("iteration 993"); exit(0); } + if ( do_994() != 994 ) { FAIL("iteration 994"); exit(0); } + if ( do_995() != 995 ) { FAIL("iteration 995"); exit(0); } + if ( do_996() != 996 ) { FAIL("iteration 996"); exit(0); } + if ( do_997() != 997 ) { FAIL("iteration 997"); exit(0); } + if ( do_998() != 998 ) { FAIL("iteration 998"); exit(0); } + if ( do_999() != 999 ) { FAIL("iteration 999"); exit(0); } + return NULL; +} + + +int main() +{ + pthread_t worker[10]; + for (int i=0; i < 10; ++i) { + if ( pthread_create(&worker[i], NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + } + + void* result; + for (int i=0; i < 10; ++i) { + pthread_join(worker[i], &result); + } + + PASS("thread-lazy-bind"); + return 0; +} diff --git a/unit-tests/test-cases/trie-symbol-overrun/Makefile b/unit-tests/test-cases/trie-symbol-overrun/Makefile new file mode 100644 index 0000000..41fe1c1 --- /dev/null +++ b/unit-tests/test-cases/trie-symbol-overrun/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib -Wno-deprecated-declarations + + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib + diff --git a/unit-tests/test-cases/trie-symbol-overrun/foo.c b/unit-tests/test-cases/trie-symbol-overrun/foo.c new file mode 100644 index 0000000..62d5f38 --- /dev/null +++ b/unit-tests/test-cases/trie-symbol-overrun/foo.c @@ -0,0 +1,12 @@ + +void abc() +{ +} + +void abcdefghi() +{ +} + +void abcdee() +{ +} diff --git a/unit-tests/test-cases/trie-symbol-overrun/main.c b/unit-tests/test-cases/trie-symbol-overrun/main.c new file mode 100644 index 0000000..34d1a63 --- /dev/null +++ b/unit-tests/test-cases/trie-symbol-overrun/main.c @@ -0,0 +1,56 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // allocate two pages + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, 8192, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + FAIL("vm_allocate returned %d", r); + exit(0); + } + // mark the second page unreadable + mprotect((char*)(addr+4096), 4096, 0); + // copy a string to the end of the first page + char* sym = (char*)(addr+4096-5); + strcpy(sym, "_abd"); + + // 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 + _dyld_lookup_and_bind(sym, NULL, NULL); + + PASS("trie-symbol-overrun"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/unloadable-library-residue/Makefile b/unit-tests/test-cases/unloadable-library-residue/Makefile index fd4ad9b..42459e6 100644 --- a/unit-tests/test-cases/unloadable-library-residue/Makefile +++ b/unit-tests/test-cases/unloadable-library-residue/Makefile @@ -31,8 +31,9 @@ include ${TESTROOT}/include/common.makefile ### so the next use of libfoo seemed to succeed, when it should have failed. ### +all-check: all check -run: all +check: export DYLD_IMAGE_SUFFIX="_missing" && ./main all: main libfoo.dylib diff --git a/unit-tests/test-cases/weak-coalesce-c++/Makefile b/unit-tests/test-cases/weak-coalesce-c++/Makefile index 4f38eeb..253ea75 100644 --- a/unit-tests/test-cases/weak-coalesce-c++/Makefile +++ b/unit-tests/test-cases/weak-coalesce-c++/Makefile @@ -24,12 +24,13 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: ./dynamic-test all: dynamic-test static-test - static-test: a1.o a2.o main.o $(CXX) -I${TESTROOT}/include a1.o a2.o main.o -o static-test diff --git a/unit-tests/test-cases/weak-coalesce/Makefile b/unit-tests/test-cases/weak-coalesce/Makefile index 13bdf37..543a5be 100644 --- a/unit-tests/test-cases/weak-coalesce/Makefile +++ b/unit-tests/test-cases/weak-coalesce/Makefile @@ -23,27 +23,27 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +all-check: all check -run: all +check: ./main all: main - main: main.c libfoo1.dylib libbase.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib libfoo1.dylib: foo1.c libfoo2.dylib libbase.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib libfoo2.dylib: foo2.c libfoo3.dylib libbase.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib libfoo3.dylib: foo3.c libbase.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000 libbase.dylib: base.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000 + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000 diff --git a/unit-tests/test-cases/weak-external-reloc-flat/Makefile b/unit-tests/test-cases/weak-external-reloc-flat/Makefile new file mode 100644 index 0000000..b1e95b2 --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/Makefile @@ -0,0 +1,34 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# flat_namespace and weak binding conflict +# +# Note that libfoo.dylib is built flat-namespace +# + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib -flat_namespace + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/weak-external-reloc-flat/bar.c b/unit-tests/test-cases/weak-external-reloc-flat/bar.c new file mode 100644 index 0000000..4787d5b --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/bar.c @@ -0,0 +1,8 @@ + + +int bar[] = { 20, 21, 22, 23 }; + + +int __attribute__((weak)) frob[] = { 30, 31, 32, 33 }; + +int bar_getfrob(int x) { return frob[x]; } diff --git a/unit-tests/test-cases/weak-external-reloc-flat/baz.c b/unit-tests/test-cases/weak-external-reloc-flat/baz.c new file mode 100644 index 0000000..f35d05c --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/unit-tests/test-cases/weak-external-reloc-flat/foo.c b/unit-tests/test-cases/weak-external-reloc-flat/foo.c new file mode 100644 index 0000000..d2d73ef --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/foo.c @@ -0,0 +1,14 @@ + + +extern int bar_getfrob(int x); + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; +int __attribute__((weak)) frob[] = { 20, 21, 22, 23 }; + +int* pfoo = &foo[2]; + +int getfrob() { + return bar_getfrob(2); +} \ No newline at end of file diff --git a/unit-tests/test-cases/weak-external-reloc-flat/main.c b/unit-tests/test-cases/weak-external-reloc-flat/main.c new file mode 100644 index 0000000..e74b44d --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc-flat/main.c @@ -0,0 +1,41 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + + +// libfoo.dylib has a weak frob[] +// libbar.dylib has a weak frob[] and a funtion that refrences frob[] +// the function should use libfoo's frob[] even if libfoo is flat +extern int getfrob(); + +int main() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc-flat, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc-flat, pbar=%d", *pbar); + else if ( getfrob() != 22 ) + FAIL("weak-external-reloc-flat, frob()=%d", getfrob()); + else + PASS("weak-external-reloc-flat"); + + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/weak-external-reloc/Makefile b/unit-tests/test-cases/weak-external-reloc/Makefile new file mode 100644 index 0000000..b01a66b --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/weak-external-reloc/bar.c b/unit-tests/test-cases/weak-external-reloc/bar.c new file mode 100644 index 0000000..615196e --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing +int __attribute__((weak)) junk = 5; diff --git a/unit-tests/test-cases/weak-external-reloc/baz.c b/unit-tests/test-cases/weak-external-reloc/baz.c new file mode 100644 index 0000000..f35d05c --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/unit-tests/test-cases/weak-external-reloc/foo.c b/unit-tests/test-cases/weak-external-reloc/foo.c new file mode 100644 index 0000000..9e4873a --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/foo.c @@ -0,0 +1,10 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* pfoo = &foo[2]; + diff --git a/unit-tests/test-cases/weak-external-reloc/main.c b/unit-tests/test-cases/weak-external-reloc/main.c new file mode 100644 index 0000000..a25c448 --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/main.c @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/unit-tests/test-cases/weak-external-reloc/realmain.c b/unit-tests/test-cases/weak-external-reloc/realmain.c new file mode 100644 index 0000000..6870f5a --- /dev/null +++ b/unit-tests/test-cases/weak-external-reloc/realmain.c @@ -0,0 +1,39 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + +// there is only one stuff, but it is weak +// so we are testing the case when stuff is not overridden +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; +int* pstuff = &stuff[3]; + + +void realmain() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc, pbar=%d", *pbar); + else if ( *pstuff != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", *pstuff); + else + PASS("weak-external-reloc"); +} + + diff --git a/unit-tests/test-cases/weak-in-dylib/Makefile b/unit-tests/test-cases/weak-in-dylib/Makefile new file mode 100644 index 0000000..cd54661 --- /dev/null +++ b/unit-tests/test-cases/weak-in-dylib/Makefile @@ -0,0 +1,21 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/weak-in-dylib/foo.c b/unit-tests/test-cases/weak-in-dylib/foo.c new file mode 100644 index 0000000..ec6f836 --- /dev/null +++ b/unit-tests/test-cases/weak-in-dylib/foo.c @@ -0,0 +1,7 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; + diff --git a/unit-tests/test-cases/weak-in-dylib/main.c b/unit-tests/test-cases/weak-in-dylib/main.c new file mode 100644 index 0000000..358e0f7 --- /dev/null +++ b/unit-tests/test-cases/weak-in-dylib/main.c @@ -0,0 +1,22 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak foo[] +extern int foo[]; + +int* pfoo3 = &foo[3]; + +int main() +{ + if ( *pfoo3 != 4 ) + FAIL("weak-in-dylib, pfoo3=%d", *pfoo3); + else if ( foo[2] != 3 ) + FAIL("weak-in-dylib, foo[2]=%d", foo[2]); + else + PASS("weak-in-dyliby"); + return 0; +} + diff --git a/unit-tests/test-cases/weak-lazy-slidable/Makefile b/unit-tests/test-cases/weak-lazy-slidable/Makefile new file mode 100644 index 0000000..274962b --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/Makefile @@ -0,0 +1,31 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib libother.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libother.dylib libfoo.dylib + +libother.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libother.dylib other.c + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbar3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbar3.dylib + +libbar3.dylib: bar3.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar3.dylib bar3.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbar3.dylib libother.dylib + diff --git a/unit-tests/test-cases/weak-lazy-slidable/bar.c b/unit-tests/test-cases/weak-lazy-slidable/bar.c new file mode 100644 index 0000000..e52bf11 --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/bar.c @@ -0,0 +1,12 @@ + + +#include + +bool bar1() +{ + return true; +} + +__attribute__((weak)) void junk() +{ +} diff --git a/unit-tests/test-cases/weak-lazy-slidable/bar3.c b/unit-tests/test-cases/weak-lazy-slidable/bar3.c new file mode 100644 index 0000000..0f27868 --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/bar3.c @@ -0,0 +1,9 @@ + + +#include + +__attribute__((weak)) bool bar1() +{ + return false; +} + diff --git a/unit-tests/test-cases/weak-lazy-slidable/foo.c b/unit-tests/test-cases/weak-lazy-slidable/foo.c new file mode 100644 index 0000000..7e3c8a5 --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/foo.c @@ -0,0 +1,29 @@ +#include + + + +extern bool bar1(); + + +__attribute__((weak)) bool bar1() +{ + return false; +} + +__attribute__((weak)) bool bar2() +{ + return true; +} + + +bool foo1() +{ + return bar1(); +} + +bool foo2() +{ + return bar2(); +} + + diff --git a/unit-tests/test-cases/weak-lazy-slidable/main.c b/unit-tests/test-cases/weak-lazy-slidable/main.c new file mode 100644 index 0000000..a91ef85 --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/main.c @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool foo1(); +extern bool foo2(); + + +int main() +{ + if ( foo1() && foo2() ) + PASS("weak-lazy-slidable"); + else + FAIL("weak-lazy-slidable"); + + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/weak-lazy-slidable/other.c b/unit-tests/test-cases/weak-lazy-slidable/other.c new file mode 100644 index 0000000..0c04195 --- /dev/null +++ b/unit-tests/test-cases/weak-lazy-slidable/other.c @@ -0,0 +1,7 @@ +#include + +bool bar2() +{ + return false; +} + diff --git a/unit-tests/test-cases/weak-library/Makefile b/unit-tests/test-cases/weak-library/Makefile index b4eb71c..1d1863e 100644 --- a/unit-tests/test-cases/weak-library/Makefile +++ b/unit-tests/test-cases/weak-library/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -23,16 +23,18 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all - cd absent/ && ../${TESTROOT}/bin/exit-zero-pass.pl "pass message" "fail message" ./main - cd present/ && ../${TESTROOT}/bin/exit-zero-pass.pl "pass message" "fail message" ./main +all-check: all check + +check: + cd absent/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main + cd present/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main all: foo.c main.c ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c mkdir -p absent/ - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o absent/main main.c + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o absent/main main.c mkdir -p present/ - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o present/main main.c + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o present/main main.c mv libfoo.dylib present/ clean: diff --git a/unit-tests/test-cases/weak-non-lazy/Makefile b/unit-tests/test-cases/weak-non-lazy/Makefile new file mode 100644 index 0000000..c3d1b70 --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/unit-tests/test-cases/weak-non-lazy/bar.c b/unit-tests/test-cases/weak-non-lazy/bar.c new file mode 100644 index 0000000..9db55bd --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing fun +int __attribute__((weak)) junk = 5; diff --git a/unit-tests/test-cases/weak-non-lazy/baz.c b/unit-tests/test-cases/weak-non-lazy/baz.c new file mode 100644 index 0000000..411ae68 --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/baz.c @@ -0,0 +1,9 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + + diff --git a/unit-tests/test-cases/weak-non-lazy/foo.c b/unit-tests/test-cases/weak-non-lazy/foo.c new file mode 100644 index 0000000..a212684 --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/foo.c @@ -0,0 +1,15 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* getfoo() +{ + return foo; +} + + + diff --git a/unit-tests/test-cases/weak-non-lazy/main.c b/unit-tests/test-cases/weak-non-lazy/main.c new file mode 100644 index 0000000..523b7be --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/main.c @@ -0,0 +1,12 @@ + + +// put test code in a dylib so that is slides +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/unit-tests/test-cases/weak-non-lazy/realmain.c b/unit-tests/test-cases/weak-non-lazy/realmain.c new file mode 100644 index 0000000..ca4a6f2 --- /dev/null +++ b/unit-tests/test-cases/weak-non-lazy/realmain.c @@ -0,0 +1,36 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// getfoo() is in libfoo.dylib and uses a non-lazy pointer to reference foo +// libfoo.dylib also has weak foo[] +// getfoo() should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* getfoo(); + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; + + +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; + + +void realmain() +{ + if ( getfoo()[2] != 7 ) + FAIL("weak-non-lazy, getfoo()[2]=%d", getfoo()[2]); + else if ( bar[1] != 21 ) + FAIL("weak-non-lazy, bar[1]=%d", bar[1]); + else if ( stuff[3] != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", stuff[3]); + else + PASS("weak-non-lazy"); +} + + diff --git a/unit-tests/test-cases/weak-override/Makefile b/unit-tests/test-cases/weak-override/Makefile index 26f8617..8d7799f 100644 --- a/unit-tests/test-cases/weak-override/Makefile +++ b/unit-tests/test-cases/weak-override/Makefile @@ -23,8 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +all-check: all check -run: all +check: ./main all: main diff --git a/unit-tests/test-cases/weak-override/foo.c b/unit-tests/test-cases/weak-override/foo.c index dcce76e..984ee82 100644 --- a/unit-tests/test-cases/weak-override/foo.c +++ b/unit-tests/test-cases/weak-override/foo.c @@ -33,3 +33,10 @@ int __attribute__((weak)) foo() { return myfunc(); } + +int (*myfuncproc)() = &myfunc; + +int bar() +{ + return (*myfuncproc)(); +} diff --git a/unit-tests/test-cases/weak-override/main.c b/unit-tests/test-cases/weak-override/main.c index 372c29a..8010cc0 100644 --- a/unit-tests/test-cases/weak-override/main.c +++ b/unit-tests/test-cases/weak-override/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,16 +25,14 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() -// foo is defined in libfoo.dylib -// it calls myfunc() +// foo is defined in libfoo.dylib it calls myfunc() extern int foo(); - -// add this so WEAK_DEFINES is set, so dyld searchs this image -int __attribute__((weak)) junk = 2; +// bar is defined in libfoo.dylib it calls myfunc() +extern int bar(); int main() { - if ( foo() == 10 ) + if ( (foo() == 10) && (bar() == 10) ) PASS("weak-override"); else FAIL("weak-override"); diff --git a/unit-tests/test-cases/weak-symbol-flat/Makefile b/unit-tests/test-cases/weak-symbol-flat/Makefile index ba1d7aa..dfd56ce 100644 --- a/unit-tests/test-cases/weak-symbol-flat/Makefile +++ b/unit-tests/test-cases/weak-symbol-flat/Makefile @@ -1,4 +1,4 @@ -## +# # Copyright (c) 2005 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ @@ -23,11 +23,13 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all +all-check: all check + +check: (export DYLD_IMAGE_SUFFIX=_missing && ./main) || echo "FAIL \"weak-symbol-flat\"" ./main 1 -all : main libfoo.dylib libfoo_missing.dylib +all: main libfoo.dylib libfoo_missing.dylib libfoo.dylib : foo.c ${CC} -dynamiclib -DSYMBOL_PRESENT -o libfoo.dylib foo.c @@ -36,7 +38,7 @@ libfoo_missing.dylib : foo.c ${CC} -dynamiclib -o libfoo_missing.dylib foo.c -install_name libfoo.dylib main: main.c libfoo.dylib - ${CC} -I${TESTROOT}/include -mmacosx-version-min=10.2 -L. -lfoo -o main main.c -flat_namespace + ${CC} -I${TESTROOT}/include -L. -lfoo -o main main.c -flat_namespace clean: ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main diff --git a/unit-tests/test-cases/weak-symbol/Makefile b/unit-tests/test-cases/weak-symbol/Makefile index e24ee52..f2d1cec 100644 --- a/unit-tests/test-cases/weak-symbol/Makefile +++ b/unit-tests/test-cases/weak-symbol/Makefile @@ -23,13 +23,16 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -run: all + +all-check: all check + +check: export DYLD_IMAGE_SUFFIX=_missing && ${TESTROOT}/bin/exit-zero-pass.pl "weak-symbol" "weak-symbol" ./main all: foo.c main.c ${CC} foo.c -dynamiclib -o libfoo.dylib -DSYMBOL_PRESENT ${CC} foo.c -dynamiclib -o libfoo_missing.dylib -install_name libfoo.dylib - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -I${TESTROOT}/include -L. -lfoo main.c -o main + ${CC} -I${TESTROOT}/include -L. -lfoo main.c -o main clean: ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main diff --git a/unit-tests/test-cases/weak_import/Makefile b/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..7833962 --- /dev/null +++ b/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + +all-check: all check + +check: + ./main + export DYLD_IMAGE_SUFFIX=_some && ./main missing + +all: main libfoo_some.dylib + + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -DALL_SYMBOLS=1 -dynamiclib -o libfoo.dylib + +libfoo_some.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo_some.dylib -install_name libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo_some.dylib diff --git a/unit-tests/test-cases/weak_import/foo.c b/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 0000000..8f50835 --- /dev/null +++ b/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,19 @@ + + +#include "foo.h" + +void func1() {} +void func3() {} +int data1 = 0; +int data3; +int data5 = 0; + +#if ALL_SYMBOLS +void func2() {} +void func4() {} + +int data2 = 0; // weak_import initialized +int data4; // weak_import uninitialized +int data6 = 0; // weak_import +#endif + diff --git a/unit-tests/test-cases/weak_import/foo.h b/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/unit-tests/test-cases/weak_import/main.c b/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 0000000..a777a52 --- /dev/null +++ b/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,59 @@ +/* + * 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" // PASS(), FAIL() + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main(int argc, const char* argv[]) +{ + if ( argc > 1 ) { + // this weak case where the even symbol names are missing at runtime + func1(); + func3(); + data1 = data3; + if ( (&func2 == NULL) && (&func4 == NULL) && (&data2 == NULL) && (&data4 == NULL) && (pdata6 == NULL) ) + PASS("weak_import"); + else + FAIL("weak_import"); + } + else { + // this is the regular case where all symbols exist at runtime + func1(); + func2(); + func3(); + func4(); + data1 = data2 + data3 + data4; + PASS("weak_import"); + } + return 0; +} + diff --git a/unit-tests/test-cases/zero-fill-segment/Makefile b/unit-tests/test-cases/zero-fill-segment/Makefile index 513cd75..f0380c7 100644 --- a/unit-tests/test-cases/zero-fill-segment/Makefile +++ b/unit-tests/test-cases/zero-fill-segment/Makefile @@ -23,8 +23,9 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +all-check: all check -run: all +check: ./main all: main diff --git a/unit-tests/test-cases/zero-length-segment/Makefile b/unit-tests/test-cases/zero-length-segment/Makefile index 9d8b5cb..297c760 100644 --- a/unit-tests/test-cases/zero-length-segment/Makefile +++ b/unit-tests/test-cases/zero-length-segment/Makefile @@ -25,7 +25,10 @@ include ${TESTROOT}/include/common.makefile SHELL = bash # use bash shell so we can redirect just stderr -run: all + +all-check: all check + +check: export DYLD_PRINT_SEGMENTS=1 && ./main 2> segments.log grep __FOOBAR segments.log > /dev/null || echo "PASS zero-length-segment" (grep __FOOBAR segments.log > /dev/null && echo "FAIL zero-length-segment") || /usr/bin/true -- 2.45.2